1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.lang3.time;
19
20 import static org.apache.commons.lang3.LangAssertions.assertIllegalArgumentException;
21 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
22 import static org.junit.jupiter.api.Assertions.assertEquals;
23 import static org.junit.jupiter.api.Assertions.assertFalse;
24 import static org.junit.jupiter.api.Assertions.assertNotEquals;
25 import static org.junit.jupiter.api.Assertions.assertNotNull;
26 import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
27 import static org.junit.jupiter.api.Assertions.assertTrue;
28
29 import java.lang.reflect.Constructor;
30 import java.lang.reflect.Modifier;
31 import java.time.Duration;
32 import java.util.Calendar;
33 import java.util.TimeZone;
34
35 import org.apache.commons.lang3.AbstractLangTest;
36 import org.apache.commons.lang3.time.DurationFormatUtils.Token;
37 import org.junit.jupiter.api.Test;
38 import org.junit.jupiter.params.ParameterizedTest;
39 import org.junit.jupiter.params.provider.Arguments;
40 import org.junit.jupiter.params.provider.MethodSource;
41 import org.junitpioneer.jupiter.DefaultTimeZone;
42
43
44
45
46
47
48
49 class DurationFormatUtilsTest extends AbstractLangTest {
50
51 private static final int FOUR_YEARS = 365 * 3 + 366;
52
53 private static Arguments[] testLANG1827() {
54 final long twoHours = Duration.ofHours(2).toMillis();
55 final long twoHoursThirtyMin = Duration.ofHours(2).plusMinutes(30).toMillis();
56 final long oneDayTwoHours = Duration.ofDays(1).plusHours(2).toMillis();
57 return new Arguments[] {
58 Arguments.of("escaped quote inside literal", "2 o'clock", twoHours, "H' o''clock'"),
59 Arguments.of("escaped quote at start of literal", "it's 2 hours", twoHours, "'it''s 'H' hours'"),
60 Arguments.of("escaped quote outside literal", "2'30", twoHoursThirtyMin, "H''m"),
61 Arguments.of("multiple escaped quotes", "it's been 1 day's and 2 hour's", oneDayTwoHours,
62 "'it''s been 'd' day''s and 'H' hour''s'"),
63 Arguments.of("standalone escaped quote", "2h'30m", twoHoursThirtyMin, "H'h'''m'm'"),
64 Arguments.of("escaped quote inside optional block", "2 hour's", twoHours, "[d' day''s ']H' hour''s'"),
65 Arguments.of("existing literal behavior", "2 hours 30 minutes", twoHoursThirtyMin, "H' hours 'm' minutes'")
66 };
67 }
68
69 private void assertEqualDuration(final String expected, final int[] start, final int[] end, final String format) {
70 assertEqualDuration(null, expected, start, end, format);
71 }
72
73 private void assertEqualDuration(final String message, final String expected, final int[] start, final int[] end, final String format) {
74 final Calendar cal1 = Calendar.getInstance();
75 cal1.set(start[0], start[1], start[2], start[3], start[4], start[5]);
76 cal1.set(Calendar.MILLISECOND, 0);
77 final Calendar cal2 = Calendar.getInstance();
78 cal2.set(end[0], end[1], end[2], end[3], end[4], end[5]);
79 cal2.set(Calendar.MILLISECOND, 0);
80 final long milli1 = cal1.getTime().getTime();
81 final long milli2 = cal2.getTime().getTime();
82 final String result = DurationFormatUtils.formatPeriod(milli1, milli2, format);
83 if (message == null) {
84 assertEquals(expected, result);
85 } else {
86 assertEquals(expected, result, message);
87 }
88 }
89
90 private void assertFormatPeriodOneMilli(final long startMillis, final long endMillis) {
91 final TimeZone gmt = TimeZones.getTimeZone("GMT");
92 assertEquals("1", DurationFormatUtils.formatPeriod(startMillis, endMillis, "S", true, gmt));
93 assertEquals("0/0/0/0/0/0.001",
94 DurationFormatUtils.formatPeriod(startMillis, endMillis, "y/M/d/H/m/s.S", true, gmt));
95
96 assertEquals(DurationFormatUtils.formatDuration(endMillis - startMillis, "S"),
97 DurationFormatUtils.formatPeriod(startMillis, endMillis, "S", true, gmt));
98 }
99
100 private void bruteForce(final int year, final int month, final int day, final String format, final int calendarType) {
101 final String msg = year + "-" + month + "-" + day + " to ";
102 final Calendar c = Calendar.getInstance();
103 c.set(year, month, day, 0, 0, 0);
104 final int[] array1 = { year, month, day, 0, 0, 0 };
105 final int[] array2 = { year, month, day, 0, 0, 0 };
106 for (int i = 0; i < FOUR_YEARS; i++) {
107 array2[0] = c.get(Calendar.YEAR);
108 array2[1] = c.get(Calendar.MONTH);
109 array2[2] = c.get(Calendar.DAY_OF_MONTH);
110 final String tmpMsg = msg + array2[0] + "-" + array2[1] + "-" + array2[2] + " at ";
111 assertEqualDuration(tmpMsg + i, Integer.toString(i), array1, array2, format);
112 c.add(calendarType, 1);
113 }
114 }
115
116 private DurationFormatUtils.Token createTokenWithCount(final CharSequence value, final int count) {
117 final DurationFormatUtils.Token token = new DurationFormatUtils.Token(value, false, -1);
118
119 assertNotNull(token.toString());
120 for (int i = 1; i < count; i++) {
121 token.increment();
122 assertNotNull(token.toString());
123 }
124 return token;
125 }
126
127 @Test
128 void testAlternatingLiteralOptionals() {
129 final String format = "['d'dH'h'][m'm']['s's]['ms'S]";
130 assertEquals("d1", DurationFormatUtils.formatDuration(Duration.ofDays(1).toMillis(), format));
131 assertEquals("1h", DurationFormatUtils.formatDuration(Duration.ofHours(1).toMillis(), format));
132 assertEquals("1m", DurationFormatUtils.formatDuration(Duration.ofMinutes(1).toMillis(), format));
133 assertEquals("s1", DurationFormatUtils.formatDuration(Duration.ofSeconds(1).toMillis(), format));
134 assertEquals("ms001", DurationFormatUtils.formatDuration(Duration.ofMillis(1).toMillis(), format));
135 assertEquals("d1s1", DurationFormatUtils.formatDuration(Duration.ofDays(1).plusSeconds(1).toMillis(), format));
136 assertEquals("d11h", DurationFormatUtils.formatDuration(Duration.ofDays(1).plusHours(1).toMillis(), format));
137 assertEquals("d11h1m", DurationFormatUtils.formatDuration(Duration.ofDays(1).plusHours(1).plusMinutes(1).toMillis(), format));
138 assertEquals("d11h1ms1", DurationFormatUtils.formatDuration(Duration.ofDays(1).plusHours(1).plusMinutes(1).plusSeconds(1).toMillis(), format));
139 assertEquals("d11h1ms1ms001",
140 DurationFormatUtils.formatDuration(Duration.ofDays(1).plusHours(1).plusMinutes(1).plusSeconds(1).plusMillis(1).toMillis(), format));
141 }
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174 @Test
175 @DefaultTimeZone(TimeZones.GMT_ID)
176 void testEdgeDurations() {
177
178 TimeZone.setDefault(FastTimeZone.getGmtTimeZone());
179 assertEqualDuration("01", new int[] { 2006, 0, 15, 0, 0, 0 }, new int[] { 2006, 2, 10, 0, 0, 0 }, "MM");
180 assertEqualDuration("12", new int[] { 2005, 0, 15, 0, 0, 0 }, new int[] { 2006, 0, 15, 0, 0, 0 }, "MM");
181 assertEqualDuration("12", new int[] { 2005, 0, 15, 0, 0, 0 }, new int[] { 2006, 0, 16, 0, 0, 0 }, "MM");
182 assertEqualDuration("11", new int[] { 2005, 0, 15, 0, 0, 0 }, new int[] { 2006, 0, 14, 0, 0, 0 }, "MM");
183 assertEqualDuration("01 26", new int[] { 2006, 0, 15, 0, 0, 0 }, new int[] { 2006, 2, 10, 0, 0, 0 }, "MM dd");
184 assertEqualDuration("54", new int[] { 2006, 0, 15, 0, 0, 0 }, new int[] { 2006, 2, 10, 0, 0, 0 }, "dd");
185 assertEqualDuration("09 12", new int[] { 2006, 1, 20, 0, 0, 0 }, new int[] { 2006, 11, 4, 0, 0, 0 }, "MM dd");
186 assertEqualDuration("287", new int[] { 2006, 1, 20, 0, 0, 0 }, new int[] { 2006, 11, 4, 0, 0, 0 }, "dd");
187 assertEqualDuration("11 30", new int[] { 2006, 0, 2, 0, 0, 0 }, new int[] { 2007, 0, 1, 0, 0, 0 }, "MM dd");
188 assertEqualDuration("364", new int[] { 2006, 0, 2, 0, 0, 0 }, new int[] { 2007, 0, 1, 0, 0, 0 }, "dd");
189 assertEqualDuration("12 00", new int[] { 2006, 0, 1, 0, 0, 0 }, new int[] { 2007, 0, 1, 0, 0, 0 }, "MM dd");
190 assertEqualDuration("365", new int[] { 2006, 0, 1, 0, 0, 0 }, new int[] { 2007, 0, 1, 0, 0, 0 }, "dd");
191 assertEqualDuration("31", new int[] { 2006, 0, 1, 0, 0, 0 }, new int[] { 2006, 1, 1, 0, 0, 0 }, "dd");
192 assertEqualDuration("92", new int[] { 2005, 9, 1, 0, 0, 0 }, new int[] { 2006, 0, 1, 0, 0, 0 }, "dd");
193 assertEqualDuration("77", new int[] { 2005, 9, 16, 0, 0, 0 }, new int[] { 2006, 0, 1, 0, 0, 0 }, "dd");
194
195 assertEqualDuration("136", new int[] { 2005, 9, 16, 0, 0, 0 }, new int[] { 2006, 2, 1, 0, 0, 0 }, "dd");
196
197 assertEqualDuration("136", new int[] { 2004, 9, 16, 0, 0, 0 }, new int[] { 2005, 2, 1, 0, 0, 0 }, "dd");
198
199 assertEqualDuration("137", new int[] { 2003, 9, 16, 0, 0, 0 }, new int[] { 2004, 2, 1, 0, 0, 0 }, "dd");
200
201 assertEqualDuration("135", new int[] { 2003, 9, 16, 0, 0, 0 }, new int[] { 2004, 1, 28, 0, 0, 0 }, "dd");
202 assertEqualDuration("364", new int[] { 2007, 0, 2, 0, 0, 0 }, new int[] { 2008, 0, 1, 0, 0, 0 }, "dd");
203 assertEqualDuration("729", new int[] { 2006, 0, 2, 0, 0, 0 }, new int[] { 2008, 0, 1, 0, 0, 0 }, "dd");
204 assertEqualDuration("365", new int[] { 2007, 2, 2, 0, 0, 0 }, new int[] { 2008, 2, 1, 0, 0, 0 }, "dd");
205 assertEqualDuration("333", new int[] { 2007, 1, 2, 0, 0, 0 }, new int[] { 2008, 0, 1, 0, 0, 0 }, "dd");
206 assertEqualDuration("28", new int[] { 2008, 1, 2, 0, 0, 0 }, new int[] { 2008, 2, 1, 0, 0, 0 }, "dd");
207 assertEqualDuration("393", new int[] { 2007, 1, 2, 0, 0, 0 }, new int[] { 2008, 2, 1, 0, 0, 0 }, "dd");
208 assertEqualDuration("369", new int[] { 2004, 0, 29, 0, 0, 0 }, new int[] { 2005, 1, 1, 0, 0, 0 }, "dd");
209 assertEqualDuration("338", new int[] { 2004, 1, 29, 0, 0, 0 }, new int[] { 2005, 1, 1, 0, 0, 0 }, "dd");
210 assertEqualDuration("28", new int[] { 2004, 2, 8, 0, 0, 0 }, new int[] { 2004, 3, 5, 0, 0, 0 }, "dd");
211 assertEqualDuration("48", new int[] { 1992, 1, 29, 0, 0, 0 }, new int[] { 1996, 1, 29, 0, 0, 0 }, "M");
212
213
214 assertEqualDuration("11", new int[] { 1996, 1, 29, 0, 0, 0 }, new int[] { 1997, 1, 28, 0, 0, 0 }, "M");
215
216 assertEqualDuration("11 28", new int[] { 1996, 1, 29, 0, 0, 0 }, new int[] { 1997, 1, 28, 0, 0, 0 }, "M d");
217 }
218
219 @Test
220 void testEmptyOptionals() {
221 assertEquals("", DurationFormatUtils.formatDuration(0L, "[d'd'][H'h'][m'm'][s's']"));
222 assertEquals("", DurationFormatUtils.formatDuration(0L, "['d''h''m''s's]"));
223 }
224
225 @Test
226 void testFormatDuration() {
227 long duration = 0;
228 assertEquals("0", DurationFormatUtils.formatDuration(duration, "y"));
229 assertEquals("0", DurationFormatUtils.formatDuration(duration, "M"));
230 assertEquals("0", DurationFormatUtils.formatDuration(duration, "d"));
231 assertEquals("0", DurationFormatUtils.formatDuration(duration, "H"));
232 assertEquals("0", DurationFormatUtils.formatDuration(duration, "m"));
233 assertEquals("0", DurationFormatUtils.formatDuration(duration, "s"));
234 assertEquals("0", DurationFormatUtils.formatDuration(duration, "S"));
235 assertEquals("0000", DurationFormatUtils.formatDuration(duration, "SSSS"));
236 assertEquals("0000", DurationFormatUtils.formatDuration(duration, "yyyy"));
237 assertEquals("0000", DurationFormatUtils.formatDuration(duration, "yyMM"));
238
239 duration = 60 * 1000;
240 assertEquals("0", DurationFormatUtils.formatDuration(duration, "y"));
241 assertEquals("0", DurationFormatUtils.formatDuration(duration, "M"));
242 assertEquals("0", DurationFormatUtils.formatDuration(duration, "d"));
243 assertEquals("0", DurationFormatUtils.formatDuration(duration, "H"));
244 assertEquals("1", DurationFormatUtils.formatDuration(duration, "m"));
245 assertEquals("60", DurationFormatUtils.formatDuration(duration, "s"));
246 assertEquals("60000", DurationFormatUtils.formatDuration(duration, "S"));
247 assertEquals("01:00", DurationFormatUtils.formatDuration(duration, "mm:ss"));
248
249 final Calendar base = Calendar.getInstance();
250 base.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
251 base.set(Calendar.MILLISECOND, 0);
252
253 final Calendar cal = Calendar.getInstance();
254 cal.set(2003, Calendar.FEBRUARY, 1, 0, 0, 0);
255 cal.set(Calendar.MILLISECOND, 0);
256 duration = cal.getTime().getTime() - base.getTime().getTime();
257
258
259 final int days = 366 + 365 + 365 + 31;
260 assertEquals("0 0 " + days, DurationFormatUtils.formatDuration(duration, "y M d"));
261 }
262
263 @Test
264 void testFormatDurationHMS() {
265 long time = 0;
266 assertEquals("00:00:00.000", DurationFormatUtils.formatDurationHMS(time));
267 time = 1;
268 assertEquals("00:00:00.001", DurationFormatUtils.formatDurationHMS(time));
269 time = 15;
270 assertEquals("00:00:00.015", DurationFormatUtils.formatDurationHMS(time));
271 time = 165;
272 assertEquals("00:00:00.165", DurationFormatUtils.formatDurationHMS(time));
273 time = 1675;
274 assertEquals("00:00:01.675", DurationFormatUtils.formatDurationHMS(time));
275 time = 13465;
276 assertEquals("00:00:13.465", DurationFormatUtils.formatDurationHMS(time));
277 time = 72789;
278 assertEquals("00:01:12.789", DurationFormatUtils.formatDurationHMS(time));
279 time = 12789 + 32 * 60000;
280 assertEquals("00:32:12.789", DurationFormatUtils.formatDurationHMS(time));
281 time = 12789 + 62 * 60000;
282 assertEquals("01:02:12.789", DurationFormatUtils.formatDurationHMS(time));
283 }
284
285 @Test
286 void testFormatDurationISO() {
287 assertEquals("P0Y0M0DT0H0M0.000S", DurationFormatUtils.formatDurationISO(0L));
288 assertEquals("P0Y0M0DT0H0M0.001S", DurationFormatUtils.formatDurationISO(1L));
289 assertEquals("P0Y0M0DT0H0M0.010S", DurationFormatUtils.formatDurationISO(10L));
290 assertEquals("P0Y0M0DT0H0M0.100S", DurationFormatUtils.formatDurationISO(100L));
291 assertEquals("P0Y0M0DT0H1M15.321S", DurationFormatUtils.formatDurationISO(75321L));
292 }
293
294
295
296
297 @Test
298 void testFormatDurationPluralWords() {
299 final long oneSecond = 1000;
300 final long oneMinute = oneSecond * 60;
301 final long oneHour = oneMinute * 60;
302 final long oneDay = oneHour * 24;
303 String text;
304
305 text = DurationFormatUtils.formatDurationWords(oneSecond, false, false);
306 assertEquals("0 days 0 hours 0 minutes 1 second", text);
307 text = DurationFormatUtils.formatDurationWords(oneSecond * 2, false, false);
308 assertEquals("0 days 0 hours 0 minutes 2 seconds", text);
309 text = DurationFormatUtils.formatDurationWords(oneSecond * 11, false, false);
310 assertEquals("0 days 0 hours 0 minutes 11 seconds", text);
311
312 text = DurationFormatUtils.formatDurationWords(oneMinute, false, false);
313 assertEquals("0 days 0 hours 1 minute 0 seconds", text);
314 text = DurationFormatUtils.formatDurationWords(oneMinute * 2, false, false);
315 assertEquals("0 days 0 hours 2 minutes 0 seconds", text);
316 text = DurationFormatUtils.formatDurationWords(oneMinute * 11, false, false);
317 assertEquals("0 days 0 hours 11 minutes 0 seconds", text);
318 text = DurationFormatUtils.formatDurationWords(oneMinute + oneSecond, false, false);
319 assertEquals("0 days 0 hours 1 minute 1 second", text);
320
321 text = DurationFormatUtils.formatDurationWords(oneHour, false, false);
322 assertEquals("0 days 1 hour 0 minutes 0 seconds", text);
323 text = DurationFormatUtils.formatDurationWords(oneHour * 2, false, false);
324 assertEquals("0 days 2 hours 0 minutes 0 seconds", text);
325 text = DurationFormatUtils.formatDurationWords(oneHour * 11, false, false);
326 assertEquals("0 days 11 hours 0 minutes 0 seconds", text);
327 text = DurationFormatUtils.formatDurationWords(oneHour + oneMinute + oneSecond, false, false);
328 assertEquals("0 days 1 hour 1 minute 1 second", text);
329
330 text = DurationFormatUtils.formatDurationWords(oneDay, false, false);
331 assertEquals("1 day 0 hours 0 minutes 0 seconds", text);
332 text = DurationFormatUtils.formatDurationWords(oneDay * 2, false, false);
333 assertEquals("2 days 0 hours 0 minutes 0 seconds", text);
334 text = DurationFormatUtils.formatDurationWords(oneDay * 11, false, false);
335 assertEquals("11 days 0 hours 0 minutes 0 seconds", text);
336 text = DurationFormatUtils.formatDurationWords(oneDay + oneHour + oneMinute + oneSecond, false, false);
337 assertEquals("1 day 1 hour 1 minute 1 second", text);
338 }
339
340 @Test
341 void testFormatDurationWords() {
342 String text;
343
344 text = DurationFormatUtils.formatDurationWords(50 * 1000, true, false);
345 assertEquals("50 seconds", text);
346 text = DurationFormatUtils.formatDurationWords(65 * 1000, true, false);
347 assertEquals("1 minute 5 seconds", text);
348 text = DurationFormatUtils.formatDurationWords(120 * 1000, true, false);
349 assertEquals("2 minutes 0 seconds", text);
350 text = DurationFormatUtils.formatDurationWords(121 * 1000, true, false);
351 assertEquals("2 minutes 1 second", text);
352 text = DurationFormatUtils.formatDurationWords(72 * 60 * 1000, true, false);
353 assertEquals("1 hour 12 minutes 0 seconds", text);
354 text = DurationFormatUtils.formatDurationWords(24 * 60 * 60 * 1000, true, false);
355 assertEquals("1 day 0 hours 0 minutes 0 seconds", text);
356
357 text = DurationFormatUtils.formatDurationWords(50 * 1000, true, true);
358 assertEquals("50 seconds", text);
359 text = DurationFormatUtils.formatDurationWords(65 * 1000, true, true);
360 assertEquals("1 minute 5 seconds", text);
361 text = DurationFormatUtils.formatDurationWords(120 * 1000, true, true);
362 assertEquals("2 minutes", text);
363 text = DurationFormatUtils.formatDurationWords(121 * 1000, true, true);
364 assertEquals("2 minutes 1 second", text);
365 text = DurationFormatUtils.formatDurationWords(72 * 60 * 1000, true, true);
366 assertEquals("1 hour 12 minutes", text);
367 text = DurationFormatUtils.formatDurationWords(24 * 60 * 60 * 1000, true, true);
368 assertEquals("1 day", text);
369
370 text = DurationFormatUtils.formatDurationWords(50 * 1000, false, true);
371 assertEquals("0 days 0 hours 0 minutes 50 seconds", text);
372 text = DurationFormatUtils.formatDurationWords(65 * 1000, false, true);
373 assertEquals("0 days 0 hours 1 minute 5 seconds", text);
374 text = DurationFormatUtils.formatDurationWords(120 * 1000, false, true);
375 assertEquals("0 days 0 hours 2 minutes", text);
376 text = DurationFormatUtils.formatDurationWords(121 * 1000, false, true);
377 assertEquals("0 days 0 hours 2 minutes 1 second", text);
378 text = DurationFormatUtils.formatDurationWords(72 * 60 * 1000, false, true);
379 assertEquals("0 days 1 hour 12 minutes", text);
380 text = DurationFormatUtils.formatDurationWords(24 * 60 * 60 * 1000, false, true);
381 assertEquals("1 day", text);
382
383 text = DurationFormatUtils.formatDurationWords(50 * 1000, false, false);
384 assertEquals("0 days 0 hours 0 minutes 50 seconds", text);
385 text = DurationFormatUtils.formatDurationWords(65 * 1000, false, false);
386 assertEquals("0 days 0 hours 1 minute 5 seconds", text);
387 text = DurationFormatUtils.formatDurationWords(120 * 1000, false, false);
388 assertEquals("0 days 0 hours 2 minutes 0 seconds", text);
389 text = DurationFormatUtils.formatDurationWords(121 * 1000, false, false);
390 assertEquals("0 days 0 hours 2 minutes 1 second", text);
391 text = DurationFormatUtils.formatDurationWords(72 * 60 * 1000, false, false);
392 assertEquals("0 days 1 hour 12 minutes 0 seconds", text);
393 text = DurationFormatUtils.formatDurationWords(24 * 60 * 60 * 1000 + 72 * 60 * 1000, false, false);
394 assertEquals("1 day 1 hour 12 minutes 0 seconds", text);
395 text = DurationFormatUtils.formatDurationWords(2 * 24 * 60 * 60 * 1000 + 72 * 60 * 1000, false, false);
396 assertEquals("2 days 1 hour 12 minutes 0 seconds", text);
397 for (int i = 2; i < 31; i++) {
398 text = DurationFormatUtils.formatDurationWords(i * 24 * 60 * 60 * 1000L, false, false);
399 assertEquals(i + " days 0 hours 0 minutes 0 seconds", text);
400 }
401 }
402
403 @Test
404 void testFormatNegativeDuration() {
405 assertIllegalArgumentException(() -> DurationFormatUtils.formatDuration(-5000, "S", true));
406 }
407
408 @Test
409 void testFormatNegativeDurationHMS() {
410 assertIllegalArgumentException(() -> DurationFormatUtils.formatDurationHMS(-5000));
411 }
412
413 @Test
414 void testFormatNegativeDurationISO() {
415 assertIllegalArgumentException(() -> DurationFormatUtils.formatDurationISO(-5000));
416 }
417
418 @Test
419 void testFormatNegativeDurationWords() {
420 assertIllegalArgumentException(() -> DurationFormatUtils.formatDurationWords(-5000, true, true));
421 }
422
423 @Test
424 void testFormatPeriod() {
425 final Calendar cal1970 = Calendar.getInstance();
426 cal1970.set(1970, Calendar.JANUARY, 1, 0, 0, 0);
427 cal1970.set(Calendar.MILLISECOND, 0);
428 final long time1970 = cal1970.getTime().getTime();
429
430 assertEquals("0", DurationFormatUtils.formatPeriod(time1970, time1970, "y"));
431 assertEquals("0", DurationFormatUtils.formatPeriod(time1970, time1970, "M"));
432 assertEquals("0", DurationFormatUtils.formatPeriod(time1970, time1970, "d"));
433 assertEquals("0", DurationFormatUtils.formatPeriod(time1970, time1970, "H"));
434 assertEquals("0", DurationFormatUtils.formatPeriod(time1970, time1970, "m"));
435 assertEquals("0", DurationFormatUtils.formatPeriod(time1970, time1970, "s"));
436 assertEquals("0", DurationFormatUtils.formatPeriod(time1970, time1970, "S"));
437 assertEquals("0000", DurationFormatUtils.formatPeriod(time1970, time1970, "SSSS"));
438 assertEquals("0000", DurationFormatUtils.formatPeriod(time1970, time1970, "yyyy"));
439 assertEquals("0000", DurationFormatUtils.formatPeriod(time1970, time1970, "yyMM"));
440
441 long time = time1970 + 60 * 1000;
442 assertEquals("0", DurationFormatUtils.formatPeriod(time1970, time, "y"));
443 assertEquals("0", DurationFormatUtils.formatPeriod(time1970, time, "M"));
444 assertEquals("0", DurationFormatUtils.formatPeriod(time1970, time, "d"));
445 assertEquals("0", DurationFormatUtils.formatPeriod(time1970, time, "H"));
446 assertEquals("1", DurationFormatUtils.formatPeriod(time1970, time, "m"));
447 assertEquals("60", DurationFormatUtils.formatPeriod(time1970, time, "s"));
448 assertEquals("60000", DurationFormatUtils.formatPeriod(time1970, time, "S"));
449 assertEquals("01:00", DurationFormatUtils.formatPeriod(time1970, time, "mm:ss"));
450
451 final Calendar cal = Calendar.getInstance();
452 cal.set(1973, Calendar.JULY, 1, 0, 0, 0);
453 cal.set(Calendar.MILLISECOND, 0);
454 time = cal.getTime().getTime();
455 assertEquals("36", DurationFormatUtils.formatPeriod(time1970, time, "yM"));
456 assertEquals("3 years 6 months", DurationFormatUtils.formatPeriod(time1970, time, "y' years 'M' months'"));
457 assertEquals("03/06", DurationFormatUtils.formatPeriod(time1970, time, "yy/MM"));
458
459 cal.set(1973, Calendar.NOVEMBER, 1, 0, 0, 0);
460 cal.set(Calendar.MILLISECOND, 0);
461 time = cal.getTime().getTime();
462 assertEquals("310", DurationFormatUtils.formatPeriod(time1970, time, "yM"));
463 assertEquals("3 years 10 months", DurationFormatUtils.formatPeriod(time1970, time, "y' years 'M' months'"));
464 assertEquals("03/10", DurationFormatUtils.formatPeriod(time1970, time, "yy/MM"));
465
466 cal.set(1974, Calendar.JANUARY, 1, 0, 0, 0);
467 cal.set(Calendar.MILLISECOND, 0);
468 time = cal.getTime().getTime();
469 assertEquals("40", DurationFormatUtils.formatPeriod(time1970, time, "yM"));
470 assertEquals("4 'years 0 months", DurationFormatUtils.formatPeriod(time1970, time, "y' ''years' M 'months'"));
471 assertEquals("4 years 0 months", DurationFormatUtils.formatPeriod(time1970, time, "y' years 'M' months'"));
472 assertEquals("4years 0months", DurationFormatUtils.formatPeriod(time1970, time, "y'years 'M'months'"));
473 assertEquals("04/00", DurationFormatUtils.formatPeriod(time1970, time, "yy/MM"));
474 assertEquals("48", DurationFormatUtils.formatPeriod(time1970, time, "M"));
475 assertEquals("48", DurationFormatUtils.formatPeriod(time1970, time, "MM"));
476 assertEquals("048", DurationFormatUtils.formatPeriod(time1970, time, "MMM"));
477
478 assertEquals("hello", DurationFormatUtils.formatPeriod(time1970, time, "'hello'"));
479 assertEquals("hello'world", DurationFormatUtils.formatPeriod(time1970, time, "'hello''world'"));
480 }
481
482 @Test
483 void testFormatPeriodeStartGreaterEnd() {
484 assertIllegalArgumentException(() -> DurationFormatUtils.formatPeriod(5000, 2500, "yy/MM"));
485 }
486
487 @Test
488 void testFormatPeriodExtremeDatesWithOffsets() {
489 assertTimeoutPreemptively(Duration.ofSeconds(5), () -> {
490 final String format = "d H m s S";
491 final String expected = DurationFormatUtils.formatDuration(Long.MAX_VALUE, format);
492 for (final String zone : new String[] { "GMT", "GMT+14:00", "GMT-12:00" }) {
493 final TimeZone timeZone = TimeZones.getTimeZone(zone);
494 assertEquals(expected, DurationFormatUtils.formatPeriod(0, Long.MAX_VALUE, format, true, timeZone), zone);
495 assertEquals(expected, DurationFormatUtils.formatPeriod(Long.MIN_VALUE, -1, format, true, timeZone), zone);
496 assertEquals("213503982334", DurationFormatUtils.formatPeriod(Long.MIN_VALUE, Long.MAX_VALUE, "d", true, timeZone), zone);
497 }
498 });
499 }
500
501 @SuppressWarnings("deprecation")
502 @Test
503 void testFormatPeriodISO() {
504 final TimeZone timeZone = TimeZones.getTimeZone("GMT-3");
505 final Calendar base = Calendar.getInstance(timeZone);
506 base.set(1970, Calendar.JANUARY, 1, 0, 0, 0);
507 base.set(Calendar.MILLISECOND, 0);
508
509 final Calendar cal = Calendar.getInstance(timeZone);
510 cal.set(2002, Calendar.FEBRUARY, 23, 9, 11, 12);
511 cal.set(Calendar.MILLISECOND, 1);
512 String text;
513
514 text = DateFormatUtils.format(cal, DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(), timeZone);
515 assertEquals("2002-02-23T09:11:12-03:00", text);
516
517 text = DurationFormatUtils.formatPeriod(base.getTime().getTime(), cal.getTime().getTime(),
518 DurationFormatUtils.ISO_EXTENDED_FORMAT_PATTERN, false, timeZone);
519 assertEquals("P32Y1M22DT9H11M12.001S", text);
520
521 cal.set(1971, Calendar.FEBRUARY, 3, 10, 30, 0);
522 cal.set(Calendar.MILLISECOND, 0);
523 text = DurationFormatUtils.formatPeriod(base.getTime().getTime(), cal.getTime().getTime(),
524 DurationFormatUtils.ISO_EXTENDED_FORMAT_PATTERN, false, timeZone);
525 assertEquals("P1Y1M2DT10H30M0.000S", text);
526
527
528 }
529
530 @Test
531 void testFormatPeriodISOAcrossMonths() {
532 final TimeZone tz = TimeZones.getTimeZone("GMT-3");
533
534 final Calendar start = Calendar.getInstance(tz);
535 start.clear();
536 start.set(2020, Calendar.JANUARY, 31);
537 final Calendar end = Calendar.getInstance(tz);
538 end.clear();
539 end.set(2020, Calendar.FEBRUARY, 1);
540 final String result = DurationFormatUtils.formatPeriod(start.getTimeInMillis(), end.getTimeInMillis(), DurationFormatUtils.ISO_EXTENDED_FORMAT_PATTERN,
541 false, tz);
542 assertEquals("P0Y0M1DT0H0M0.000S", result);
543 }
544
545 @Test
546 void testFormatPeriodISOLeapYearBoundary() {
547 final TimeZone tz = TimeZones.getTimeZone("GMT-3");
548
549 final Calendar start = Calendar.getInstance(tz);
550 start.clear();
551 start.set(2020, Calendar.FEBRUARY, 29);
552 final Calendar end = Calendar.getInstance(tz);
553 end.clear();
554 end.set(2020, Calendar.MARCH, 1);
555 final String result = DurationFormatUtils.formatPeriod(start.getTimeInMillis(), end.getTimeInMillis(), DurationFormatUtils.ISO_EXTENDED_FORMAT_PATTERN,
556 false, tz);
557 assertEquals("P0Y0M1DT0H0M0.000S", result);
558 }
559
560 @Test
561 void testFormatPeriodISOMethod() {
562 assertEquals("P0Y0M0DT0H0M0.000S", DurationFormatUtils.formatPeriodISO(0L, 0L));
563 assertEquals("P0Y0M0DT0H0M1.000S", DurationFormatUtils.formatPeriodISO(0L, 1000L));
564 assertEquals("P0Y0M0DT0H1M1.000S", DurationFormatUtils.formatPeriodISO(0L, 61000L));
565 }
566
567 @Test
568 void testFormatPeriodISOStartGreaterEnd() {
569 assertIllegalArgumentException(() -> DurationFormatUtils.formatPeriodISO(5000, 2000));
570 }
571
572 @Test
573 void testFormatPeriodLargeFunnelledValue() {
574 final TimeZone gmt = TimeZones.getTimeZone("GMT");
575
576
577 final long endMillis = 2_175_984_000_000L;
578 assertEquals("2175984000", DurationFormatUtils.formatPeriod(0, endMillis, "s", true, gmt));
579 assertEquals("2175984000000", DurationFormatUtils.formatPeriod(0, endMillis, "S", true, gmt));
580
581 assertEquals(DurationFormatUtils.formatDuration(endMillis, "s"),
582 DurationFormatUtils.formatPeriod(0, endMillis, "s", true, gmt));
583 }
584
585
586
587
588 @Test
589 void testFormatPeriodLeapDayBoundaries() {
590 final TimeZone gmt = TimeZones.getTimeZone("GMT");
591 final Calendar leapDay = Calendar.getInstance(gmt);
592 leapDay.clear();
593 leapDay.set(2000, Calendar.FEBRUARY, 29, 0, 0, 0);
594 final Calendar feb28 = Calendar.getInstance(gmt);
595 feb28.clear();
596 feb28.set(2005, Calendar.FEBRUARY, 28, 0, 0, 0);
597 assertEquals("1826", DurationFormatUtils.formatPeriod(leapDay.getTimeInMillis(), feb28.getTimeInMillis(), "d", true, gmt));
598 final Calendar mar1 = Calendar.getInstance(gmt);
599 mar1.clear();
600 mar1.set(2005, Calendar.MARCH, 1, 0, 0, 0);
601 assertEquals("1827", DurationFormatUtils.formatPeriod(leapDay.getTimeInMillis(), mar1.getTimeInMillis(), "d", true, gmt));
602 final Calendar mar1999 = Calendar.getInstance(gmt);
603 mar1999.clear();
604 mar1999.set(1999, Calendar.MARCH, 1, 0, 0, 0);
605 assertEquals("2191", DurationFormatUtils.formatPeriod(mar1999.getTimeInMillis(), feb28.getTimeInMillis(), "d", true, gmt));
606 }
607
608 @Test
609 void testFormatPeriodLeapDayWithTimeBorrowing() {
610 final TimeZone timeZone = TimeZones.getTimeZone("GMT");
611 final Calendar start = Calendar.getInstance(timeZone);
612 start.clear();
613 start.set(2000, Calendar.FEBRUARY, 29, 23, 59, 59);
614 start.set(Calendar.MILLISECOND, 999);
615 final Calendar end = Calendar.getInstance(timeZone);
616 end.clear();
617 end.set(2005, Calendar.FEBRUARY, 28);
618 assertEquals("1825 0 0 0 001", DurationFormatUtils.formatPeriod(start.getTimeInMillis(), end.getTimeInMillis(), "d H m s S", true, timeZone));
619 }
620
621 @Test
622 void testFormatPeriodLocalDaysAcrossDaylightSaving() {
623 final TimeZone timeZone = TimeZones.getTimeZone("America/New_York");
624 final Calendar start = Calendar.getInstance(timeZone);
625 start.clear();
626 start.set(2024, Calendar.MARCH, 9, 12, 0, 0);
627 final Calendar end = (Calendar) start.clone();
628 end.add(Calendar.DAY_OF_MONTH, 1);
629 assertEquals(Duration.ofHours(23).toMillis(), end.getTimeInMillis() - start.getTimeInMillis());
630 assertEquals("1 0", DurationFormatUtils.formatPeriod(start.getTimeInMillis(), end.getTimeInMillis(), "d H", true, timeZone));
631 start.set(2024, Calendar.NOVEMBER, 2, 12, 0, 0);
632 end.setTimeInMillis(start.getTimeInMillis());
633 end.add(Calendar.DAY_OF_MONTH, 1);
634 assertEquals(Duration.ofHours(25).toMillis(), end.getTimeInMillis() - start.getTimeInMillis());
635 assertEquals("1 0", DurationFormatUtils.formatPeriod(start.getTimeInMillis(), end.getTimeInMillis(), "d H", true, timeZone));
636 }
637
638 @Test
639 void testFormatPeriodLongRangeBounds() {
640
641
642 assertFormatPeriodOneMilli(Long.MAX_VALUE - 1, Long.MAX_VALUE);
643 assertFormatPeriodOneMilli(Long.MIN_VALUE, Long.MIN_VALUE + 1);
644 assertFormatPeriodOneMilli((long) Integer.MIN_VALUE - 1, Integer.MIN_VALUE);
645 }
646
647
648
649
650
651 @Test
652 void testFormatPeriodMaximumSpanCompletesQuickly() {
653 final TimeZone gmt = TimeZones.getTimeZone("GMT");
654
655 assertTimeoutPreemptively(Duration.ofSeconds(5),
656 () -> assertEquals("106751991167", DurationFormatUtils.formatPeriod(0, Long.MAX_VALUE, "d", true, gmt)));
657 }
658
659 @Test
660 void testFormatPeriodWithoutMonths() {
661 final TimeZone timeZone = TimeZone.getTimeZone("UTC");
662 final Calendar start = Calendar.getInstance(timeZone);
663 start.set(2024, Calendar.DECEMBER, 15, 0, 0, 0);
664 start.set(Calendar.MILLISECOND, 0);
665
666 final Calendar end = Calendar.getInstance(timeZone);
667 end.set(2025, Calendar.JANUARY, 15, 0, 0, 0);
668 end.set(Calendar.MILLISECOND, 0);
669
670
671 assertEquals("0 years 31 days", DurationFormatUtils.formatPeriod(start.getTimeInMillis(), end.getTimeInMillis(), "y' years 'd' days'", false, timeZone));
672 assertEquals("0y 31d", DurationFormatUtils.formatPeriod(start.getTimeInMillis(), end.getTimeInMillis(), "y'y 'd'd'", false, timeZone));
673
674
675 start.set(2024, Calendar.JANUARY, 15, 0, 0, 0);
676 end.set(2025, Calendar.JANUARY, 10, 0, 0, 0);
677 assertEquals("0 years 361 days", DurationFormatUtils.formatPeriod(start.getTimeInMillis(), end.getTimeInMillis(), "y' years 'd' days'", false, timeZone));
678
679
680 start.set(2024, Calendar.FEBRUARY, 29, 0, 0, 0);
681 end.set(2025, Calendar.FEBRUARY, 28, 0, 0, 0);
682 assertEquals("0 years 365 days", DurationFormatUtils.formatPeriod(start.getTimeInMillis(), end.getTimeInMillis(), "y' years 'd' days'", false, timeZone));
683 }
684
685 @Test
686 void testFormatPeriodWithoutMonthsAfterLeapDayAnniversary() {
687 final TimeZone timeZone = TimeZone.getTimeZone("UTC");
688 final Calendar start = Calendar.getInstance(timeZone);
689 start.clear();
690
691 start.set(2020, Calendar.FEBRUARY, 29);
692 final Calendar end = Calendar.getInstance(timeZone);
693 end.clear();
694
695 end.set(2021, Calendar.MARCH, 1);
696 assertEquals("1 years 1 days", DurationFormatUtils.formatPeriod(start.getTimeInMillis(), end.getTimeInMillis(), "y' years 'd' days'", false, timeZone));
697 }
698
699
700
701
702 @Test
703 void testFourYears() {
704 final Calendar c = Calendar.getInstance();
705 c.set(2004, 0, 1, 0, 0, 0);
706 for (int i = 0; i < FOUR_YEARS; i++) {
707 bruteForce(c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH), "d", Calendar.DAY_OF_MONTH);
708 c.add(Calendar.DAY_OF_MONTH, 1);
709 }
710 }
711
712
713 @Test
714 void testJiraLang281() {
715 assertEqualDuration("09", new int[] { 2005, 11, 31, 0, 0, 0 }, new int[] { 2006, 9, 6, 0, 0, 0 }, "MM");
716 }
717
718 @ParameterizedTest
719 @MethodSource
720 void testLANG1827(final String label, final String expected, final long durationMillis, final String format) {
721 assertEquals(expected, DurationFormatUtils.formatDuration(durationMillis, format), label);
722 }
723
724 @Test
725 void testLANG1827UnmatchedQuote() {
726 assertIllegalArgumentException(() -> DurationFormatUtils.lexx("'unmatched"));
727 }
728
729 @Test
730 void testLANG815() {
731 final Calendar calendar = Calendar.getInstance();
732 calendar.set(2012, Calendar.JULY, 30, 0, 0, 0);
733 final long startMillis = calendar.getTimeInMillis();
734
735 calendar.set(2012, Calendar.SEPTEMBER, 8);
736 final long endMillis = calendar.getTimeInMillis();
737
738 assertEquals("1 9", DurationFormatUtils.formatPeriod(startMillis, endMillis, "M d"));
739 }
740
741 @Test
742 void testLANG981() {
743 assertIllegalArgumentException(() -> DurationFormatUtils.lexx("'yMdHms''S"));
744 }
745
746 @Test
747 void testLANG982() {
748 assertEquals("61.999", DurationFormatUtils.formatDuration(61999, "s.S"));
749 assertEquals("1 1999", DurationFormatUtils.formatDuration(61999, "m S"));
750 assertEquals("61.999", DurationFormatUtils.formatDuration(61999, "s.SSS"));
751 assertEquals("1 1999", DurationFormatUtils.formatDuration(61999, "m SSS"));
752 assertEquals("61.0999", DurationFormatUtils.formatDuration(61999, "s.SSSS"));
753 assertEquals("1 1999", DurationFormatUtils.formatDuration(61999, "m SSSS"));
754 assertEquals("61.00999", DurationFormatUtils.formatDuration(61999, "s.SSSSS"));
755 assertEquals("1 01999", DurationFormatUtils.formatDuration(61999, "m SSSSS"));
756 }
757
758 @Test
759 void testLANG984() {
760 assertEquals("0", DurationFormatUtils.formatDuration(0, "S"));
761 assertEquals(Integer.toString(Integer.MAX_VALUE), DurationFormatUtils.formatDuration(Integer.MAX_VALUE, "S"));
762 long maxIntPlus = Integer.MAX_VALUE;
763 maxIntPlus++;
764 assertEquals(Long.toString(maxIntPlus), DurationFormatUtils.formatDuration(maxIntPlus, "S"));
765 assertEquals(Long.toString(Long.MAX_VALUE), DurationFormatUtils.formatDuration(Long.MAX_VALUE, "S"));
766 }
767
768 @Test
769 void testLexx() {
770
771 assertArrayEquals(new DurationFormatUtils.Token[] {
772 createTokenWithCount(DurationFormatUtils.y, 1),
773 createTokenWithCount(DurationFormatUtils.M, 1),
774 createTokenWithCount(DurationFormatUtils.d, 1),
775 createTokenWithCount(DurationFormatUtils.H, 1),
776 createTokenWithCount(DurationFormatUtils.m, 1),
777 createTokenWithCount(DurationFormatUtils.s, 1),
778 createTokenWithCount(DurationFormatUtils.S, 1) }, DurationFormatUtils.lexx("yMdHmsS"));
779
780 assertArrayEquals(new DurationFormatUtils.Token[] {
781 createTokenWithCount(DurationFormatUtils.H, 2),
782 createTokenWithCount(new StringBuilder(":"), 1),
783 createTokenWithCount(DurationFormatUtils.m, 2),
784 createTokenWithCount(new StringBuilder(":"), 1),
785 createTokenWithCount(DurationFormatUtils.s, 2),
786 createTokenWithCount(new StringBuilder("."), 1),
787 createTokenWithCount(DurationFormatUtils.S, 3) }, DurationFormatUtils.lexx("HH:mm:ss.SSS"));
788
789 assertArrayEquals(new DurationFormatUtils.Token[] {
790 createTokenWithCount(new StringBuilder("P"), 1),
791 createTokenWithCount(DurationFormatUtils.y, 4),
792 createTokenWithCount(new StringBuilder("Y"), 1),
793 createTokenWithCount(DurationFormatUtils.M, 1),
794 createTokenWithCount(new StringBuilder("M"), 1),
795 createTokenWithCount(DurationFormatUtils.d, 1),
796 createTokenWithCount(new StringBuilder("DT"), 1),
797 createTokenWithCount(DurationFormatUtils.H, 1),
798 createTokenWithCount(new StringBuilder("H"), 1),
799 createTokenWithCount(DurationFormatUtils.m, 1),
800 createTokenWithCount(new StringBuilder("M"), 1),
801 createTokenWithCount(DurationFormatUtils.s, 1),
802 createTokenWithCount(new StringBuilder("."), 1),
803 createTokenWithCount(DurationFormatUtils.S, 3),
804 createTokenWithCount(new StringBuilder("S"), 1) }, DurationFormatUtils.lexx(DurationFormatUtils.ISO_EXTENDED_FORMAT_PATTERN));
805
806 final DurationFormatUtils.Token token = createTokenWithCount(DurationFormatUtils.y, 4);
807 assertEquals(token, token);
808 assertEquals(token.hashCode(), token.hashCode());
809 assertNotEquals(token, new Object(), "Token equal to non-Token class.");
810 final Token token2 = createTokenWithCount("", 1);
811 assertNotEquals(token, token2, "Token equal to Token with wrong value class.");
812 assertNotEquals(token.hashCode(), token2.hashCode());
813 assertNotEquals(token, createTokenWithCount(DurationFormatUtils.y, 1), "Token equal to Token with different count.");
814 final DurationFormatUtils.Token numToken = createTokenWithCount("1", 4);
815 assertEquals(numToken, numToken, "Token with Number value not equal to itself.");
816 }
817
818 @Test
819 void testLiteralPrefixOptionalToken() {
820 assertEquals(DurationFormatUtils.formatDuration(10000L, "s's'"), DurationFormatUtils.formatDuration(10000L, "['['d']']['<'H'>']['{'m'}']s's'"));
821 assertEquals(DurationFormatUtils.formatDuration(10000L, "s's'"), DurationFormatUtils.formatDuration(10000L, "['{'m'}']s's'"));
822 }
823
824
825 @Test
826 void testLowDurations() {
827 for (int hr = 0; hr < 24; hr++) {
828 for (int min = 0; min < 60; min++) {
829 for (int sec = 0; sec < 60; sec++) {
830 assertEqualDuration(hr + ":" + min + ":" + sec, new int[] { 2000, 0, 1, 0, 0, 0, 0 }, new int[] { 2000, 0, 1, hr, min, sec }, "H:m:s");
831 }
832 }
833 }
834 }
835
836 @Test
837 void testMultipleOptionalBlocks() {
838 assertEquals(DurationFormatUtils.formatDuration(Duration.ofHours(1).toMillis(), "'[['H']]'"),
839 DurationFormatUtils.formatDuration(Duration.ofHours(1).toMillis(), "['{'d'}']['[['H']]']"));
840 assertEquals(DurationFormatUtils.formatDuration(Duration.ofDays(1).toMillis(), "['{'d'}']"),
841 DurationFormatUtils.formatDuration(Duration.ofDays(1).toMillis(), "['{'d'}']['['H']']"));
842 }
843
844 @Test
845 void testOptionalLiteralSpecialCharacters() {
846 assertEquals(
847 DurationFormatUtils.formatDuration(10000L, "s's'"),
848 DurationFormatUtils.formatDuration(10000L, "['['m']']s's'"));
849 }
850
851 @Test
852 void testOptionalToken() {
853
854 assertEquals(
855 DurationFormatUtils.formatDuration(915361000L, "d'd'H'h'm'm's's'"),
856 DurationFormatUtils.formatDuration(915361000L, "[d'd'H'h'm'm']s's'"));
857 assertEquals(
858 DurationFormatUtils.formatDuration(9153610L, "H'h'm'm's's'"),
859 DurationFormatUtils.formatDuration(9153610L, "[d'd'H'h'm'm']s's'"));
860 assertEquals(
861 DurationFormatUtils.formatDuration(915361L, "m'm's's'"),
862 DurationFormatUtils.formatDuration(915361L, "[d'd'H'h'm'm']s's'"));
863 assertEquals(
864 DurationFormatUtils.formatDuration(9153L, "s's'"),
865 DurationFormatUtils.formatDuration(9153L, "[d'd'H'h'm'm']s's'"));
866 assertEquals(
867 DurationFormatUtils.formatDuration(9153L, "s's'"),
868 DurationFormatUtils.formatDuration(9153L, "[d'd'H'h'm'm']s's'"));
869 assertEquals(
870 DurationFormatUtils.formatPeriod(9153610L, 915361000L, "d'd'H'h'm'm's's'"),
871 DurationFormatUtils.formatPeriod(9153610L, 915361000L, "[d'd'H'h'm'm']s's'"));
872 assertEquals(
873 DurationFormatUtils.formatPeriod(915361L, 9153610L, "H'h'm'm's's'"),
874 DurationFormatUtils.formatPeriod(915361L, 9153610L, "[d'd'H'h'm'm']s's'"));
875 assertEquals(
876 DurationFormatUtils.formatPeriod(9153L, 915361L, "m'm's's'"),
877 DurationFormatUtils.formatPeriod(9153L, 915361L, "[d'd'H'h'm'm']s's'"));
878 assertEquals(
879 DurationFormatUtils.formatPeriod(0L, 9153L, "s's'"),
880 DurationFormatUtils.formatPeriod(0L, 9153L, "[d'd'H'h'm'm']s's'"));
881
882 assertEquals("2h32m33s610ms", DurationFormatUtils.formatDuration(9153610L, "[d'd'H'h'm'm's's']S'ms'"));
883 assertEquals("15m15s361ms", DurationFormatUtils.formatDuration(915361L, "[d'd'H'h'm'm's's']S'ms'"));
884 assertEquals("9s153ms", DurationFormatUtils.formatDuration(9153L, "[d'd'H'h'm'm's's']S'ms'"));
885 assertEquals("915ms", DurationFormatUtils.formatDuration(915L, "[d'd'H'h'm'm's's']S'ms'"));
886
887 assertEquals(
888 DurationFormatUtils.formatPeriod(915361L, 9153610L, "H'h''h2'm'm's's'"),
889 DurationFormatUtils.formatPeriod(915361L, 9153610L, "[d'd''d2'H'h''h2'm'm']s's'"));
890 }
891
892 @Test
893 void testRepeatedTokenSeparatedByLiteral() {
894 final long fiveHours = Duration.ofHours(5).toMillis();
895
896 assertEquals("5x5", DurationFormatUtils.formatDuration(fiveHours, "HxH", false));
897 assertEquals("5x5", DurationFormatUtils.formatDuration(fiveHours, "H'x'H", false));
898
899 assertEquals("5x5", DurationFormatUtils.formatDuration(fiveHours, "HxH", true));
900
901 assertEquals("5x5", DurationFormatUtils.formatDuration(fiveHours, "H[x]H", false));
902 assertEquals("2-2", DurationFormatUtils.formatDuration(Duration.ofDays(2).toMillis(), "d-d", false));
903
904 assertEquals("05", DurationFormatUtils.formatDuration(fiveHours, "HH", true));
905 }
906
907 @Test
908 void testUnmatchedOptionalTokens() {
909 assertIllegalArgumentException(() -> DurationFormatUtils.formatDuration(1, "[s"));
910 assertIllegalArgumentException(() -> DurationFormatUtils.formatDuration(1, "[[s"));
911 assertIllegalArgumentException(() -> DurationFormatUtils.formatDuration(1, "[s]]"));
912 }
913
914 }