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