diff options
author | Tarek Oraby <42799254+tarekoraby@users.noreply.github.com> | 2020-04-21 12:22:45 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-21 12:22:45 +0300 |
commit | 01936188ca3105271109e7b8a5ede42e8a1cc1d8 (patch) | |
tree | 2d11c192ffa55050e97cd87419b124d56272ed76 /uitest | |
parent | d35bd4cde4ae0074d68c7e1f10f2be8757403cc7 (diff) | |
download | vaadin-framework-01936188ca3105271109e7b8a5ede42e8a1cc1d8.tar.gz vaadin-framework-01936188ca3105271109e7b8a5ede42e8a1cc1d8.zip |
Allow AbstractDateField to provide DST zone names over custom ranges (#11927)
DateTimeField and DateField currently implement a hardcoded logic by which they adjust their time zone names to display daylight-saving time (DST) zone names. Specifically, this hardcoded logic only adjusts the displayed date to DST format if that date falls in one of the years between 1980 and the following 20 years in the future from the current date (that is, until 2040 at the time of this commit).
For some use cases, this is problematic because it is desirable to display proper DST-adjusted time zones beyond the 20 years limit (and possibly also before 1980).
Rather than choosing another arbitrary, hardcoded threshold, this commit extends the AbstractDateField API to allow the user to choose the range (start and end years) between which the DST transition dates are calculated (and hence displayed properly). If the user doesn't invoke this new API, DateTimeField and DateField will default to behave according the existing logic (i.e. display DST zone names between 1980 and 20 years into the future).
Closes #11919
Diffstat (limited to 'uitest')
2 files changed, 340 insertions, 0 deletions
diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldZoneIdFutureSummerDates.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldZoneIdFutureSummerDates.java new file mode 100644 index 0000000000..f00d1d65fb --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldZoneIdFutureSummerDates.java @@ -0,0 +1,185 @@ +package com.vaadin.tests.components.datefield; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.Month; +import java.time.ZoneId; +import java.util.Locale; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Stream; + +import com.vaadin.server.UserError; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.DateField; +import com.vaadin.ui.DateTimeField; + +public class DateTimeFieldZoneIdFutureSummerDates extends AbstractTestUI { + + static final String ZONE_ID = "zoneId"; + static final String LOCALE_ID = "localeId"; + static final String START_YEAR_DATEFIELD_ID = "startYearDateFieldID"; + static final String END_YEAR_DATEFIELD_ID = "endYearDateFieldID"; + static final String FIXED_RANGE_DATEFIELD_ID = "fixedRangeDateFieldID"; + static final String VARIABLE_RANGE_DATEFIELD_ID = "variableRangeDateFieldID"; + + static final String INITIAL_ZONE_ID = "CET"; + static final Locale INITIAL_LOCALE = Locale.US; + static final LocalDateTime INITIAL_DATE_TIME = LocalDateTime + .of(LocalDate.now().getYear() + 21, Month.JULY, 1, 0, 0); + static final LocalDate INITIAL_START_DATE = LocalDate + .of(INITIAL_DATE_TIME.getYear() - 5, Month.JULY, 1); + static final LocalDate INITIAL_END_DATE = LocalDate + .of(INITIAL_DATE_TIME.getYear() + 5, Month.JULY, 1); + private static final String TARGET_FORMAT_PATTERN = "dd MMM yyyy - z"; + private static final String RANGE_FORMAT_PATTERN = "yyyy"; + + @Override + protected String getTestDescription() { + return "DateTimeField should correctly show the daylight saving (summer time) zone name " + + "of a date that occurs within a user-defined range"; + } + + @Override + protected Integer getTicketNumber() { + return 11919; + } + + @Override + protected void setup(VaadinRequest request) { + final ComboBox<String> zoneIdComboBox = getZoneIdComboBox(); + addComponent(zoneIdComboBox); + + final ComboBox<Locale> localeIdComboBox = getLocaleIdComboBox(); + addComponent(localeIdComboBox); + + final DateField transitionsStartYear = getDateField( + START_YEAR_DATEFIELD_ID, INITIAL_START_DATE, + RANGE_FORMAT_PATTERN, + "DST Transitions' start year (inclusive):"); + addComponent(transitionsStartYear); + + final DateField transitionsEndYear = getDateField(END_YEAR_DATEFIELD_ID, + INITIAL_END_DATE, RANGE_FORMAT_PATTERN, + "DST Transitions' end year (inclusive):"); + addComponent(transitionsEndYear); + + String captionVarField = "A DateTimeField with custom start" + + " and end years between which DST zone names are displayed:"; + final DateTimeField dateTimeFieldWithCustomRange = getDateTimeField( + VARIABLE_RANGE_DATEFIELD_ID, INITIAL_DATE_TIME, + TARGET_FORMAT_PATTERN, INITIAL_LOCALE, INITIAL_ZONE_ID, + captionVarField); + dateTimeFieldWithCustomRange.setDaylightSavingTimeRange( + INITIAL_START_DATE.getYear(), INITIAL_END_DATE.getYear()); + + addComponent(dateTimeFieldWithCustomRange); + + transitionsStartYear.addValueChangeListener(event -> { + int startYear = event.getValue().getYear(); + int endYear = transitionsEndYear.getValue().getYear(); + if (startYear > endYear) { + showDateRangeError(transitionsStartYear); + } else { + clearErrors(transitionsStartYear, transitionsEndYear); + dateTimeFieldWithCustomRange + .setDaylightSavingTimeRange(startYear, endYear); + } + }); + + transitionsEndYear.addValueChangeListener(event -> { + int startYear = transitionsStartYear.getValue().getYear(); + int endYear = event.getValue().getYear(); + if (startYear > endYear) { + showDateRangeError(transitionsEndYear); + } else { + clearErrors(transitionsStartYear, transitionsEndYear); + dateTimeFieldWithCustomRange + .setDaylightSavingTimeRange(startYear, endYear); + } + }); + + String captionFixedField = "A default DateTimeField (By default, " + + "DST zones are displayed between 1980 and 20 years into the future):"; + final DateTimeField dateTimeFieldWithDefaultRange = getDateTimeField( + FIXED_RANGE_DATEFIELD_ID, INITIAL_DATE_TIME, + TARGET_FORMAT_PATTERN, INITIAL_LOCALE, INITIAL_ZONE_ID, + captionFixedField); + addComponent(dateTimeFieldWithDefaultRange); + + zoneIdComboBox.addValueChangeListener(event -> { + final String value = event.getValue(); + if (value == null) { + dateTimeFieldWithCustomRange.setZoneId(null); + dateTimeFieldWithDefaultRange.setZoneId(null); + } else { + dateTimeFieldWithCustomRange.setZoneId(ZoneId.of(value)); + dateTimeFieldWithDefaultRange.setZoneId(ZoneId.of(value)); + } + }); + + localeIdComboBox.addValueChangeListener(event -> { + dateTimeFieldWithCustomRange.setLocale(event.getValue()); + dateTimeFieldWithDefaultRange.setLocale(event.getValue()); + }); + } + + private DateTimeField getDateTimeField(String id, + LocalDateTime initialDateTime, String dateFormat, Locale locale, + String zoneId, String caption) { + final DateTimeField dateTimeField = new DateTimeField(); + dateTimeField.setId(id); + dateTimeField.setValue(initialDateTime); + dateTimeField.setDateFormat(dateFormat); + dateTimeField.setLocale(locale); + dateTimeField.setZoneId(ZoneId.of(zoneId)); + dateTimeField.setCaption(caption); + return dateTimeField; + } + + private DateField getDateField(String id, LocalDate initialDate, + String dateFormat, String caption) { + final DateField dateField = new DateField(); + dateField.setId(id); + dateField.setDateFormat(dateFormat); + dateField.setCaption(caption); + dateField.setValue(initialDate); + return dateField; + } + + private void clearErrors(DateField transitionStartyear, + DateField transitionEndyear) { + transitionStartyear.setComponentError(null); + transitionEndyear.setComponentError(null); + } + + private void showDateRangeError(DateField dateField) { + dateField.setComponentError(new UserError( + "Start year must be less than or equal to end year!")); + } + + private ComboBox<Locale> getLocaleIdComboBox() { + final ComboBox<Locale> localeIdComboBox = new ComboBox<>(); + localeIdComboBox.setId(LOCALE_ID); + final Stream<Locale> localeStream = Stream + .of(Locale.getAvailableLocales()) + .sorted((l1, l2) -> l1.toString().compareTo(l2.toString())); + localeIdComboBox.setItems(localeStream); + localeIdComboBox.setValue(INITIAL_LOCALE); + localeIdComboBox.setCaption("Locale:"); + return localeIdComboBox; + } + + private ComboBox<String> getZoneIdComboBox() { + final ComboBox<String> zoneIdComboBox = new ComboBox<>(); + zoneIdComboBox.setId(ZONE_ID); + final Set<String> zoneIdSet = new TreeSet<>( + ZoneId.getAvailableZoneIds()); + zoneIdComboBox.setItems(zoneIdSet); + zoneIdComboBox.setValue(INITIAL_ZONE_ID); + zoneIdComboBox.setCaption("Zone:"); + return zoneIdComboBox; + } +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldZoneIdFutureSummerDatesTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldZoneIdFutureSummerDatesTest.java new file mode 100644 index 0000000000..1e7ea8eeef --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldZoneIdFutureSummerDatesTest.java @@ -0,0 +1,155 @@ +package com.vaadin.tests.components.datefield; + +import static com.vaadin.tests.components.datefield.DateTimeFieldZoneIdFutureSummerDates.END_YEAR_DATEFIELD_ID; +import static com.vaadin.tests.components.datefield.DateTimeFieldZoneIdFutureSummerDates.FIXED_RANGE_DATEFIELD_ID; +import static com.vaadin.tests.components.datefield.DateTimeFieldZoneIdFutureSummerDates.LOCALE_ID; +import static com.vaadin.tests.components.datefield.DateTimeFieldZoneIdFutureSummerDates.START_YEAR_DATEFIELD_ID; +import static com.vaadin.tests.components.datefield.DateTimeFieldZoneIdFutureSummerDates.VARIABLE_RANGE_DATEFIELD_ID; +import static com.vaadin.tests.components.datefield.DateTimeFieldZoneIdFutureSummerDates.ZONE_ID; +import static org.junit.Assert.assertTrue; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.Month; +import java.util.Locale; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.testbench.elements.ComboBoxElement; +import com.vaadin.testbench.elements.DateFieldElement; +import com.vaadin.testbench.elements.DateTimeFieldElement; +import com.vaadin.tests.tb3.SingleBrowserTest; + +public class DateTimeFieldZoneIdFutureSummerDatesTest + extends SingleBrowserTest { + + private static final String TESTING_ZONE_ID = "CET"; + private static final String TESTING_LOCALE = Locale.US.toString(); + + DateTimeFieldElement dateTimeFieldWithVariableRange; + DateTimeFieldElement dateTimeFieldWithDefaultRange; + + DateFieldElement transitionsStartYear; + DateFieldElement transitionsEndYear; + + ComboBoxElement zoneIdComboBox; + ComboBoxElement localeIdComboBox; + + @Before + public void init() { + openTestURL(); + + dateTimeFieldWithVariableRange = $(DateTimeFieldElement.class) + .id(VARIABLE_RANGE_DATEFIELD_ID); + dateTimeFieldWithDefaultRange = $(DateTimeFieldElement.class) + .id(FIXED_RANGE_DATEFIELD_ID); + + transitionsStartYear = $(DateFieldElement.class) + .id(START_YEAR_DATEFIELD_ID); + transitionsEndYear = $(DateFieldElement.class).id(END_YEAR_DATEFIELD_ID); + + zoneIdComboBox = $(ComboBoxElement.class).id(ZONE_ID); + zoneIdComboBox.selectByText(TESTING_ZONE_ID); + localeIdComboBox = $(ComboBoxElement.class).id(LOCALE_ID); + localeIdComboBox.selectByText(TESTING_LOCALE); + } + + @Test + public void dateTimeFieldWithCustomRangeShouldShowDSTWithinRange() { + final int testingRangeCentralYear = LocalDate.now().getYear() + 50; + final int testingRangeUpperYear = testingRangeCentralYear + 3; + final int testingRangeLowerYear = testingRangeCentralYear - 3; + + transitionsEndYear.setDate(LocalDate.of(testingRangeUpperYear, 1, 1)); + transitionsStartYear.setDate(LocalDate.of(testingRangeLowerYear, 1, 1)); + + LocalDateTime testingDateTime = LocalDateTime + .of(testingRangeCentralYear, Month.JULY, 1, 0, 0); + dateTimeFieldWithVariableRange.setDateTime(testingDateTime); + assertEndsWith(dateTimeFieldWithVariableRange, "CEST"); + + testingDateTime = LocalDateTime.of(testingRangeUpperYear, Month.JULY, 1, + 0, 0); + dateTimeFieldWithVariableRange.setDateTime(testingDateTime); + assertEndsWith(dateTimeFieldWithVariableRange, "CEST"); + + testingDateTime = LocalDateTime.of(testingRangeLowerYear, Month.JULY, 1, + 0, 0); + dateTimeFieldWithVariableRange.setDateTime(testingDateTime); + assertEndsWith(dateTimeFieldWithVariableRange, "CEST"); + } + + @Test + public void dateTimeFieldWithCustomRangeShouldNotShowDSTOutsideRange() { + final int testingRangeCentralYear = LocalDate.now().getYear() + 50; + final int testingRangeUpperYear = testingRangeCentralYear + 3; + final int testingRangeLowerYear = testingRangeCentralYear - 3; + + transitionsEndYear.setDate(LocalDate.of(testingRangeUpperYear, 1, 1)); + transitionsStartYear.setDate(LocalDate.of(testingRangeLowerYear, 1, 1)); + + // This year is out of specified range + LocalDateTime testingDateTime = LocalDateTime + .of(LocalDate.now().getYear(), Month.JULY, 1, 0, 0); + dateTimeFieldWithVariableRange.setDateTime(testingDateTime); + assertEndsWith(dateTimeFieldWithVariableRange, "CET"); + + // One year after the specified range + testingDateTime = LocalDateTime.of(testingRangeUpperYear + 1, + Month.JULY, 1, 0, 0); + dateTimeFieldWithVariableRange.setDateTime(testingDateTime); + assertEndsWith(dateTimeFieldWithVariableRange, "CET"); + + // One year before the specified range + testingDateTime = LocalDateTime.of(testingRangeLowerYear - 1, + Month.JULY, 1, 0, 0); + dateTimeFieldWithVariableRange.setDateTime(testingDateTime); + assertEndsWith(dateTimeFieldWithVariableRange, "CET"); + } + + @Test + public void dateTimeFieldWithDefaultRangeShouldShowDSTFrom1980Until20FutureYears() { + // The 1980 to 20 future years range is the hard-coded default range + // for which DST is shown if user doesn't provide a custom range + + final int testingRangeLowerYear = 1980; + LocalDateTime testingDateTime = LocalDateTime.of(testingRangeLowerYear, + Month.JULY, 1, 0, 0); + dateTimeFieldWithDefaultRange.setDateTime(testingDateTime); + assertEndsWith(dateTimeFieldWithDefaultRange, "CEST"); + + final int testingRangeUpperYear = LocalDate.now().getYear() + 20; + testingDateTime = LocalDateTime.of(testingRangeUpperYear, Month.JULY, 1, + 0, 0); + dateTimeFieldWithDefaultRange.setDateTime(testingDateTime); + assertEndsWith(dateTimeFieldWithDefaultRange, "CEST"); + + final int testingCurrYear = LocalDate.now().getYear(); + testingDateTime = LocalDateTime.of(testingCurrYear, Month.JULY, 1, 0, + 0); + dateTimeFieldWithDefaultRange.setDateTime(testingDateTime); + assertEndsWith(dateTimeFieldWithDefaultRange, "CEST"); + } + + @Test + public void dateTimeFieldWithDefaultRangeShouldNotShowDSTBefore1980() { + final LocalDateTime testingDateTime = LocalDateTime.of(1979, Month.JULY, + 1, 0, 0); + dateTimeFieldWithDefaultRange.setDateTime(testingDateTime); + assertEndsWith(dateTimeFieldWithDefaultRange, "CET"); + } + + @Test + public void dateTimeFieldWithDefaultRangeShouldNotShowDSTAfter20FutureYears() { + final LocalDateTime testingDateTime = LocalDateTime + .of(LocalDate.now().getYear() + 21, Month.JULY, 1, 0, 0); + dateTimeFieldWithDefaultRange.setDateTime(testingDateTime); + assertEndsWith(dateTimeFieldWithDefaultRange, "CET"); + } + + private void assertEndsWith(DateTimeFieldElement element, String suffix) { + final String text = element.getValue(); + assertTrue(text + " should end with " + suffix, text.endsWith(suffix)); + } +} |