1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.lang3.time;
18
19 import static org.junit.jupiter.api.Assertions.assertNotEquals;
20 import static org.junit.jupiter.api.Assertions.fail;
21 import static org.junit.jupiter.api.Assumptions.assumeFalse;
22 import static org.junit.jupiter.api.Assumptions.assumeTrue;
23
24 import java.text.DateFormatSymbols;
25 import java.text.ParseException;
26 import java.util.ArrayList;
27 import java.util.Comparator;
28 import java.util.Date;
29 import java.util.List;
30 import java.util.Locale;
31 import java.util.Objects;
32 import java.util.TimeZone;
33 import java.util.concurrent.atomic.AtomicInteger;
34
35 import org.apache.commons.lang3.AbstractLangTest;
36 import org.apache.commons.lang3.ArraySorter;
37 import org.apache.commons.lang3.JavaVersion;
38 import org.apache.commons.lang3.LocaleUtils;
39 import org.apache.commons.lang3.SystemUtils;
40 import org.junit.jupiter.api.AfterAll;
41 import org.junit.jupiter.api.Test;
42 import org.junit.jupiter.params.ParameterizedTest;
43 import org.junit.jupiter.params.provider.MethodSource;
44 import org.junitpioneer.jupiter.DefaultLocale;
45 import org.junitpioneer.jupiter.DefaultTimeZone;
46 import org.junitpioneer.jupiter.ReadsDefaultLocale;
47 import org.junitpioneer.jupiter.ReadsDefaultTimeZone;
48
49
50
51
52 @DefaultLocale(language = "en")
53 @DefaultTimeZone(TimeZones.GMT_ID)
54 @ReadsDefaultLocale
55 @ReadsDefaultTimeZone
56 class FastDateParser_TimeZoneStrategyTest extends AbstractLangTest {
57
58 private static final List<Locale> Java11Failures = new ArrayList<>();
59 private static final List<Locale> Java17Failures = new ArrayList<>();
60 private static final AtomicInteger fails = new AtomicInteger();
61
62 @AfterAll
63 public static void afterAll() {
64 if (!Java17Failures.isEmpty()) {
65 System.err.printf("Actual failures on Java 17+: %,d%n%s%n", Java17Failures.size(), Java17Failures);
66 }
67 if (!Java11Failures.isEmpty()) {
68 System.err.printf("Actual failures on Java 11: %,d%n%s%n", Java11Failures.size(), Java11Failures);
69 }
70 }
71
72 private String[][] getZoneStringsSorted(final Locale locale) {
73 return ArraySorter.sort(DateFormatSymbols.getInstance(locale).getZoneStrings(), Comparator.comparing(array -> array[0]));
74 }
75
76 @Test
77 void testLang1219() throws ParseException {
78 final FastDateParser parser = new FastDateParser("dd.MM.yyyy HH:mm:ss z", TimeZone.getDefault(), Locale.GERMAN);
79 final Date summer = parser.parse("26.10.2014 02:00:00 MESZ");
80 final Date standard = parser.parse("26.10.2014 02:00:00 MEZ");
81 assertNotEquals(summer.getTime(), standard.getTime());
82 }
83
84 @ParameterizedTest
85 @MethodSource("org.apache.commons.lang3.LocaleUtils#availableLocaleList()")
86 void testTimeZoneStrategy_DateFormatSymbols(final Locale locale) {
87 testTimeZoneStrategyPattern_DateFormatSymbols_getZoneStrings(locale);
88 }
89
90 @ParameterizedTest
91 @MethodSource("org.apache.commons.lang3.LocaleUtils#availableLocaleList()")
92 void testTimeZoneStrategy_TimeZone(final Locale locale) {
93 testTimeZoneStrategyPattern_TimeZone_getAvailableIDs(locale);
94 }
95
96 private void testTimeZoneStrategyPattern(final String languageTag, final String source) throws ParseException {
97 final Locale locale = Locale.forLanguageTag(languageTag);
98 final TimeZone timeZone = TimeZone.getTimeZone("Etc/UTC");
99 assumeFalse(LocaleUtils.isLanguageUndetermined(locale), () -> toFailureMessage(locale, languageTag, timeZone));
100 assumeTrue(LocaleUtils.isAvailableLocale(locale), () -> toFailureMessage(locale, languageTag, timeZone));
101 final FastDateParser parser = new FastDateParser("z", timeZone, locale);
102 parser.parse(source);
103 testTimeZoneStrategyPattern_TimeZone_getAvailableIDs(locale);
104 }
105
106 private void testTimeZoneStrategyPattern_DateFormatSymbols_getZoneStrings(final Locale locale) {
107 Objects.requireNonNull(locale, "locale");
108 assumeFalse(LocaleUtils.isLanguageUndetermined(locale), () -> toFailureMessage(locale, null, null));
109 assumeTrue(LocaleUtils.isAvailableLocale(locale), () -> toFailureMessage(locale, null, null));
110
111 final String[][] zones = getZoneStringsSorted(locale);
112 for (final String[] zone : zones) {
113 for (int zIndex = 1; zIndex < zone.length; ++zIndex) {
114 final String tzDisplay = zone[zIndex];
115 if (tzDisplay == null) {
116 break;
117 }
118 final TimeZone timeZone = TimeZone.getDefault();
119 final FastDateParser parser = new FastDateParser("z", timeZone, locale);
120
121 try {
122 parser.parse(tzDisplay);
123 } catch (final ParseException e) {
124
125
126 final String localeStr = locale.toString();
127 if (SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_17)
128 && (localeStr.contains("_") || "Coordinated Universal Time".equals(tzDisplay)
129 || "sommartid â Atyrau".equals(tzDisplay))) {
130 Java17Failures.add(locale);
131
132 System.err.printf(
133 "[%,d][%s] Java %s %s - Mark as an assumption failure instead of a hard fail: locale = '%s', parse = '%s'%n",
134 fails.incrementAndGet(),
135 Thread.currentThread().getName(),
136 SystemUtils.JAVA_VENDOR,
137 SystemUtils.JAVA_VM_VERSION,
138 localeStr, tzDisplay);
139 assumeTrue(false, localeStr);
140 continue;
141 }
142 if (SystemUtils.IS_JAVA_11
143 && (localeStr.contains("_") || "Coordinated Universal Time".equals(tzDisplay))) {
144 Java11Failures.add(locale);
145
146 System.err.printf(
147 "[%,d][%s] Java %s %s - Mark as an assumption failure instead of a hard fail: locale = '%s', parse = '%s'%n",
148 fails.incrementAndGet(),
149 Thread.currentThread().getName(),
150 SystemUtils.JAVA_VENDOR,
151 SystemUtils.JAVA_VM_VERSION,
152 localeStr, tzDisplay);
153 assumeTrue(false, localeStr);
154 continue;
155 }
156
157 fail(String.format("%s: with locale = %s, zIndex = %,d, tzDisplay = '%s', parser = '%s'", e,
158 localeStr, zIndex, tzDisplay, parser), e);
159 }
160 }
161 }
162 }
163
164
165
166
167
168
169 private void testTimeZoneStrategyPattern_TimeZone_getAvailableIDs(final Locale locale) {
170 Objects.requireNonNull(locale, "locale");
171 assumeFalse(LocaleUtils.isLanguageUndetermined(locale), () -> toFailureMessage(locale, null, null));
172 assumeTrue(LocaleUtils.isAvailableLocale(locale), () -> toFailureMessage(locale, null, null));
173 for (final String id : ArraySorter.sort(TimeZone.getAvailableIDs())) {
174 final TimeZone timeZone = TimeZone.getTimeZone(id);
175 final String displayName = timeZone.getDisplayName(locale);
176 final FastDateParser parser = new FastDateParser("z", timeZone, locale);
177 try {
178 parser.parse(displayName);
179 } catch (final ParseException e) {
180
181
182 fail(String.format("%s: with id = '%s', displayName = '%s', %s, parser = '%s'", e, id, displayName,
183 toFailureMessage(locale, null, timeZone), parser.toStringAll()), e);
184 }
185 }
186 }
187
188 @Test
189 void testTimeZoneStrategyPattern_zh_HK_Hans() throws ParseException {
190 testTimeZoneStrategyPattern("zh_HK_#Hans", "?????????");
191 }
192
193
194
195
196
197
198
199
200
201
202
203
204 @Test
205 void testTimeZoneStrategyPatternPortugal() throws ParseException {
206 testTimeZoneStrategyPattern("pt_PT", "Horário do Meridiano de Greenwich");
207 }
208
209
210
211
212
213
214
215
216
217
218
219
220 @Test
221 void testTimeZoneStrategyPatternSuriname() throws ParseException {
222 testTimeZoneStrategyPattern("sr_ME_#Cyrl", "Srednje vreme po GriniÄu");
223 }
224
225 private String toFailureMessage(final Locale locale, final String languageTag, final TimeZone timeZone) {
226 return String.format("locale = %s, languageTag = '%s', isAvailableLocale = %s, isLanguageUndetermined = %s, timeZone = %s", languageTag, locale,
227 LocaleUtils.isAvailableLocale(locale), LocaleUtils.isLanguageUndetermined(locale), TimeZones.toTimeZone(timeZone));
228 }
229 }