View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3.time;
18  
19  import static org.apache.commons.lang3.LangAssertions.assertIllegalArgumentException;
20  import static org.apache.commons.lang3.LangAssertions.assertNullPointerException;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertFalse;
23  import static org.junit.jupiter.api.Assertions.assertNotNull;
24  import static org.junit.jupiter.api.Assertions.assertNotSame;
25  import static org.junit.jupiter.api.Assertions.assertThrows;
26  import static org.junit.jupiter.api.Assertions.assertTrue;
27  
28  import java.lang.reflect.Constructor;
29  import java.lang.reflect.Modifier;
30  import java.text.DateFormat;
31  import java.text.ParseException;
32  import java.text.SimpleDateFormat;
33  import java.time.Instant;
34  import java.time.LocalDateTime;
35  import java.time.OffsetDateTime;
36  import java.time.ZoneId;
37  import java.time.ZoneOffset;
38  import java.time.ZonedDateTime;
39  import java.time.temporal.ChronoUnit;
40  import java.util.Calendar;
41  import java.util.Date;
42  import java.util.GregorianCalendar;
43  import java.util.Iterator;
44  import java.util.Locale;
45  import java.util.NoSuchElementException;
46  import java.util.TimeZone;
47  import java.util.stream.Stream;
48  
49  import org.apache.commons.lang3.AbstractLangTest;
50  import org.junit.jupiter.api.AfterEach;
51  import org.junit.jupiter.api.BeforeAll;
52  import org.junit.jupiter.api.BeforeEach;
53  import org.junit.jupiter.api.Test;
54  import org.junit.jupiter.params.ParameterizedTest;
55  import org.junit.jupiter.params.provider.Arguments;
56  import org.junit.jupiter.params.provider.MethodSource;
57  import org.junitpioneer.jupiter.DefaultLocale;
58  import org.junitpioneer.jupiter.DefaultTimeZone;
59  import org.junitpioneer.jupiter.ReadsDefaultLocale;
60  import org.junitpioneer.jupiter.WritesDefaultLocale;
61  
62  /**
63   * Tests {@link DateUtils}.
64   */
65  @ReadsDefaultLocale
66  @WritesDefaultLocale
67  class DateUtilsTest extends AbstractLangTest {
68  
69      private static final Date MAX_DATE = new Date(Long.MAX_VALUE);
70      private static final TimeZone TIME_ZONE_NY = TimeZones.getTimeZone("America/New_York");
71      private static final TimeZone TIME_ZONE_DEFAULT = TimeZone.getDefault();
72      private static final TimeZone TIME_ZONE_MET = TimeZones.getTimeZone("MET");
73      private static Date BASE_DATE;
74  
75      /**
76       * Used to check that Calendar objects are close enough
77       * delta is in milliseconds
78       */
79      private static void assertCalendarsEquals(final String message, final Calendar cal1, final Calendar cal2, final long delta) {
80          assertFalse(Math.abs(cal1.getTime().getTime() - cal2.getTime().getTime()) > delta,
81                  message + " expected " + cal1.getTime() + " but got " + cal2.getTime());
82      }
83  
84      /**
85       * This checks that this is a 7 element iterator of Calendar objects
86       * that are dates (no time), and exactly 1 day spaced after each other.
87       */
88      private static void assertWeekIterator(final Iterator<?> it, final Calendar start) {
89          final Calendar end = (Calendar) start.clone();
90          end.add(Calendar.DATE, 6);
91  
92          assertWeekIterator(it, start, end);
93      }
94  
95      /**
96       * This checks that this is a 7 divisible iterator of Calendar objects
97       * that are dates (no time), and exactly 1 day spaced after each other
98       * (in addition to the proper start and stop dates)
99       */
100     private static void assertWeekIterator(final Iterator<?> it, final Calendar start, final Calendar end) {
101         Calendar cal = (Calendar) it.next();
102         assertCalendarsEquals("", start, cal, 0);
103         Calendar last;
104         int count = 1;
105         while (it.hasNext()) {
106             //Check this is just a date (no time component)
107             assertCalendarsEquals("", cal, DateUtils.truncate(cal, Calendar.DATE), 0);
108 
109             last = cal;
110             cal = (Calendar) it.next();
111             count++;
112 
113             //Check that this is one day more than the last date
114             last.add(Calendar.DATE, 1);
115             assertCalendarsEquals("", last, cal, 0);
116         }
117 
118         assertFalse(count % 7 != 0, "There were " + count + " days in this iterator");
119         assertCalendarsEquals("", end, cal, 0);
120     }
121 
122     /**
123      * Convenience method for when working with Date objects
124      */
125     private static void assertWeekIterator(final Iterator<?> it, final Date start, final Date end) {
126         final Calendar calStart = Calendar.getInstance();
127         calStart.setTime(start);
128         final Calendar calEnd = Calendar.getInstance();
129         calEnd.setTime(end);
130 
131         assertWeekIterator(it, calStart, calEnd);
132     }
133 
134     @BeforeAll
135     public static void classSetup() {
136         final GregorianCalendar cal = new GregorianCalendar(2000, 6, 5, 4, 3, 2);
137         cal.set(Calendar.MILLISECOND, 1);
138         BASE_DATE = cal.getTime();
139     }
140 
141     private static Stream<Arguments> testToLocalDateTimeTimeZone() {
142         // @formatter:off
143         return Stream.of(
144                 // [1]
145                 Arguments.of(
146                         // -292275055-05-16T16:47:04.192
147                         LocalDateTime.of(-292275055, 5, 16, 16, 47, 04, 192_000_000),
148                         new Date(Long.MIN_VALUE),
149                         TimeZones.GMT
150                 ),
151                 // [2]
152                 Arguments.of(
153                         // +292278994-08-17T07:12:55.807
154                         LocalDateTime.of(292278994, 8, 17, 7, 12, 55, 807_000_000),
155                         MAX_DATE,
156                         TimeZones.GMT
157                 ),
158                 // [3]
159                 Arguments.of(
160                         LocalDateTime.ofInstant(Instant.EPOCH, TimeZones.GMT.toZoneId()),
161                         Date.from(LocalDateTime.of(1970, 1, 1, 0, 0).atOffset(ZoneOffset.UTC).toInstant()),
162                         TimeZones.GMT
163                 ),
164                 // [4]
165                 Arguments.of(
166                         LocalDateTime.ofInstant(Instant.EPOCH.minus(1, ChronoUnit.DAYS), TimeZones.GMT.toZoneId()),
167                         Date.from(LocalDateTime.of(1969, 12, 31, 0, 0).atOffset(ZoneOffset.UTC).toInstant()),
168                         TimeZones.GMT
169                 ),
170                 // [5]
171                 Arguments.of(
172                         LocalDateTime.ofInstant(
173                                 java.sql.Timestamp.valueOf("2000-01-01 12:30:45").toInstant(),
174                                 TIME_ZONE_NY.toZoneId()
175                         ),
176                         java.sql.Timestamp.valueOf("2000-01-01 12:30:45"),
177                         TIME_ZONE_NY
178                 ),
179                 // [6]
180                 Arguments.of(
181                         LocalDateTime.ofInstant(
182                                 java.sql.Timestamp.valueOf("2023-03-12 02:30:00").toInstant(),
183                                 TIME_ZONE_NY.toZoneId()
184                         ),
185                         java.sql.Timestamp.valueOf("2023-03-12 02:30:00"),
186                         TIME_ZONE_NY
187                 ),
188                 // [7]
189                 Arguments.of(
190                         LocalDateTime.ofInstant(
191                                 java.sql.Timestamp.valueOf("2023-03-12 02:30:00").toInstant(),
192                                 TimeZone.getDefault().toZoneId()
193                         ),
194                         java.sql.Timestamp.valueOf("2023-03-12 02:30:00"),
195                         null
196                 ),
197                 // [8]
198                 Arguments.of(
199                         LocalDateTime.of(2022, 12, 31, 19, 0),
200                         Date.from(LocalDateTime.of(2023, 1, 1, 0, 0).atOffset(ZoneOffset.UTC).toInstant()),
201                         TIME_ZONE_NY
202                 ),
203                 // [9]
204                 Arguments.of(
205                         LocalDateTime.of(2023, 3, 12, 3, 0),
206                         Date.from(LocalDateTime.of(2023, 3, 12, 7, 0).atOffset(ZoneOffset.UTC).toInstant()),
207                         TIME_ZONE_NY
208                 ),
209                 // [10]
210                 Arguments.of(
211                         LocalDateTime.of(2023, 1, 1, 14, 0),
212                         Date.from(LocalDateTime.of(2023, 1, 1, 0, 0).atOffset(ZoneOffset.UTC).toInstant()),
213                         TimeZones.getTimeZone("Pacific/Kiritimati")
214                 )
215         );
216         // @formatter:on
217     }
218 
219     private DateFormat dateParser;
220     private DateFormat dateTimeParser;
221     private Date dateAmPm1;
222     private Date dateAmPm2;
223     private Date dateAmPm3;
224     private Date dateAmPm4;
225     private Date date0;
226     private Date date1;
227     private Date date2;
228     private Date date3;
229     private Date date4;
230     private Date date5;
231     private Date date6;
232     private Date date7;
233     private Date date8;
234     private Date date9;
235     private Calendar calAmPm1;
236     private Calendar calAmPm2;
237     private Calendar calAmPm3;
238     private Calendar calAmPm4;
239     private Calendar cal1;
240     private Calendar cal2;
241     private Calendar cal3;
242     private Calendar cal4;
243     private Calendar cal5;
244     private Calendar cal6;
245     private Calendar cal7;
246     private Calendar cal8;
247 
248     @AfterEach
249     public void afterEachResetTimeZones() {
250         TimeZone.setDefault(TIME_ZONE_DEFAULT);
251         dateTimeParser.setTimeZone(TIME_ZONE_DEFAULT);
252     }
253     private void assertDate(final Date date, final int year, final int month, final int day, final int hour, final int min, final int sec, final int mil) {
254         final GregorianCalendar cal = new GregorianCalendar();
255         cal.setTime(date);
256         assertEquals(year, cal.get(Calendar.YEAR));
257         assertEquals(month, cal.get(Calendar.MONTH));
258         assertEquals(day, cal.get(Calendar.DAY_OF_MONTH));
259         assertEquals(hour, cal.get(Calendar.HOUR_OF_DAY));
260         assertEquals(min, cal.get(Calendar.MINUTE));
261         assertEquals(sec, cal.get(Calendar.SECOND));
262         assertEquals(mil, cal.get(Calendar.MILLISECOND));
263     }
264     @BeforeEach
265     public void setUp() throws Exception {
266         dateParser = new SimpleDateFormat("MMM dd, yyyy", Locale.ENGLISH);
267         dateTimeParser = new SimpleDateFormat("MMM dd, yyyy H:mm:ss.SSS", Locale.ENGLISH);
268         dateAmPm1 = dateTimeParser.parse("February 3, 2002 01:10:00.000");
269         dateAmPm2 = dateTimeParser.parse("February 3, 2002 11:10:00.000");
270         dateAmPm3 = dateTimeParser.parse("February 3, 2002 13:10:00.000");
271         dateAmPm4 = dateTimeParser.parse("February 3, 2002 19:10:00.000");
272         date0 = dateTimeParser.parse("February 3, 2002 12:34:56.789");
273         date1 = dateTimeParser.parse("February 12, 2002 12:34:56.789");
274         date2 = dateTimeParser.parse("November 18, 2001 1:23:11.321");
275         try {
276             TimeZone.setDefault(TIME_ZONE_MET);
277             dateTimeParser.setTimeZone(TIME_ZONE_MET);
278             date3 = dateTimeParser.parse("March 30, 2003 05:30:45.000");
279             date4 = dateTimeParser.parse("March 30, 2003 01:10:00.000");
280             date5 = dateTimeParser.parse("March 30, 2003 01:40:00.000");
281             date6 = dateTimeParser.parse("March 30, 2003 02:10:00.000");
282             date7 = dateTimeParser.parse("March 30, 2003 02:40:00.000");
283             date8 = dateTimeParser.parse("October 26, 2003 05:30:45.000");
284         } finally {
285             dateTimeParser.setTimeZone(TIME_ZONE_DEFAULT);
286             TimeZone.setDefault(TIME_ZONE_DEFAULT);
287         }
288         date9 = dateTimeParser.parse("March 30, 2003 01:10:00.000");
289         calAmPm1 = Calendar.getInstance();
290         calAmPm1.setTime(dateAmPm1);
291         calAmPm2 = Calendar.getInstance();
292         calAmPm2.setTime(dateAmPm2);
293         calAmPm3 = Calendar.getInstance();
294         calAmPm3.setTime(dateAmPm3);
295         calAmPm4 = Calendar.getInstance();
296         calAmPm4.setTime(dateAmPm4);
297         cal1 = Calendar.getInstance();
298         cal1.setTime(date1);
299         cal2 = Calendar.getInstance();
300         cal2.setTime(date2);
301         try {
302             TimeZone.setDefault(TIME_ZONE_MET);
303             cal3 = Calendar.getInstance();
304             cal3.setTime(date3);
305             cal4 = Calendar.getInstance();
306             cal4.setTime(date4);
307             cal5 = Calendar.getInstance();
308             cal5.setTime(date5);
309             cal6 = Calendar.getInstance();
310             cal6.setTime(date6);
311             cal7 = Calendar.getInstance();
312             cal7.setTime(date7);
313             cal8 = Calendar.getInstance();
314             cal8.setTime(date8);
315         } finally {
316             TimeZone.setDefault(TIME_ZONE_DEFAULT);
317         }
318     }
319 
320     @Test
321     void testAddDays() throws Exception {
322         Date result = DateUtils.addDays(BASE_DATE, 0);
323         assertNotSame(BASE_DATE, result);
324         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
325         assertDate(result, 2000, 6, 5, 4, 3, 2, 1);
326 
327         result = DateUtils.addDays(BASE_DATE, 1);
328         assertNotSame(BASE_DATE, result);
329         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
330         assertDate(result, 2000, 6, 6, 4, 3, 2, 1);
331 
332         result = DateUtils.addDays(BASE_DATE, -1);
333         assertNotSame(BASE_DATE, result);
334         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
335         assertDate(result, 2000, 6, 4, 4, 3, 2, 1);
336 
337         assertNullPointerException(() -> DateUtils.addDays(null, 0));
338     }
339 
340     @Test
341     void testAddHours() throws Exception {
342         Date result = DateUtils.addHours(BASE_DATE, 0);
343         assertNotSame(BASE_DATE, result);
344         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
345         assertDate(result, 2000, 6, 5, 4, 3, 2, 1);
346 
347         result = DateUtils.addHours(BASE_DATE, 1);
348         assertNotSame(BASE_DATE, result);
349         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
350         assertDate(result, 2000, 6, 5, 5, 3, 2, 1);
351 
352         result = DateUtils.addHours(BASE_DATE, -1);
353         assertNotSame(BASE_DATE, result);
354         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
355         assertDate(result, 2000, 6, 5, 3, 3, 2, 1);
356 
357         assertNullPointerException(() -> DateUtils.addHours(null, 0));
358     }
359 
360     @Test
361     void testAddMilliseconds() throws Exception {
362         Date result = DateUtils.addMilliseconds(BASE_DATE, 0);
363         assertNotSame(BASE_DATE, result);
364         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
365         assertDate(result, 2000, 6, 5, 4, 3, 2, 1);
366 
367         result = DateUtils.addMilliseconds(BASE_DATE, 1);
368         assertNotSame(BASE_DATE, result);
369         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
370         assertDate(result, 2000, 6, 5, 4, 3, 2, 2);
371 
372         result = DateUtils.addMilliseconds(BASE_DATE, -1);
373         assertNotSame(BASE_DATE, result);
374         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
375         assertDate(result, 2000, 6, 5, 4, 3, 2, 0);
376 
377         assertNullPointerException(() -> DateUtils.addMilliseconds(null, 0));
378     }
379 
380     @Test
381     void testAddMinutes() throws Exception {
382         Date result = DateUtils.addMinutes(BASE_DATE, 0);
383         assertNotSame(BASE_DATE, result);
384         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
385         assertDate(result, 2000, 6, 5, 4, 3, 2, 1);
386 
387         result = DateUtils.addMinutes(BASE_DATE, 1);
388         assertNotSame(BASE_DATE, result);
389         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
390         assertDate(result, 2000, 6, 5, 4, 4, 2, 1);
391 
392         result = DateUtils.addMinutes(BASE_DATE, -1);
393         assertNotSame(BASE_DATE, result);
394         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
395         assertDate(result, 2000, 6, 5, 4, 2, 2, 1);
396 
397         assertNullPointerException(() -> DateUtils.addMinutes(null, 0));
398     }
399 
400     @Test
401     void testAddMonths() throws Exception {
402         Date result = DateUtils.addMonths(BASE_DATE, 0);
403         assertNotSame(BASE_DATE, result);
404         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
405         assertDate(result, 2000, 6, 5, 4, 3, 2, 1);
406 
407         result = DateUtils.addMonths(BASE_DATE, 1);
408         assertNotSame(BASE_DATE, result);
409         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
410         assertDate(result, 2000, 7, 5, 4, 3, 2, 1);
411 
412         result = DateUtils.addMonths(BASE_DATE, -1);
413         assertNotSame(BASE_DATE, result);
414         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
415         assertDate(result, 2000, 5, 5, 4, 3, 2, 1);
416 
417         assertNullPointerException(() -> DateUtils.addMonths(null, 0));
418     }
419 
420     @Test
421     void testAddSeconds() throws Exception {
422         Date result = DateUtils.addSeconds(BASE_DATE, 0);
423         assertNotSame(BASE_DATE, result);
424         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
425         assertDate(result, 2000, 6, 5, 4, 3, 2, 1);
426 
427         result = DateUtils.addSeconds(BASE_DATE, 1);
428         assertNotSame(BASE_DATE, result);
429         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
430         assertDate(result, 2000, 6, 5, 4, 3, 3, 1);
431 
432         result = DateUtils.addSeconds(BASE_DATE, -1);
433         assertNotSame(BASE_DATE, result);
434         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
435         assertDate(result, 2000, 6, 5, 4, 3, 1, 1);
436 
437         assertNullPointerException(() -> DateUtils.addSeconds(null, 0));
438     }
439 
440     @Test
441     void testAddWeeks() throws Exception {
442         Date result = DateUtils.addWeeks(BASE_DATE, 0);
443         assertNotSame(BASE_DATE, result);
444         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
445         assertDate(result, 2000, 6, 5, 4, 3, 2, 1);
446 
447         result = DateUtils.addWeeks(BASE_DATE, 1);
448         assertNotSame(BASE_DATE, result);
449         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
450         assertDate(result, 2000, 6, 12, 4, 3, 2, 1);
451 
452         result = DateUtils.addWeeks(BASE_DATE, -1);
453         assertNotSame(BASE_DATE, result);
454         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);      // july
455         assertDate(result, 2000, 5, 28, 4, 3, 2, 1);   // june
456 
457         assertNullPointerException(() -> DateUtils.addMonths(null, 0));
458     }
459 
460     @Test
461     void testAddYears() throws Exception {
462         Date result = DateUtils.addYears(BASE_DATE, 0);
463         assertNotSame(BASE_DATE, result);
464         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
465         assertDate(result, 2000, 6, 5, 4, 3, 2, 1);
466 
467         result = DateUtils.addYears(BASE_DATE, 1);
468         assertNotSame(BASE_DATE, result);
469         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
470         assertDate(result, 2001, 6, 5, 4, 3, 2, 1);
471 
472         result = DateUtils.addYears(BASE_DATE, -1);
473         assertNotSame(BASE_DATE, result);
474         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
475         assertDate(result, 1999, 6, 5, 4, 3, 2, 1);
476 
477         assertNullPointerException(() -> DateUtils.addYears(null, 0));
478     }
479 
480     /**
481      * Tests various values with the ceiling method
482      *
483      * @throws Exception Thrown if an operation in the test fails.
484      */
485     @Test
486     void testCeiling() throws Exception {
487         // test javadoc
488         assertEquals(dateTimeParser.parse("March 28, 2002 14:00:00.000"),
489                 DateUtils.ceiling(
490                             dateTimeParser.parse("March 28, 2002 13:45:01.231"),
491                         Calendar.HOUR),
492                 "ceiling javadoc-1 failed");
493         assertEquals(dateTimeParser.parse("April 1, 2002 00:00:00.000"),
494                 DateUtils.ceiling(
495                             dateTimeParser.parse("March 28, 2002 13:45:01.231"),
496                         Calendar.MONTH),
497                 "ceiling javadoc-2 failed");
498 
499         // tests public static Date ceiling(Date date, int field)
500         assertEquals(dateParser.parse("January 1, 2003"),
501                 DateUtils.ceiling(date1, Calendar.YEAR),
502                 "ceiling year-1 failed");
503         assertEquals(dateParser.parse("January 1, 2002"),
504                 DateUtils.ceiling(date2, Calendar.YEAR),
505                 "ceiling year-2 failed");
506         assertEquals(dateParser.parse("March 1, 2002"),
507                 DateUtils.ceiling(date1, Calendar.MONTH),
508                 "ceiling month-1 failed");
509         assertEquals(dateParser.parse("December 1, 2001"),
510                 DateUtils.ceiling(date2, Calendar.MONTH),
511                 "ceiling month-2 failed");
512         assertEquals(dateParser.parse("February 16, 2002"),
513                 DateUtils.ceiling(date1, DateUtils.SEMI_MONTH),
514                 "ceiling semimonth-1 failed");
515         assertEquals(dateParser.parse("December 1, 2001"),
516                 DateUtils.ceiling(date2, DateUtils.SEMI_MONTH),
517                 "ceiling semimonth-2 failed");
518         assertEquals(dateParser.parse("February 13, 2002"),
519                 DateUtils.ceiling(date1, Calendar.DATE),
520                 "ceiling date-1 failed");
521         assertEquals(dateParser.parse("November 19, 2001"),
522                 DateUtils.ceiling(date2, Calendar.DATE),
523                 "ceiling date-2 failed");
524         assertEquals(dateTimeParser.parse("February 12, 2002 13:00:00.000"),
525                 DateUtils.ceiling(date1, Calendar.HOUR),
526                 "ceiling hour-1 failed");
527         assertEquals(dateTimeParser.parse("November 18, 2001 2:00:00.000"),
528                 DateUtils.ceiling(date2, Calendar.HOUR),
529                 "ceiling hour-2 failed");
530         assertEquals(dateTimeParser.parse("February 12, 2002 12:35:00.000"),
531                 DateUtils.ceiling(date1, Calendar.MINUTE),
532                 "ceiling minute-1 failed");
533         assertEquals(dateTimeParser.parse("November 18, 2001 1:24:00.000"),
534                 DateUtils.ceiling(date2, Calendar.MINUTE),
535                 "ceiling minute-2 failed");
536         // Edge cases (LANG-771)
537         assertEquals(dateTimeParser.parse("March 30, 2003 01:10:00.000"),
538                 DateUtils.ceiling(date9, Calendar.MINUTE),
539                 "ceiling minute boundary failed");
540         final Date epoch = new Date(0);
541         assertEquals(epoch,
542                 DateUtils.ceiling(epoch, Calendar.MINUTE),
543                 "ceiling minute epoch failed");
544         final Date negative = new Date(-1);
545         assertEquals(new Date(0),
546                 DateUtils.ceiling(negative, Calendar.MINUTE),
547                 "ceiling minute negative failed");
548         assertThrows(ArithmeticException.class,
549                 () -> DateUtils.ceiling(new Date(Long.MIN_VALUE), Calendar.MINUTE),
550                 "ceiling minute Long.MIN_VALUE failed");
551         assertThrows(ArithmeticException.class,
552                 () -> DateUtils.ceiling(new Date(Long.MAX_VALUE), Calendar.MINUTE),
553                 "ceiling minute Long.MAX_VALUE failed");
554         assertEquals(dateTimeParser.parse("February 12, 2002 12:34:57.000"),
555                 DateUtils.ceiling(date1, Calendar.SECOND),
556                 "ceiling second-1 failed");
557         assertEquals(dateTimeParser.parse("November 18, 2001 1:23:12.000"),
558                 DateUtils.ceiling(date2, Calendar.SECOND),
559                 "ceiling second-2 failed");
560         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"),
561                 DateUtils.ceiling(dateAmPm1, Calendar.AM_PM),
562                 "ceiling ampm-1 failed");
563         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"),
564                 DateUtils.ceiling(dateAmPm2, Calendar.AM_PM),
565                 "ceiling ampm-2 failed");
566         assertEquals(dateTimeParser.parse("February 4, 2002 00:00:00.000"),
567                 DateUtils.ceiling(dateAmPm3, Calendar.AM_PM),
568                 "ceiling ampm-3 failed");
569         assertEquals(dateTimeParser.parse("February 4, 2002 00:00:00.000"),
570                 DateUtils.ceiling(dateAmPm4, Calendar.AM_PM),
571                 "ceiling ampm-4 failed");
572 
573      // tests public static Date ceiling(Object date, int field)
574         assertEquals(dateParser.parse("January 1, 2003"),
575                 DateUtils.ceiling((Object) date1, Calendar.YEAR),
576                 "ceiling year-1 failed");
577         assertEquals(dateParser.parse("January 1, 2002"),
578                 DateUtils.ceiling((Object) date2, Calendar.YEAR),
579                 "ceiling year-2 failed");
580         assertEquals(dateParser.parse("March 1, 2002"),
581                 DateUtils.ceiling((Object) date1, Calendar.MONTH),
582                 "ceiling month-1 failed");
583         assertEquals(dateParser.parse("December 1, 2001"),
584                 DateUtils.ceiling((Object) date2, Calendar.MONTH),
585                 "ceiling month-2 failed");
586         assertEquals(dateParser.parse("February 16, 2002"),
587                 DateUtils.ceiling((Object) date1, DateUtils.SEMI_MONTH),
588                 "ceiling semimonth-1 failed");
589         assertEquals(dateParser.parse("December 1, 2001"),
590                 DateUtils.ceiling((Object) date2, DateUtils.SEMI_MONTH),
591                 "ceiling semimonth-2 failed");
592         assertEquals(dateParser.parse("February 13, 2002"),
593                 DateUtils.ceiling((Object) date1, Calendar.DATE),
594                 "ceiling date-1 failed");
595         assertEquals(dateParser.parse("November 19, 2001"),
596                 DateUtils.ceiling((Object) date2, Calendar.DATE),
597                 "ceiling date-2 failed");
598         assertEquals(dateTimeParser.parse("February 12, 2002 13:00:00.000"),
599                 DateUtils.ceiling((Object) date1, Calendar.HOUR),
600                 "ceiling hour-1 failed");
601         assertEquals(dateTimeParser.parse("November 18, 2001 2:00:00.000"),
602                 DateUtils.ceiling((Object) date2, Calendar.HOUR),
603                 "ceiling hour-2 failed");
604         assertEquals(dateTimeParser.parse("February 12, 2002 12:35:00.000"),
605                 DateUtils.ceiling((Object) date1, Calendar.MINUTE),
606                 "ceiling minute-1 failed");
607         assertEquals(dateTimeParser.parse("November 18, 2001 1:24:00.000"),
608                 DateUtils.ceiling((Object) date2, Calendar.MINUTE),
609                 "ceiling minute-2 failed");
610         assertEquals(dateTimeParser.parse("February 12, 2002 12:34:57.000"),
611                 DateUtils.ceiling((Object) date1, Calendar.SECOND),
612                 "ceiling second-1 failed");
613         assertEquals(dateTimeParser.parse("November 18, 2001 1:23:12.000"),
614                 DateUtils.ceiling((Object) date2, Calendar.SECOND),
615                 "ceiling second-2 failed");
616         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"),
617                 DateUtils.ceiling((Object) dateAmPm1, Calendar.AM_PM),
618                 "ceiling ampm-1 failed");
619         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"),
620                 DateUtils.ceiling((Object) dateAmPm2, Calendar.AM_PM),
621                 "ceiling ampm-2 failed");
622         assertEquals(dateTimeParser.parse("February 4, 2002 00:00:00.000"),
623                 DateUtils.ceiling((Object) dateAmPm3, Calendar.AM_PM),
624                 "ceiling ampm-3 failed");
625         assertEquals(dateTimeParser.parse("February 4, 2002 00:00:00.000"),
626                 DateUtils.ceiling((Object) dateAmPm4, Calendar.AM_PM),
627                 "ceiling ampm-4 failed");
628 
629         assertEquals(dateTimeParser.parse("February 12, 2002 12:34:57.000"),
630                 DateUtils.ceiling((Object) cal1, Calendar.SECOND),
631                 "ceiling calendar second-1 failed");
632         assertEquals(dateTimeParser.parse("November 18, 2001 1:23:12.000"),
633                 DateUtils.ceiling((Object) cal2, Calendar.SECOND),
634                 "ceiling calendar second-2 failed");
635 
636         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"),
637                 DateUtils.ceiling((Object) calAmPm1, Calendar.AM_PM),
638                 "ceiling ampm-1 failed");
639         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"),
640                 DateUtils.ceiling((Object) calAmPm2, Calendar.AM_PM),
641                 "ceiling ampm-2 failed");
642         assertEquals(dateTimeParser.parse("February 4, 2002 00:00:00.000"),
643                 DateUtils.ceiling((Object) calAmPm3, Calendar.AM_PM),
644                 "ceiling ampm-3 failed");
645         assertEquals(dateTimeParser.parse("February 4, 2002 00:00:00.000"),
646                 DateUtils.ceiling((Object) calAmPm4, Calendar.AM_PM),
647                 "ceiling ampm-4 failed");
648 
649         assertNullPointerException(() -> DateUtils.ceiling((Date) null, Calendar.SECOND));
650         assertNullPointerException(() -> DateUtils.ceiling((Calendar) null, Calendar.SECOND));
651         assertNullPointerException(() -> DateUtils.ceiling((Object) null, Calendar.SECOND));
652         assertThrows(ClassCastException.class, () -> DateUtils.ceiling("", Calendar.SECOND));
653         assertIllegalArgumentException(() -> DateUtils.ceiling(date1, -9999));
654 
655     }
656 
657     /**
658      * Tests various values with the ceiling method
659      *
660      * @throws Exception Thrown if an operation in the test fails.
661      */
662     @Test
663     void testCeiling_Bugzilla_31395() throws Exception {
664         // Bug 31395, large dates
665         final Date endOfTime = MAX_DATE; // fyi: Sun Aug 17 07:12:55 CET 292278994 -- 807 millis
666         final GregorianCalendar endCal = new GregorianCalendar();
667         endCal.setTime(endOfTime);
668         assertThrows(ArithmeticException.class, () -> DateUtils.ceiling(endCal, Calendar.DATE));
669         endCal.set(Calendar.YEAR, 280000001);
670         assertThrows(ArithmeticException.class, () -> DateUtils.ceiling(endCal, Calendar.DATE));
671         endCal.set(Calendar.YEAR, 280000000);
672         final Calendar cal = DateUtils.ceiling(endCal, Calendar.DATE);
673         assertEquals(0, cal.get(Calendar.HOUR));
674     }
675 
676     /**
677      * Tests various values with the ceiling method
678      *
679      * @throws Exception Thrown if an operation in the test fails.
680      */
681     @Test
682     void testCeiling_MET() throws Exception {
683         // Fix for https://issues.apache.org/bugzilla/show_bug.cgi?id=25560
684         // Test ceiling across the beginning of daylight saving time
685         TimeZone.setDefault(TIME_ZONE_MET);
686         dateTimeParser.setTimeZone(TIME_ZONE_MET);
687 
688         assertEquals(dateTimeParser.parse("March 31, 2003 00:00:00.000"), DateUtils.ceiling(date4, Calendar.DATE), "ceiling MET date across DST change-over");
689         assertEquals(dateTimeParser.parse("March 31, 2003 00:00:00.000"), DateUtils.ceiling((Object) cal4, Calendar.DATE),
690                 "ceiling MET date across DST change-over");
691         assertEquals(dateTimeParser.parse("March 31, 2003 00:00:00.000"), DateUtils.ceiling(date5, Calendar.DATE), "ceiling MET date across DST change-over");
692         assertEquals(dateTimeParser.parse("March 31, 2003 00:00:00.000"), DateUtils.ceiling((Object) cal5, Calendar.DATE),
693                 "ceiling MET date across DST change-over");
694         assertEquals(dateTimeParser.parse("March 31, 2003 00:00:00.000"), DateUtils.ceiling(date6, Calendar.DATE), "ceiling MET date across DST change-over");
695         assertEquals(dateTimeParser.parse("March 31, 2003 00:00:00.000"), DateUtils.ceiling((Object) cal6, Calendar.DATE),
696                 "ceiling MET date across DST change-over");
697         assertEquals(dateTimeParser.parse("March 31, 2003 00:00:00.000"), DateUtils.ceiling(date7, Calendar.DATE), "ceiling MET date across DST change-over");
698         assertEquals(dateTimeParser.parse("March 31, 2003 00:00:00.000"), DateUtils.ceiling((Object) cal7, Calendar.DATE),
699                 "ceiling MET date across DST change-over");
700 
701         assertEquals(dateTimeParser.parse("March 30, 2003 03:00:00.000"), DateUtils.ceiling(date4, Calendar.HOUR_OF_DAY),
702                 "ceiling MET date across DST change-over");
703         assertEquals(dateTimeParser.parse("March 30, 2003 03:00:00.000"), DateUtils.ceiling((Object) cal4, Calendar.HOUR_OF_DAY),
704                 "ceiling MET date across DST change-over");
705         assertEquals(dateTimeParser.parse("March 30, 2003 03:00:00.000"), DateUtils.ceiling(date5, Calendar.HOUR_OF_DAY),
706                 "ceiling MET date across DST change-over");
707         assertEquals(dateTimeParser.parse("March 30, 2003 03:00:00.000"), DateUtils.ceiling((Object) cal5, Calendar.HOUR_OF_DAY),
708                 "ceiling MET date across DST change-over");
709         assertEquals(dateTimeParser.parse("March 30, 2003 04:00:00.000"), DateUtils.ceiling(date6, Calendar.HOUR_OF_DAY),
710                 "ceiling MET date across DST change-over");
711         assertEquals(dateTimeParser.parse("March 30, 2003 04:00:00.000"), DateUtils.ceiling((Object) cal6, Calendar.HOUR_OF_DAY),
712                 "ceiling MET date across DST change-over");
713         assertEquals(dateTimeParser.parse("March 30, 2003 04:00:00.000"), DateUtils.ceiling(date7, Calendar.HOUR_OF_DAY),
714                 "ceiling MET date across DST change-over");
715         assertEquals(dateTimeParser.parse("March 30, 2003 04:00:00.000"), DateUtils.ceiling((Object) cal7, Calendar.HOUR_OF_DAY),
716                 "ceiling MET date across DST change-over");
717     }
718 
719     @Test
720     void testConstructor() {
721         assertNotNull(new DateUtils());
722         final Constructor<?>[] cons = DateUtils.class.getDeclaredConstructors();
723         assertEquals(1, cons.length);
724         assertTrue(Modifier.isPublic(cons[0].getModifiers()));
725         assertTrue(Modifier.isPublic(DateUtils.class.getModifiers()));
726         assertFalse(Modifier.isFinal(DateUtils.class.getModifiers()));
727     }
728 
729     /**
730      * Failed date parse can poison the calendar's time zone for the next pattern.
731      *
732      * <p>
733      * {@link DateUtils#parseDateWithLeniency} reuses a single {@link Calendar} across pattern attempts. The reset between iterations is
734      * {@link Calendar#clear()}, which does NOT reset the calendar's time zone. A TZ-aware pattern (e.g. starts with {@code "X"}, {@code "Z"}, {@code "z"}) that
735      * successfully consumes its TZ token (mutating the calendar via {@link Calendar#setTimeZone}) before failing on later tokens leaves the calendar carrying
736      * the parsed (foreign) zone. The next pattern in the list then interprets its date fields against that zone, producing a wrong instant.
737      * </p>
738      *
739      * <p>
740      * <b>Construction:</b> input {@code "+0500 01/01/2024"} is parsed against two patterns:
741      * </p>
742      * <ol>
743      * <li>{@code "Z 'X' MM/dd/yyyy"}: the {@code Z} strategy consumes {@code "+0500"} and calls {@code calendar.setTimeZone(GMT+05:00)}; the next literal
744      * {@code 'X'} fails to match {@code " 01"}, so the overall parse returns false. The leaked TZ remains on the shared calendar.</li>
745      * <li>{@code "'+0500 'MM/dd/yyyy"}: a TZ-less pattern that absorbs the literal prefix and parses the date. With the bug, the calendar still carries
746      * GMT+05:00, so the date is interpreted as midnight in GMT+05:00 (5 hours earlier as an instant) than the same date interpreted in the system default
747      * zone.</li>
748      * </ol>
749      *
750      * <p>
751      * The assertion compares {@link Date#getTime()} against a baseline parse using only the second pattern. A naive PoC that only checked calendar fields
752      * (year/month/day) would not detect the bug because those equal in both runs.
753      * </p>
754      *
755      * <p>
756      * Pre-patch: the bug shifts the resulting instant by 5 hours.<br>
757      * Post-patch: {@code parseDateWithLeniency} restores {@code calendar.setTimeZone(TimeZone.getDefault())} before each iteration, so the leaked zone never
758      * reaches the second attempt and both instants match.
759      * </p>
760      */
761     @Test
762     public void testFailedTimeZonePatternDoesNotPoisonNextPatternInstant() throws Exception {
763         final String input = "+0500 01/01/2024";
764         // Baseline: a single TZ-less pattern; no leak source. The calendar uses
765         // TimeZone.getDefault(), so the result is midnight 2024-01-01 in the default zone.
766         final Date baseline = DateUtils.parseDate(input, "'+0500 'MM/dd/yyyy");
767         assertNotNull(baseline, "baseline parse must succeed");
768         // Poisoning sequence:
769         //   pattern 1 = "Z 'X' MM/dd/yyyy"  -> Z consumes "+0500" then setTimeZone(GMT+05:00),
770         //                                       'X' literal mismatches " 01", returns false.
771         //   pattern 2 = "'+0500 'MM/dd/yyyy"-> matches the whole input via literal prefix +
772         //                                       MM/dd/yyyy. With the bug, calendar still has
773         //                                       GMT+05:00 leaked from pattern 1, so the date
774         //                                       is interpreted in GMT+05:00 instead of default.
775         final Date poisoned = DateUtils.parseDate(input, "Z 'X' MM/dd/yyyy", "'+0500 'MM/dd/yyyy");
776         assertNotNull(poisoned, "poisoning sequence parse must succeed via the second pattern");
777         // Compare INSTANTS, not Y/M/D. Calendar fields equal in both runs by coincidence
778         // even when the bug shifts the underlying instant.
779         assertEquals(baseline.getTime(), poisoned.getTime(),
780                 "Calendar TZ leaked from a failed prior pattern must not affect the instant produced by a subsequent TZ-less pattern");
781     }
782 
783     @Test
784     void testIsSameDay_Cal() {
785         final GregorianCalendar cala = new GregorianCalendar(2004, 6, 9, 13, 45);
786         final GregorianCalendar calb = new GregorianCalendar(2004, 6, 9, 13, 45);
787         assertTrue(DateUtils.isSameDay(cala, calb));
788         calb.add(Calendar.DAY_OF_YEAR, 1);
789         assertFalse(DateUtils.isSameDay(cala, calb));
790         cala.add(Calendar.DAY_OF_YEAR, 1);
791         assertTrue(DateUtils.isSameDay(cala, calb));
792         calb.add(Calendar.YEAR, 1);
793         assertFalse(DateUtils.isSameDay(cala, calb));
794     }
795 
796     @Test
797     void testIsSameDay_CalNotNullNull() {
798         assertNullPointerException(() -> DateUtils.isSameDay(Calendar.getInstance(), null));
799     }
800 
801     @Test
802     void testIsSameDay_CalNullNotNull() {
803         assertNullPointerException(() -> DateUtils.isSameDay(null, Calendar.getInstance()));
804     }
805 
806     @Test
807     void testIsSameDay_CalNullNull() {
808         assertNullPointerException(() -> DateUtils.isSameDay((Calendar) null, null));
809     }
810 
811     @Test
812     void testIsSameDay_Date() {
813         Date dateA = new GregorianCalendar(2004, 6, 9, 13, 45).getTime();
814         Date dateB = new GregorianCalendar(2004, 6, 9, 13, 45).getTime();
815         assertTrue(DateUtils.isSameDay(dateA, dateB));
816         dateB = new GregorianCalendar(2004, 6, 10, 13, 45).getTime();
817         assertFalse(DateUtils.isSameDay(dateA, dateB));
818         dateA = new GregorianCalendar(2004, 6, 10, 13, 45).getTime();
819         assertTrue(DateUtils.isSameDay(dateA, dateB));
820         dateB = new GregorianCalendar(2005, 6, 10, 13, 45).getTime();
821         assertFalse(DateUtils.isSameDay(dateA, dateB));
822         assertTrue(DateUtils.isSameDay(dateA, dateA));
823         assertTrue(DateUtils.isSameDay(dateA, (Date) dateA.clone()));
824         assertTrue(DateUtils.isSameDay(MAX_DATE, MAX_DATE));
825         assertTrue(DateUtils.isSameDay(MAX_DATE, (Date) MAX_DATE.clone()));
826     }
827 
828     @Test
829     void testIsSameDay_DateNotNullNull() {
830         assertNullPointerException(() -> DateUtils.isSameDay(new Date(), null));
831     }
832 
833     @Test
834     void testIsSameDay_DateNullNotNull() {
835         assertNullPointerException(() -> DateUtils.isSameDay(null, new Date()));
836     }
837 
838     @Test
839     void testIsSameDay_DateNullNull() {
840         assertNullPointerException(() -> DateUtils.isSameDay((Date) null, null));
841     }
842 
843     @Test
844     void testIsSameInstant_Cal() {
845         final GregorianCalendar cala = new GregorianCalendar(TimeZones.getTimeZone("GMT+1"));
846         final GregorianCalendar calb = new GregorianCalendar(TimeZones.getTimeZone("GMT-1"));
847         cala.set(2004, Calendar.JULY, 9, 13, 45, 0);
848         cala.set(Calendar.MILLISECOND, 0);
849         calb.set(2004, Calendar.JULY, 9, 13, 45, 0);
850         calb.set(Calendar.MILLISECOND, 0);
851         assertFalse(DateUtils.isSameInstant(cala, calb));
852 
853         calb.set(2004, Calendar.JULY, 9, 11, 45, 0);
854         assertTrue(DateUtils.isSameInstant(cala, calb));
855     }
856 
857     @Test
858     void testIsSameInstant_CalNotNullNull() {
859         assertNullPointerException(() -> DateUtils.isSameInstant(Calendar.getInstance(), null));
860     }
861 
862     @Test
863     void testIsSameInstant_CalNullNotNull() {
864         assertNullPointerException(() -> DateUtils.isSameInstant(null, Calendar.getInstance()));
865     }
866 
867     @Test
868     void testIsSameInstant_CalNullNull() {
869         assertNullPointerException(() -> DateUtils.isSameInstant((Calendar) null, null));
870     }
871 
872     @Test
873     void testIsSameInstant_Date() {
874         Date datea = new GregorianCalendar(2004, 6, 9, 13, 45).getTime();
875         Date dateb = new GregorianCalendar(2004, 6, 9, 13, 45).getTime();
876         assertTrue(DateUtils.isSameInstant(datea, dateb));
877         dateb = new GregorianCalendar(2004, 6, 10, 13, 45).getTime();
878         assertFalse(DateUtils.isSameInstant(datea, dateb));
879         datea = new GregorianCalendar(2004, 6, 10, 13, 45).getTime();
880         assertTrue(DateUtils.isSameInstant(datea, dateb));
881         dateb = new GregorianCalendar(2005, 6, 10, 13, 45).getTime();
882         assertFalse(DateUtils.isSameInstant(datea, dateb));
883     }
884 
885     @Test
886     void testIsSameInstant_DateNotNullNull() {
887         assertNullPointerException(() -> DateUtils.isSameInstant(new Date(), null));
888     }
889 
890     @Test
891     void testIsSameInstant_DateNullNotNull() {
892         assertNullPointerException(() -> DateUtils.isSameInstant(null, new Date()));
893     }
894 
895     @Test
896     void testIsSameInstant_DateNullNull() {
897         assertNullPointerException(() -> DateUtils.isSameInstant((Date) null, null));
898     }
899 
900     @Test
901     void testIsSameLocalTime_Cal() {
902         final GregorianCalendar cala = new GregorianCalendar(TimeZones.getTimeZone("GMT+1"));
903         final GregorianCalendar calb = new GregorianCalendar(TimeZones.getTimeZone("GMT-1"));
904         cala.set(2004, Calendar.JULY, 9, 13, 45, 0);
905         cala.set(Calendar.MILLISECOND, 0);
906         calb.set(2004, Calendar.JULY, 9, 13, 45, 0);
907         calb.set(Calendar.MILLISECOND, 0);
908         assertTrue(DateUtils.isSameLocalTime(cala, calb));
909 
910         final Calendar calc = Calendar.getInstance();
911         final Calendar cald = Calendar.getInstance();
912         calc.set(2004, Calendar.JULY, 9, 4,  0, 0);
913         cald.set(2004, Calendar.JULY, 9, 16, 0, 0);
914         calc.set(Calendar.MILLISECOND, 0);
915         cald.set(Calendar.MILLISECOND, 0);
916         assertFalse(DateUtils.isSameLocalTime(calc, cald), "LANG-677");
917 
918         calb.set(2004, Calendar.JULY, 9, 11, 45, 0);
919         assertFalse(DateUtils.isSameLocalTime(cala, calb));
920     }
921 
922     @Test
923     void testIsSameLocalTime_CalNotNullNull() {
924         assertNullPointerException(() -> DateUtils.isSameLocalTime(Calendar.getInstance(), null));
925     }
926 
927     @Test
928     void testIsSameLocalTime_CalNullNotNull() {
929         assertNullPointerException(() -> DateUtils.isSameLocalTime(null, Calendar.getInstance()));
930     }
931 
932     @Test
933     void testIsSameLocalTime_CalNullNull() {
934         assertNullPointerException(() -> DateUtils.isSameLocalTime(null, null));
935     }
936 
937     /**
938      * Tests the iterator exceptions
939      */
940     @Test
941     void testIteratorEx() {
942         assertIllegalArgumentException(() -> DateUtils.iterator(Calendar.getInstance(), -9999));
943         assertNullPointerException(() -> DateUtils.iterator((Date) null, DateUtils.RANGE_WEEK_CENTER));
944         assertNullPointerException(() -> DateUtils.iterator((Calendar) null, DateUtils.RANGE_WEEK_CENTER));
945         assertNullPointerException(() -> DateUtils.iterator((Object) null, DateUtils.RANGE_WEEK_CENTER));
946         assertThrows(ClassCastException.class, () -> DateUtils.iterator("", DateUtils.RANGE_WEEK_CENTER));
947     }
948 
949     /** See https://issues.apache.org/jira/browse/LANG-530 */
950     @SuppressWarnings("deprecation")
951     @Test
952     void testLang530() throws ParseException {
953         final Date d = new Date();
954         final String isoDateStr = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(d);
955         final Date d2 = DateUtils.parseDate(isoDateStr, DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern());
956         // the format loses milliseconds so have to reintroduce them
957         assertEquals(d.getTime(), d2.getTime() + d.getTime() % 1000, "Date not equal to itself ISO formatted and parsed");
958     }
959 
960     @Test
961     void testLANG799() throws ParseException {
962         DateUtils.parseDateStrictly("09 abril 2008 23:55:38 GMT", new Locale("es"), "dd MMM yyyy HH:mm:ss zzz");
963     }
964 
965     /** Parse English date with German Locale. */
966     @DefaultLocale(language = "de")
967     @Test
968     void testLANG799_DE_FAIL() {
969         assertThrows(ParseException.class, () -> DateUtils.parseDate("Wed, 09 Apr 2008 23:55:38 GMT", "EEE, dd MMM yyyy HH:mm:ss zzz"));
970     }
971 
972     @DefaultLocale(language = "de")
973     @Test
974     void testLANG799_DE_OK() throws ParseException {
975         DateUtils.parseDate("Mi, 09 Apr 2008 23:55:38 GMT", "EEE, dd MMM yyyy HH:mm:ss zzz");
976         DateUtils.parseDateStrictly("Mi, 09 Apr 2008 23:55:38 GMT", "EEE, dd MMM yyyy HH:mm:ss zzz");
977     }
978 
979     // Parse German date with English Locale
980     @DefaultLocale(language = "en")
981     @Test
982     void testLANG799_EN_FAIL() {
983         assertThrows(ParseException.class, () -> DateUtils.parseDate("Mi, 09 Apr 2008 23:55:38 GMT", "EEE, dd MMM yyyy HH:mm:ss zzz"));
984     }
985 
986     @DefaultLocale(language = "en")
987     @Test
988     void testLANG799_EN_OK() throws ParseException {
989         DateUtils.parseDate("Wed, 09 Apr 2008 23:55:38 GMT", "EEE, dd MMM yyyy HH:mm:ss zzz");
990         DateUtils.parseDateStrictly("Wed, 09 Apr 2008 23:55:38 GMT", "EEE, dd MMM yyyy HH:mm:ss zzz");
991     }
992 
993     /** Parse German date with English Locale, specifying German Locale override. */
994     @DefaultLocale(language = "en")
995     @Test
996     void testLANG799_EN_WITH_DE_LOCALE() throws ParseException {
997         DateUtils.parseDate("Mi, 09 Apr 2008 23:55:38 GMT", Locale.GERMAN, "EEE, dd MMM yyyy HH:mm:ss zzz");
998     }
999 
1000     /**
1001      * Tests the calendar iterator for month-based ranges
1002      *
1003      * @throws Exception Thrown if an operation in the test fails.
1004      */
1005     @Test
1006     void testMonthIterator() throws Exception {
1007         Iterator<?> it = DateUtils.iterator(date1, DateUtils.RANGE_MONTH_SUNDAY);
1008         assertWeekIterator(it,
1009                 dateParser.parse("January 27, 2002"),
1010                 dateParser.parse("March 2, 2002"));
1011 
1012         it = DateUtils.iterator(date1, DateUtils.RANGE_MONTH_MONDAY);
1013         assertWeekIterator(it,
1014                 dateParser.parse("January 28, 2002"),
1015                 dateParser.parse("March 3, 2002"));
1016 
1017         it = DateUtils.iterator(date2, DateUtils.RANGE_MONTH_SUNDAY);
1018         assertWeekIterator(it,
1019                 dateParser.parse("October 28, 2001"),
1020                 dateParser.parse("December 1, 2001"));
1021 
1022         it = DateUtils.iterator(date2, DateUtils.RANGE_MONTH_MONDAY);
1023         assertWeekIterator(it,
1024                 dateParser.parse("October 29, 2001"),
1025                 dateParser.parse("December 2, 2001"));
1026     }
1027 
1028     @Test
1029     void testParse_EmptyParsers() {
1030         assertThrows(ParseException.class, () -> DateUtils.parseDate("19721203"));
1031     }
1032 
1033     @Test
1034     void testParse_NullParsers() {
1035         assertNullPointerException(() -> DateUtils.parseDate("19721203", (String[]) null));
1036     }
1037 
1038     @Test
1039     void testParseDate() throws Exception {
1040         final GregorianCalendar cal = new GregorianCalendar(1972, 11, 3);
1041         String dateStr = "1972-12-03";
1042         final String[] parsers = {"yyyy'-'DDD", "yyyy'-'MM'-'dd", "yyyyMMdd"};
1043         Date date = DateUtils.parseDate(dateStr, parsers);
1044         assertEquals(cal.getTime(), date);
1045 
1046         dateStr = "1972-338";
1047         date = DateUtils.parseDate(dateStr, parsers);
1048         assertEquals(cal.getTime(), date);
1049 
1050         dateStr = "19721203";
1051         date = DateUtils.parseDate(dateStr, parsers);
1052         assertEquals(cal.getTime(), date);
1053     }
1054 
1055     @Test
1056     void testParseDate_InvalidDateString() {
1057         final String[] parsers = {"yyyy'-'DDD", "yyyy'-'MM'-'dd", "yyyyMMdd"};
1058         assertThrows(ParseException.class, () -> DateUtils.parseDate("197212AB", parsers));
1059     }
1060 
1061     @Test
1062     void testParseDate_NoDateString() {
1063         final String[] parsers = {"yyyy'-'DDD", "yyyy'-'MM'-'dd", "yyyyMMdd"};
1064         assertThrows(ParseException.class, () -> DateUtils.parseDate("PURPLE", parsers));
1065     }
1066 
1067     @Test
1068     void testParseDate_Null() {
1069         final String[] parsers = {"yyyy'-'DDD", "yyyy'-'MM'-'dd", "yyyyMMdd"};
1070         assertNullPointerException(() -> DateUtils.parseDate(null, parsers));
1071     }
1072 
1073     /** LANG-486 */
1074     @Test
1075     void testParseDateWithLeniency() throws ParseException {
1076         final GregorianCalendar cal = new GregorianCalendar(1998, 6, 30);
1077         final String dateStr = "02 942, 1996";
1078         final String[] parsers = {"MM DDD, yyyy"};
1079 
1080         final Date date = DateUtils.parseDate(dateStr, parsers);
1081         assertEquals(cal.getTime(), date);
1082 
1083         assertThrows(ParseException.class, () -> DateUtils.parseDateStrictly(dateStr, parsers));
1084     }
1085 
1086     /**
1087      * Tests various values with the round method
1088      *
1089      * @throws Exception Thrown if an operation in the test fails.
1090      */
1091     @Test
1092     void testRound() throws Exception {
1093         // tests for public static Date round(Date date, int field)
1094         assertEquals(dateParser.parse("January 1, 2002"), DateUtils.round(date1, Calendar.YEAR), "round year-1 failed");
1095         assertEquals(dateParser.parse("January 1, 2002"), DateUtils.round(date2, Calendar.YEAR), "round year-2 failed");
1096         assertEquals(dateParser.parse("February 1, 2002"), DateUtils.round(date1, Calendar.MONTH), "round month-1 failed");
1097         assertEquals(dateParser.parse("December 1, 2001"), DateUtils.round(date2, Calendar.MONTH), "round month-2 failed");
1098         assertEquals(dateParser.parse("February 1, 2002"), DateUtils.round(date0, DateUtils.SEMI_MONTH), "round semimonth-0 failed");
1099         assertEquals(dateParser.parse("February 16, 2002"), DateUtils.round(date1, DateUtils.SEMI_MONTH), "round semimonth-1 failed");
1100         assertEquals(dateParser.parse("November 16, 2001"), DateUtils.round(date2, DateUtils.SEMI_MONTH), "round semimonth-2 failed");
1101 
1102         assertEquals(dateParser.parse("February 13, 2002"), DateUtils.round(date1, Calendar.DATE), "round date-1 failed");
1103         assertEquals(dateParser.parse("November 18, 2001"), DateUtils.round(date2, Calendar.DATE), "round date-2 failed");
1104         assertEquals(dateTimeParser.parse("February 12, 2002 13:00:00.000"), DateUtils.round(date1, Calendar.HOUR), "round hour-1 failed");
1105         assertEquals(dateTimeParser.parse("November 18, 2001 1:00:00.000"), DateUtils.round(date2, Calendar.HOUR), "round hour-2 failed");
1106         assertEquals(dateTimeParser.parse("February 12, 2002 12:35:00.000"), DateUtils.round(date1, Calendar.MINUTE), "round minute-1 failed");
1107         assertEquals(dateTimeParser.parse("November 18, 2001 1:23:00.000"), DateUtils.round(date2, Calendar.MINUTE), "round minute-2 failed");
1108         assertEquals(dateTimeParser.parse("February 12, 2002 12:34:57.000"), DateUtils.round(date1, Calendar.SECOND), "round second-1 failed");
1109         assertEquals(dateTimeParser.parse("November 18, 2001 1:23:11.000"), DateUtils.round(date2, Calendar.SECOND), "round second-2 failed");
1110         assertEquals(dateTimeParser.parse("February 3, 2002 00:00:00.000"), DateUtils.round(dateAmPm1, Calendar.AM_PM), "round ampm-1 failed");
1111         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"), DateUtils.round(dateAmPm2, Calendar.AM_PM), "round ampm-2 failed");
1112         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"), DateUtils.round(dateAmPm3, Calendar.AM_PM), "round ampm-3 failed");
1113         assertEquals(dateTimeParser.parse("February 4, 2002 00:00:00.000"), DateUtils.round(dateAmPm4, Calendar.AM_PM), "round ampm-4 failed");
1114 
1115         // tests for public static Date round(Object date, int field)
1116         assertEquals(dateParser.parse("January 1, 2002"), DateUtils.round((Object) date1, Calendar.YEAR), "round year-1 failed");
1117         assertEquals(dateParser.parse("January 1, 2002"), DateUtils.round((Object) date2, Calendar.YEAR), "round year-2 failed");
1118         assertEquals(dateParser.parse("February 1, 2002"), DateUtils.round((Object) date1, Calendar.MONTH), "round month-1 failed");
1119         assertEquals(dateParser.parse("December 1, 2001"), DateUtils.round((Object) date2, Calendar.MONTH), "round month-2 failed");
1120         assertEquals(dateParser.parse("February 16, 2002"), DateUtils.round((Object) date1, DateUtils.SEMI_MONTH), "round semimonth-1 failed");
1121         assertEquals(dateParser.parse("November 16, 2001"), DateUtils.round((Object) date2, DateUtils.SEMI_MONTH), "round semimonth-2 failed");
1122         assertEquals(dateParser.parse("February 13, 2002"), DateUtils.round((Object) date1, Calendar.DATE), "round date-1 failed");
1123         assertEquals(dateParser.parse("November 18, 2001"), DateUtils.round((Object) date2, Calendar.DATE), "round date-2 failed");
1124         assertEquals(dateTimeParser.parse("February 12, 2002 13:00:00.000"), DateUtils.round((Object) date1, Calendar.HOUR), "round hour-1 failed");
1125         assertEquals(dateTimeParser.parse("November 18, 2001 1:00:00.000"), DateUtils.round((Object) date2, Calendar.HOUR), "round hour-2 failed");
1126         assertEquals(dateTimeParser.parse("February 12, 2002 12:35:00.000"), DateUtils.round((Object) date1, Calendar.MINUTE), "round minute-1 failed");
1127         assertEquals(dateTimeParser.parse("November 18, 2001 1:23:00.000"), DateUtils.round((Object) date2, Calendar.MINUTE), "round minute-2 failed");
1128         assertEquals(dateTimeParser.parse("February 12, 2002 12:34:57.000"), DateUtils.round((Object) date1, Calendar.SECOND), "round second-1 failed");
1129         assertEquals(dateTimeParser.parse("November 18, 2001 1:23:11.000"), DateUtils.round((Object) date2, Calendar.SECOND), "round second-2 failed");
1130         assertEquals(dateTimeParser.parse("February 12, 2002 12:34:57.000"), DateUtils.round((Object) cal1, Calendar.SECOND), "round calendar second-1 failed");
1131         assertEquals(dateTimeParser.parse("November 18, 2001 1:23:11.000"), DateUtils.round((Object) cal2, Calendar.SECOND), "round calendar second-2 failed");
1132         assertEquals(dateTimeParser.parse("February 3, 2002 00:00:00.000"), DateUtils.round((Object) dateAmPm1, Calendar.AM_PM), "round ampm-1 failed");
1133         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"), DateUtils.round((Object) dateAmPm2, Calendar.AM_PM), "round ampm-2 failed");
1134         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"), DateUtils.round((Object) dateAmPm3, Calendar.AM_PM), "round ampm-3 failed");
1135         assertEquals(dateTimeParser.parse("February 4, 2002 00:00:00.000"), DateUtils.round((Object) dateAmPm4, Calendar.AM_PM), "round ampm-4 failed");
1136 
1137         assertNullPointerException(() -> DateUtils.round((Date) null, Calendar.SECOND));
1138         assertNullPointerException(() -> DateUtils.round((Calendar) null, Calendar.SECOND));
1139         assertNullPointerException(() -> DateUtils.round((Object) null, Calendar.SECOND));
1140         assertThrows(ClassCastException.class, () -> DateUtils.round("", Calendar.SECOND));
1141         assertIllegalArgumentException(() -> DateUtils.round(date1, -9999));
1142 
1143         assertEquals(dateTimeParser.parse("February 3, 2002 00:00:00.000"), DateUtils.round((Object) calAmPm1, Calendar.AM_PM), "round ampm-1 failed");
1144         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"), DateUtils.round((Object) calAmPm2, Calendar.AM_PM), "round ampm-2 failed");
1145         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"), DateUtils.round((Object) calAmPm3, Calendar.AM_PM), "round ampm-3 failed");
1146         assertEquals(dateTimeParser.parse("February 4, 2002 00:00:00.000"), DateUtils.round((Object) calAmPm4, Calendar.AM_PM), "round ampm-4 failed");
1147     }
1148 
1149     /**
1150      * Tests various values with the round method
1151      *
1152      * @throws Exception Thrown if an operation in the test fails.
1153      */
1154     @Test
1155     void testRound_MET() throws Exception {
1156         // Fix for https://issues.apache.org/bugzilla/show_bug.cgi?id=25560 / LANG-13
1157         // Test rounding across the beginning of daylight saving time
1158         TimeZone.setDefault(TIME_ZONE_MET);
1159         dateTimeParser.setTimeZone(TIME_ZONE_MET);
1160         assertEquals(dateTimeParser.parse("March 30, 2003 00:00:00.000"), DateUtils.round(date4, Calendar.DATE), "round MET date across DST change-over");
1161         assertEquals(dateTimeParser.parse("March 30, 2003 00:00:00.000"), DateUtils.round((Object) cal4, Calendar.DATE),
1162                 "round MET date across DST change-over");
1163         assertEquals(dateTimeParser.parse("March 30, 2003 00:00:00.000"), DateUtils.round(date5, Calendar.DATE), "round MET date across DST change-over");
1164         assertEquals(dateTimeParser.parse("March 30, 2003 00:00:00.000"), DateUtils.round((Object) cal5, Calendar.DATE),
1165                 "round MET date across DST change-over");
1166         assertEquals(dateTimeParser.parse("March 30, 2003 00:00:00.000"), DateUtils.round(date6, Calendar.DATE), "round MET date across DST change-over");
1167         assertEquals(dateTimeParser.parse("March 30, 2003 00:00:00.000"), DateUtils.round((Object) cal6, Calendar.DATE),
1168                 "round MET date across DST change-over");
1169         assertEquals(dateTimeParser.parse("March 30, 2003 00:00:00.000"), DateUtils.round(date7, Calendar.DATE), "round MET date across DST change-over");
1170         assertEquals(dateTimeParser.parse("March 30, 2003 00:00:00.000"), DateUtils.round((Object) cal7, Calendar.DATE),
1171                 "round MET date across DST change-over");
1172 
1173         assertEquals(dateTimeParser.parse("March 30, 2003 01:00:00.000"), DateUtils.round(date4, Calendar.HOUR_OF_DAY),
1174                 "round MET date across DST change-over");
1175         assertEquals(dateTimeParser.parse("March 30, 2003 01:00:00.000"), DateUtils.round((Object) cal4, Calendar.HOUR_OF_DAY),
1176                 "round MET date across DST change-over");
1177         assertEquals(dateTimeParser.parse("March 30, 2003 03:00:00.000"), DateUtils.round(date5, Calendar.HOUR_OF_DAY),
1178                 "round MET date across DST change-over");
1179         assertEquals(dateTimeParser.parse("March 30, 2003 03:00:00.000"), DateUtils.round((Object) cal5, Calendar.HOUR_OF_DAY),
1180                 "round MET date across DST change-over");
1181         assertEquals(dateTimeParser.parse("March 30, 2003 03:00:00.000"), DateUtils.round(date6, Calendar.HOUR_OF_DAY),
1182                 "round MET date across DST change-over");
1183         assertEquals(dateTimeParser.parse("March 30, 2003 03:00:00.000"), DateUtils.round((Object) cal6, Calendar.HOUR_OF_DAY),
1184                 "round MET date across DST change-over");
1185         assertEquals(dateTimeParser.parse("March 30, 2003 04:00:00.000"), DateUtils.round(date7, Calendar.HOUR_OF_DAY),
1186                 "round MET date across DST change-over");
1187         assertEquals(dateTimeParser.parse("March 30, 2003 04:00:00.000"), DateUtils.round((Object) cal7, Calendar.HOUR_OF_DAY),
1188                 "round MET date across DST change-over");
1189     }
1190 
1191     /**
1192      * Tests the Changes Made by LANG-346 to the DateUtils.modify() private method invoked
1193      * by DateUtils.round().
1194      *
1195      * @throws Exception Thrown if an operation in the test fails.
1196      */
1197     @Test
1198     void testRoundLang346() throws Exception {
1199         final Calendar testCalendar = Calendar.getInstance();
1200         testCalendar.set(2007, Calendar.JULY, 2, 8, 8, 50);
1201         Date date = testCalendar.getTime();
1202         assertEquals(dateTimeParser.parse("July 2, 2007 08:09:00.000"),
1203                 DateUtils.round(date, Calendar.MINUTE),
1204                 "Minute Round Up Failed");
1205 
1206         testCalendar.set(2007, Calendar.JULY, 2, 8, 8, 20);
1207         date = testCalendar.getTime();
1208         assertEquals(dateTimeParser.parse("July 2, 2007 08:08:00.000"),
1209                 DateUtils.round(date, Calendar.MINUTE),
1210                 "Minute No Round Failed");
1211 
1212         testCalendar.set(2007, Calendar.JULY, 2, 8, 8, 50);
1213         testCalendar.set(Calendar.MILLISECOND, 600);
1214         date = testCalendar.getTime();
1215 
1216         assertEquals(dateTimeParser.parse("July 2, 2007 08:08:51.000"),
1217                 DateUtils.round(date, Calendar.SECOND),
1218                 "Second Round Up with 600 Milli Seconds Failed");
1219 
1220         testCalendar.set(2007, Calendar.JULY, 2, 8, 8, 50);
1221         testCalendar.set(Calendar.MILLISECOND, 200);
1222         date = testCalendar.getTime();
1223         assertEquals(dateTimeParser.parse("July 2, 2007 08:08:50.000"),
1224                 DateUtils.round(date, Calendar.SECOND),
1225                 "Second Round Down with 200 Milli Seconds Failed");
1226 
1227         testCalendar.set(2007, Calendar.JULY, 2, 8, 8, 20);
1228         testCalendar.set(Calendar.MILLISECOND, 600);
1229         date = testCalendar.getTime();
1230         assertEquals(dateTimeParser.parse("July 2, 2007 08:08:21.000"),
1231                 DateUtils.round(date, Calendar.SECOND),
1232                 "Second Round Up with 200 Milli Seconds Failed");
1233 
1234         testCalendar.set(2007, Calendar.JULY, 2, 8, 8, 20);
1235         testCalendar.set(Calendar.MILLISECOND, 200);
1236         date = testCalendar.getTime();
1237         assertEquals(dateTimeParser.parse("July 2, 2007 08:08:20.000"),
1238                 DateUtils.round(date, Calendar.SECOND),
1239                 "Second Round Down with 200 Milli Seconds Failed");
1240 
1241         testCalendar.set(2007, Calendar.JULY, 2, 8, 8, 50);
1242         date = testCalendar.getTime();
1243         assertEquals(dateTimeParser.parse("July 2, 2007 08:00:00.000"),
1244                 DateUtils.round(date, Calendar.HOUR),
1245                 "Hour Round Down Failed");
1246 
1247         testCalendar.set(2007, Calendar.JULY, 2, 8, 31, 50);
1248         date = testCalendar.getTime();
1249         assertEquals(dateTimeParser.parse("July 2, 2007 09:00:00.000"),
1250                 DateUtils.round(date, Calendar.HOUR),
1251                 "Hour Round Up Failed");
1252     }
1253 
1254     @Test
1255     void testSetDays() throws Exception {
1256         Date result = DateUtils.setDays(BASE_DATE, 1);
1257         assertNotSame(BASE_DATE, result);
1258         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1259         assertDate(result, 2000, 6, 1, 4, 3, 2, 1);
1260         result = DateUtils.setDays(BASE_DATE, 29);
1261         assertNotSame(BASE_DATE, result);
1262         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1263         assertDate(result, 2000, 6, 29, 4, 3, 2, 1);
1264         final String outsideOfRangeAssertionMessage = "DateUtils.setDays did not throw an expected IllegalArgumentException for amount outside of range 1 to 31.";
1265         assertIllegalArgumentException(() -> DateUtils.setDays(BASE_DATE, 32), outsideOfRangeAssertionMessage);
1266         assertIllegalArgumentException(() -> DateUtils.setDays(BASE_DATE, 0), outsideOfRangeAssertionMessage);
1267         assertNullPointerException(() -> DateUtils.setDays(null, 1));
1268     }
1269 
1270     @Test
1271     void testSetHours() throws Exception {
1272         Date result = DateUtils.setHours(BASE_DATE, 0);
1273         assertNotSame(BASE_DATE, result);
1274         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1275         assertDate(result, 2000, 6, 5, 0, 3, 2, 1);
1276         result = DateUtils.setHours(BASE_DATE, 23);
1277         assertNotSame(BASE_DATE, result);
1278         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1279         assertDate(result, 2000, 6, 5, 23, 3, 2, 1);
1280         final String outsideOfRangeAssertionMessage = "DateUtils.setHours did not throw an expected IllegalArgumentException for amount outside of range 0 to 23.";
1281         assertIllegalArgumentException(() -> DateUtils.setHours(BASE_DATE, 24), outsideOfRangeAssertionMessage);
1282         assertIllegalArgumentException(() -> DateUtils.setHours(BASE_DATE, -1), outsideOfRangeAssertionMessage);
1283         assertNullPointerException(() -> DateUtils.setHours(null, 0));
1284     }
1285 
1286     @Test
1287     void testSetMilliseconds() throws Exception {
1288         Date result = DateUtils.setMilliseconds(BASE_DATE, 0);
1289         assertNotSame(BASE_DATE, result);
1290         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1291         assertDate(result, 2000, 6, 5, 4, 3, 2, 0);
1292         result = DateUtils.setMilliseconds(BASE_DATE, 999);
1293         assertNotSame(BASE_DATE, result);
1294         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1295         assertDate(result, 2000, 6, 5, 4, 3, 2, 999);
1296         final String outsideOfRangeAssertionMessage = "DateUtils.setMilliseconds did not throw an expected IllegalArgumentException for range outside of 0 to 999.";
1297         assertIllegalArgumentException(() -> DateUtils.setMilliseconds(BASE_DATE, 1000), outsideOfRangeAssertionMessage);
1298         assertIllegalArgumentException(() -> DateUtils.setMilliseconds(BASE_DATE, -1), outsideOfRangeAssertionMessage);
1299         assertNullPointerException(() -> DateUtils.setMilliseconds(null, 0));
1300     }
1301 
1302     @Test
1303     void testSetMinutes() throws Exception {
1304         Date result = DateUtils.setMinutes(BASE_DATE, 0);
1305         assertNotSame(BASE_DATE, result);
1306         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1307         assertDate(result, 2000, 6, 5, 4, 0, 2, 1);
1308         result = DateUtils.setMinutes(BASE_DATE, 59);
1309         assertNotSame(BASE_DATE, result);
1310         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1311         assertDate(result, 2000, 6, 5, 4, 59, 2, 1);
1312         final String outsideOfRangeAssertionMessage = "DateUtils.setMinutes did not throw an expected IllegalArgumentException for amount outside of range 0 to 59.";
1313         assertIllegalArgumentException(() -> DateUtils.setMinutes(BASE_DATE, 60), outsideOfRangeAssertionMessage);
1314         assertIllegalArgumentException(() -> DateUtils.setMinutes(BASE_DATE, -1), outsideOfRangeAssertionMessage);
1315         assertNullPointerException(() -> DateUtils.setMinutes(null, 0));
1316     }
1317 
1318     @Test
1319     void testSetMonths() throws Exception {
1320         Date result = DateUtils.setMonths(BASE_DATE, 5);
1321         assertNotSame(BASE_DATE, result);
1322         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1323         assertDate(result, 2000, 5, 5, 4, 3, 2, 1);
1324         result = DateUtils.setMonths(BASE_DATE, 1);
1325         assertNotSame(BASE_DATE, result);
1326         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1327         assertDate(result, 2000, 1, 5, 4, 3, 2, 1);
1328         result = DateUtils.setMonths(BASE_DATE, 0);
1329         assertNotSame(BASE_DATE, result);
1330         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1331         assertDate(result, 2000, 0, 5, 4, 3, 2, 1);
1332         final String outsideOfRangeAssertionMessage = "DateUtils.setMonths did not throw an expected IllegalArgumentException for amount outside of range 0 to 11.";
1333         assertIllegalArgumentException(() -> DateUtils.setMonths(BASE_DATE, 12), outsideOfRangeAssertionMessage);
1334         assertIllegalArgumentException(() -> DateUtils.setMonths(BASE_DATE, -1), outsideOfRangeAssertionMessage);
1335         assertNullPointerException(() -> DateUtils.setMonths(null, 0));
1336     }
1337 
1338     @Test
1339     void testSetSeconds() throws Exception {
1340         Date result = DateUtils.setSeconds(BASE_DATE, 0);
1341         assertNotSame(BASE_DATE, result);
1342         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1343         assertDate(result, 2000, 6, 5, 4, 3, 0, 1);
1344         result = DateUtils.setSeconds(BASE_DATE, 59);
1345         assertNotSame(BASE_DATE, result);
1346         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1347         assertDate(result, 2000, 6, 5, 4, 3, 59, 1);
1348         final String outsideOfRangeAssertionMessage = "DateUtils.setSeconds did not throw an expected IllegalArgumentException for amount outside of range 0 to 59.";
1349         assertIllegalArgumentException(() -> DateUtils.setSeconds(BASE_DATE, 60), outsideOfRangeAssertionMessage);
1350         assertIllegalArgumentException(() -> DateUtils.setSeconds(BASE_DATE, -1), outsideOfRangeAssertionMessage);
1351         assertNullPointerException(() -> DateUtils.setSeconds(null, 0));
1352     }
1353 
1354     @Test
1355     void testSetYears() throws Exception {
1356         Date result = DateUtils.setYears(BASE_DATE, 2000);
1357         assertNotSame(BASE_DATE, result);
1358         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1359         assertDate(result, 2000, 6, 5, 4, 3, 2, 1);
1360 
1361         result = DateUtils.setYears(BASE_DATE, 2008);
1362         assertNotSame(BASE_DATE, result);
1363         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1364         assertDate(result, 2008, 6, 5, 4, 3, 2, 1);
1365 
1366         result = DateUtils.setYears(BASE_DATE, 2005);
1367         assertNotSame(BASE_DATE, result);
1368         assertDate(BASE_DATE, 2000, 6, 5, 4, 3, 2, 1);
1369         assertDate(result, 2005, 6, 5, 4, 3, 2, 1);
1370 
1371         assertNullPointerException(() -> DateUtils.setYears(null, 0));
1372     }
1373 
1374     @Test
1375     void testToCalendar() {
1376         assertEquals(date1, DateUtils.toCalendar(date1).getTime(), "Failed to convert to a Calendar and back");
1377         assertEquals(MAX_DATE, DateUtils.toCalendar(MAX_DATE).getTime(), "Failed to convert to a Calendar and back");
1378         assertNullPointerException(() -> DateUtils.toCalendar(null));
1379     }
1380 
1381     @Test
1382     void testToCalendarWithDateAndTimeZoneNotNull() {
1383         final Calendar c = DateUtils.toCalendar(date2, TIME_ZONE_DEFAULT);
1384         assertEquals(date2, c.getTime(), "Convert Date and TimeZone to a Calendar, but failed to get the Date back");
1385         assertEquals(TIME_ZONE_DEFAULT, c.getTimeZone(), "Convert Date and TimeZone to a Calendar, but failed to get the TimeZone back");
1386     }
1387 
1388     @Test
1389     void testToCalendarWithDateAndTimeZoneNull() {
1390         assertNullPointerException(() -> DateUtils.toCalendar(null, null));
1391     }
1392 
1393     @Test
1394     void testToCalendarWithDateNull() {
1395         assertNullPointerException(() -> DateUtils.toCalendar(null, TIME_ZONE_MET));
1396     }
1397 
1398     @Test
1399     void testToCalendarWithTimeZoneNull() {
1400         assertNullPointerException(() -> DateUtils.toCalendar(date1, null));
1401     }
1402 
1403     @ParameterizedTest
1404     @MethodSource("testToLocalDateTimeTimeZone")
1405     @DefaultLocale(language = "en", country = "US")
1406     @DefaultTimeZone(TimeZones.GMT_ID)
1407     void testToLocalDateTime(final LocalDateTime expected, final Date date, final TimeZone timeZone) {
1408         assertEquals(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()), DateUtils.toLocalDateTime(date),
1409                 () -> String.format("expected = %s, date = %s, timeZone = %s, TimeZone.getDefault() = %s", expected, date, timeZone, TimeZone.getDefault()));
1410     }
1411 
1412     @ParameterizedTest
1413     @MethodSource("testToLocalDateTimeTimeZone")
1414     @DefaultLocale(language = "en", country = "US")
1415     @DefaultTimeZone(TimeZones.GMT_ID)
1416     void testToLocalDateTimeTimeZone(final LocalDateTime expected, final Date date, final TimeZone timeZone) {
1417         assertEquals(expected, DateUtils.toLocalDateTime(date, timeZone),
1418                 () -> String.format("expected = %s, date = %s, timeZone = %s, TimeZone.getDefault() = %s", expected, date, timeZone, TimeZone.getDefault()));
1419     }
1420 
1421     @ParameterizedTest
1422     @MethodSource("testToLocalDateTimeTimeZone")
1423     @DefaultLocale(language = "en", country = "US")
1424     @DefaultTimeZone(TimeZones.GMT_ID)
1425     void testToOffsetDateTime(final LocalDateTime expected, final Date date, final TimeZone timeZone) {
1426         final OffsetDateTime offsetDateTime = DateUtils.toOffsetDateTime(date);
1427         assertEquals(OffsetDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()), offsetDateTime,
1428                 () -> String.format("expected = %s, date = %s, timeZone = %s, TimeZone.getDefault() = %s", expected, date, timeZone, TimeZone.getDefault()));
1429     }
1430 
1431     @ParameterizedTest
1432     @MethodSource("testToLocalDateTimeTimeZone")
1433     @DefaultLocale(language = "en", country = "US")
1434     @DefaultTimeZone(TimeZones.GMT_ID)
1435     void testToOffsetDateTimeTimeZone(final LocalDateTime expected, final Date date, final TimeZone timeZone) {
1436         assertEquals(expected, DateUtils.toOffsetDateTime(date, timeZone).toLocalDateTime(),
1437                 () -> String.format("expected = %s, date = %s, timeZone = %s, TimeZone.getDefault() = %s", expected, date, timeZone, TimeZone.getDefault()));
1438     }
1439 
1440     @ParameterizedTest
1441     @MethodSource("testToLocalDateTimeTimeZone")
1442     @DefaultLocale(language = "en", country = "US")
1443     @DefaultTimeZone(TimeZones.GMT_ID)
1444     void testToZonedDateTime(final LocalDateTime expected, final Date date, final TimeZone timeZone) {
1445         final ZonedDateTime zonedDateTime = DateUtils.toZonedDateTime(date);
1446         assertEquals(ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()), zonedDateTime,
1447                 () -> String.format("expected = %s, date = %s, timeZone = %s, TimeZone.getDefault() = %s", expected, date, timeZone, TimeZone.getDefault()));
1448         // Using atZone()
1449         assertEquals(date.toInstant().atZone(ZoneId.systemDefault()), zonedDateTime,
1450                 () -> String.format("expected = %s, date = %s, timeZone = %s, TimeZone.getDefault() = %s", expected, date, timeZone, TimeZone.getDefault()));
1451     }
1452 
1453     @ParameterizedTest
1454     @MethodSource("testToLocalDateTimeTimeZone")
1455     @DefaultLocale(language = "en", country = "US")
1456     @DefaultTimeZone(TimeZones.GMT_ID)
1457     void testToZonedDateTimeTimeZone(final LocalDateTime expected, final Date date, final TimeZone timeZone) {
1458         final ZonedDateTime zonedDateTime = DateUtils.toZonedDateTime(date, timeZone);
1459         assertEquals(expected, zonedDateTime.toOffsetDateTime().toLocalDateTime(),
1460                 () -> String.format("expected = %s, date = %s, timeZone = %s, TimeZone.getDefault() = %s", expected, date, timeZone, TimeZone.getDefault()));
1461         // Using atZone()
1462         assertEquals(date.toInstant().atZone(TimeZones.toTimeZone(timeZone).toZoneId()), zonedDateTime,
1463                 () -> String.format("expected = %s, date = %s, timeZone = %s, TimeZone.getDefault() = %s", expected, date, timeZone, TimeZone.getDefault()));
1464     }
1465 
1466     /**
1467      * Tests various values with the trunc method.
1468      *
1469      * @throws Exception Thrown if an operation in the test fails.
1470      */
1471     @Test
1472     void testTruncate() throws Exception {
1473         // tests public static Date truncate(Date date, int field)
1474         assertEquals(dateParser.parse("January 1, 2002"),
1475                 DateUtils.truncate(date1, Calendar.YEAR),
1476                 "truncate year-1 failed");
1477         assertEquals(dateParser.parse("January 1, 2001"),
1478                 DateUtils.truncate(date2, Calendar.YEAR),
1479                 "truncate year-2 failed");
1480         assertEquals(dateParser.parse("February 1, 2002"),
1481                 DateUtils.truncate(date1, Calendar.MONTH),
1482                 "truncate month-1 failed");
1483         assertEquals(dateParser.parse("November 1, 2001"),
1484                 DateUtils.truncate(date2, Calendar.MONTH),
1485                 "truncate month-2 failed");
1486         assertEquals(dateParser.parse("February 1, 2002"),
1487                 DateUtils.truncate(date1, DateUtils.SEMI_MONTH),
1488                 "truncate semimonth-1 failed");
1489         assertEquals(dateParser.parse("November 16, 2001"),
1490                 DateUtils.truncate(date2, DateUtils.SEMI_MONTH),
1491                 "truncate semimonth-2 failed");
1492         assertEquals(dateParser.parse("February 12, 2002"),
1493                 DateUtils.truncate(date1, Calendar.DATE),
1494                 "truncate date-1 failed");
1495         assertEquals(dateParser.parse("November 18, 2001"),
1496                 DateUtils.truncate(date2, Calendar.DATE),
1497                 "truncate date-2 failed");
1498         assertEquals(dateTimeParser.parse("February 12, 2002 12:00:00.000"),
1499                 DateUtils.truncate(date1, Calendar.HOUR),
1500                 "truncate hour-1 failed");
1501         assertEquals(dateTimeParser.parse("November 18, 2001 1:00:00.000"),
1502                 DateUtils.truncate(date2, Calendar.HOUR),
1503                 "truncate hour-2 failed");
1504         assertEquals(dateTimeParser.parse("February 12, 2002 12:34:00.000"),
1505                 DateUtils.truncate(date1, Calendar.MINUTE),
1506                 "truncate minute-1 failed");
1507         assertEquals(dateTimeParser.parse("November 18, 2001 1:23:00.000"),
1508                 DateUtils.truncate(date2, Calendar.MINUTE),
1509                 "truncate minute-2 failed");
1510         assertEquals(dateTimeParser.parse("February 12, 2002 12:34:56.000"),
1511                 DateUtils.truncate(date1, Calendar.SECOND),
1512                 "truncate second-1 failed");
1513         assertEquals(dateTimeParser.parse("November 18, 2001 1:23:11.000"),
1514                 DateUtils.truncate(date2, Calendar.SECOND),
1515                 "truncate second-2 failed");
1516         assertEquals(dateTimeParser.parse("February 3, 2002 00:00:00.000"),
1517                 DateUtils.truncate(dateAmPm1, Calendar.AM_PM),
1518                 "truncate ampm-1 failed");
1519         assertEquals(dateTimeParser.parse("February 3, 2002 00:00:00.000"),
1520                 DateUtils.truncate(dateAmPm2, Calendar.AM_PM),
1521                 "truncate ampm-2 failed");
1522         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"),
1523                 DateUtils.truncate(dateAmPm3, Calendar.AM_PM),
1524                 "truncate ampm-3 failed");
1525         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"),
1526                 DateUtils.truncate(dateAmPm4, Calendar.AM_PM),
1527                 "truncate ampm-4 failed");
1528 
1529         // tests public static Date truncate(Object date, int field)
1530         assertEquals(dateParser.parse("January 1, 2002"),
1531                 DateUtils.truncate((Object) date1, Calendar.YEAR),
1532                 "truncate year-1 failed");
1533         assertEquals(dateParser.parse("January 1, 2001"),
1534                 DateUtils.truncate((Object) date2, Calendar.YEAR),
1535                 "truncate year-2 failed");
1536         assertEquals(dateParser.parse("February 1, 2002"),
1537                 DateUtils.truncate((Object) date1, Calendar.MONTH),
1538                 "truncate month-1 failed");
1539         assertEquals(dateParser.parse("November 1, 2001"),
1540                 DateUtils.truncate((Object) date2, Calendar.MONTH),
1541                 "truncate month-2 failed");
1542         assertEquals(dateParser.parse("February 1, 2002"),
1543                 DateUtils.truncate((Object) date1, DateUtils.SEMI_MONTH),
1544                 "truncate semimonth-1 failed");
1545         assertEquals(dateParser.parse("November 16, 2001"),
1546                 DateUtils.truncate((Object) date2, DateUtils.SEMI_MONTH),
1547                 "truncate semimonth-2 failed");
1548         assertEquals(dateParser.parse("February 12, 2002"),
1549                 DateUtils.truncate((Object) date1, Calendar.DATE),
1550                 "truncate date-1 failed");
1551         assertEquals(dateParser.parse("November 18, 2001"),
1552                 DateUtils.truncate((Object) date2, Calendar.DATE),
1553                 "truncate date-2 failed");
1554         assertEquals(dateTimeParser.parse("February 12, 2002 12:00:00.000"),
1555                 DateUtils.truncate((Object) date1, Calendar.HOUR),
1556                 "truncate hour-1 failed");
1557         assertEquals(dateTimeParser.parse("November 18, 2001 1:00:00.000"),
1558                 DateUtils.truncate((Object) date2, Calendar.HOUR),
1559                 "truncate hour-2 failed");
1560         assertEquals(dateTimeParser.parse("February 12, 2002 12:34:00.000"),
1561                 DateUtils.truncate((Object) date1, Calendar.MINUTE),
1562                 "truncate minute-1 failed");
1563         assertEquals(dateTimeParser.parse("November 18, 2001 1:23:00.000"),
1564                 DateUtils.truncate((Object) date2, Calendar.MINUTE),
1565                 "truncate minute-2 failed");
1566         assertEquals(dateTimeParser.parse("February 12, 2002 12:34:56.000"),
1567                 DateUtils.truncate((Object) date1, Calendar.SECOND),
1568                 "truncate second-1 failed");
1569         assertEquals(dateTimeParser.parse("November 18, 2001 1:23:11.000"),
1570                 DateUtils.truncate((Object) date2, Calendar.SECOND),
1571                 "truncate second-2 failed");
1572         assertEquals(dateTimeParser.parse("February 3, 2002 00:00:00.000"),
1573                 DateUtils.truncate((Object) dateAmPm1, Calendar.AM_PM),
1574                 "truncate ampm-1 failed");
1575         assertEquals(dateTimeParser.parse("February 3, 2002 00:00:00.000"),
1576                 DateUtils.truncate((Object) dateAmPm2, Calendar.AM_PM),
1577                 "truncate ampm-2 failed");
1578         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"),
1579                 DateUtils.truncate((Object) dateAmPm3, Calendar.AM_PM),
1580                 "truncate ampm-3 failed");
1581         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"),
1582                 DateUtils.truncate((Object) dateAmPm4, Calendar.AM_PM),
1583                 "truncate ampm-4 failed");
1584 
1585         assertEquals(dateTimeParser.parse("February 12, 2002 12:34:56.000"),
1586                 DateUtils.truncate((Object) cal1, Calendar.SECOND),
1587                 "truncate calendar second-1 failed");
1588         assertEquals(dateTimeParser.parse("November 18, 2001 1:23:11.000"),
1589                 DateUtils.truncate((Object) cal2, Calendar.SECOND),
1590                 "truncate calendar second-2 failed");
1591 
1592         assertEquals(dateTimeParser.parse("February 3, 2002 00:00:00.000"),
1593                 DateUtils.truncate((Object) calAmPm1, Calendar.AM_PM),
1594                 "truncate ampm-1 failed");
1595         assertEquals(dateTimeParser.parse("February 3, 2002 00:00:00.000"),
1596                 DateUtils.truncate((Object) calAmPm2, Calendar.AM_PM),
1597                 "truncate ampm-2 failed");
1598         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"),
1599                 DateUtils.truncate((Object) calAmPm3, Calendar.AM_PM),
1600                 "truncate ampm-3 failed");
1601         assertEquals(dateTimeParser.parse("February 3, 2002 12:00:00.000"),
1602                 DateUtils.truncate((Object) calAmPm4, Calendar.AM_PM),
1603                 "truncate ampm-4 failed");
1604 
1605         assertNullPointerException(() -> DateUtils.truncate((Date) null, Calendar.SECOND));
1606         assertNullPointerException(() -> DateUtils.truncate((Calendar) null, Calendar.SECOND));
1607         assertNullPointerException(() -> DateUtils.truncate((Object) null, Calendar.SECOND));
1608         assertThrows(ClassCastException.class, () -> DateUtils.truncate("", Calendar.SECOND));
1609 
1610     }
1611 
1612     /**
1613      * Tests various values with the trunc method
1614      *
1615      * @throws Exception Thrown if an operation in the test fails.
1616      */
1617     @Test
1618     void testTruncate_Bugzilla_25560() throws Exception {
1619         // Fix for https://issues.apache.org/bugzilla/show_bug.cgi?id=25560
1620         // Test truncate across beginning of daylight saving time
1621         TimeZone.setDefault(TIME_ZONE_MET);
1622         dateTimeParser.setTimeZone(TIME_ZONE_MET);
1623         assertEquals(dateTimeParser.parse("March 30, 2003 00:00:00.000"), DateUtils.truncate(date3, Calendar.DATE), "truncate MET date across DST change-over");
1624         assertEquals(dateTimeParser.parse("March 30, 2003 00:00:00.000"), DateUtils.truncate((Object) cal3, Calendar.DATE),
1625                 "truncate MET date across DST change-over");
1626         // Test truncate across end of daylight saving time
1627         assertEquals(dateTimeParser.parse("October 26, 2003 00:00:00.000"), DateUtils.truncate(date8, Calendar.DATE),
1628                 "truncate MET date across DST change-over");
1629         assertEquals(dateTimeParser.parse("October 26, 2003 00:00:00.000"), DateUtils.truncate((Object) cal8, Calendar.DATE),
1630                 "truncate MET date across DST change-over");
1631     }
1632 
1633     /**
1634      * Tests various values with the trunc method
1635      *
1636      * @throws Exception Thrown if an operation in the test fails.
1637      */
1638     @Test
1639     void testTruncate_Bugzilla_31395() throws Exception {
1640         // Bug 31395, large dates
1641         final Date endOfTime = MAX_DATE; // fyi: Sun Aug 17 07:12:55 CET 292278994 -- 807 millis
1642         final GregorianCalendar endCal = new GregorianCalendar();
1643         endCal.setTime(endOfTime);
1644         assertThrows(ArithmeticException.class, () -> DateUtils.truncate(endCal, Calendar.DATE));
1645         endCal.set(Calendar.YEAR, 280000001);
1646         assertThrows(ArithmeticException.class, () -> DateUtils.truncate(endCal, Calendar.DATE));
1647         endCal.set(Calendar.YEAR, 280000000);
1648         final Calendar cal = DateUtils.truncate(endCal, Calendar.DATE);
1649         assertEquals(0, cal.get(Calendar.HOUR));
1650     }
1651 
1652     @Test
1653     public void testTruncatedCompareToCalendar() {
1654         final Calendar cal1 = Calendar.getInstance();
1655         cal1.set(2026, Calendar.JULY, 26, 10, 0, 0);
1656         final Calendar cal2 = Calendar.getInstance();
1657         cal2.set(2026, Calendar.JULY, 26, 14, 0, 0);
1658 
1659         assertEquals(0, DateUtils.truncatedCompareTo(cal1, cal2, Calendar.DAY_OF_MONTH));
1660         assertTrue(DateUtils.truncatedCompareTo(cal1, cal2, Calendar.HOUR_OF_DAY) < 0);
1661         assertTrue(DateUtils.truncatedCompareTo(cal2, cal1, Calendar.HOUR_OF_DAY) > 0);
1662 
1663         assertThrows(NullPointerException.class, () -> DateUtils.truncatedCompareTo(null, cal2, Calendar.DAY_OF_MONTH));
1664         assertThrows(NullPointerException.class, () -> DateUtils.truncatedCompareTo(cal1, null, Calendar.DAY_OF_MONTH));
1665     }
1666 
1667     @Test
1668     public void testTruncatedCompareToDate() {
1669         final Calendar cal1 = Calendar.getInstance();
1670         cal1.set(2026, Calendar.JULY, 26, 10, 0, 0);
1671         final Calendar cal2 = Calendar.getInstance();
1672         cal2.set(2026, Calendar.JULY, 26, 14, 0, 0);
1673 
1674         final Date date1 = cal1.getTime();
1675         final Date date2 = cal2.getTime();
1676 
1677         assertEquals(0, DateUtils.truncatedCompareTo(date1, date2, Calendar.DAY_OF_MONTH));
1678 
1679         assertTrue(DateUtils.truncatedCompareTo(date1, date2, Calendar.HOUR_OF_DAY) < 0);
1680         assertTrue(DateUtils.truncatedCompareTo(date2, date1, Calendar.HOUR_OF_DAY) > 0);
1681 
1682         assertThrows(NullPointerException.class, () -> DateUtils.truncatedCompareTo(null, date2, Calendar.DAY_OF_MONTH));
1683         assertThrows(NullPointerException.class, () -> DateUtils.truncatedCompareTo(date1, null, Calendar.DAY_OF_MONTH));
1684     }
1685 
1686     @Test
1687     public void testTruncatedEqualCalendar() {
1688         final Calendar cal1 = Calendar.getInstance();
1689         cal1.set(2026, Calendar.JULY, 26, 10, 0, 0);
1690         final Calendar cal2 = Calendar.getInstance();
1691         cal2.set(2026, Calendar.JULY, 26, 14, 0, 0);
1692 
1693         assertTrue(DateUtils.truncatedEquals(cal1, cal2, Calendar.DAY_OF_MONTH));
1694         assertFalse(DateUtils.truncatedEquals(cal1, cal2, Calendar.HOUR_OF_DAY));
1695 
1696         assertThrows(NullPointerException.class, () -> DateUtils.truncatedEquals(null, cal2, Calendar.DAY_OF_MONTH));
1697         assertThrows(NullPointerException.class, () -> DateUtils.truncatedEquals(cal1, null, Calendar.DAY_OF_MONTH));
1698     }
1699 
1700     @Test
1701     public void testTruncatedEqualDate() {
1702         final Calendar cal1 = Calendar.getInstance();
1703         cal1.set(2026, Calendar.JULY, 26, 10, 0, 0);
1704         final Calendar cal2 = Calendar.getInstance();
1705         cal2.set(2026, Calendar.JULY, 26, 14, 0, 0);
1706 
1707         final Date date1 = cal1.getTime();
1708         final Date date2 = cal2.getTime();
1709 
1710         assertTrue(DateUtils.truncatedEquals(date1, date2, Calendar.DAY_OF_MONTH));
1711         assertFalse(DateUtils.truncatedEquals(date1, date2, Calendar.HOUR_OF_DAY));
1712 
1713         assertThrows(NullPointerException.class, () -> DateUtils.truncatedEquals(null, date2, Calendar.DAY_OF_MONTH));
1714         assertThrows(NullPointerException.class, () -> DateUtils.truncatedEquals(date1, null, Calendar.DAY_OF_MONTH));
1715     }
1716 
1717     /**
1718      * Tests for LANG-59
1719      *
1720      * see https://issues.apache.org/jira/browse/LANG-59
1721      */
1722     @Test
1723     void testTruncateLang59() {
1724         // Set TimeZone to Mountain Time
1725         final TimeZone denverZone = TimeZones.getTimeZone("America/Denver");
1726         TimeZone.setDefault(denverZone);
1727         final DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS XXX");
1728         format.setTimeZone(denverZone);
1729         final Date oct31_01MDT = new Date(1099206000000L);
1730         final Date oct31MDT = new Date(oct31_01MDT.getTime() - 3600000L); // - 1 hour
1731         final Date oct31_01_02MDT = new Date(oct31_01MDT.getTime() + 120000L); // + 2 minutes
1732         final Date oct31_01_02_03MDT = new Date(oct31_01_02MDT.getTime() + 3000L); // + 3 seconds
1733         final Date oct31_01_02_03_04MDT = new Date(oct31_01_02_03MDT.getTime() + 4L); // + 4 milliseconds
1734         assertEquals("2004-10-31 00:00:00.000 -06:00", format.format(oct31MDT), "Check 00:00:00.000");
1735         assertEquals("2004-10-31 01:00:00.000 -06:00", format.format(oct31_01MDT), "Check 01:00:00.000");
1736         assertEquals("2004-10-31 01:02:00.000 -06:00", format.format(oct31_01_02MDT), "Check 01:02:00.000");
1737         assertEquals("2004-10-31 01:02:03.000 -06:00", format.format(oct31_01_02_03MDT), "Check 01:02:03.000");
1738         assertEquals("2004-10-31 01:02:03.004 -06:00", format.format(oct31_01_02_03_04MDT), "Check 01:02:03.004");
1739         // Demonstrate Problem
1740         final Calendar gval = Calendar.getInstance();
1741         gval.setTime(new Date(oct31_01MDT.getTime()));
1742         gval.set(Calendar.MINUTE, gval.get(Calendar.MINUTE)); // set minutes to the same value
1743         assertEquals(gval.getTime().getTime(), oct31_01MDT.getTime() + 3600000L, "Demonstrate Problem");
1744         // Test Truncate
1745         assertEquals(oct31_01_02_03_04MDT, DateUtils.truncate(oct31_01_02_03_04MDT, Calendar.MILLISECOND), "Truncate Calendar.MILLISECOND");
1746         assertEquals(oct31_01_02_03MDT, DateUtils.truncate(oct31_01_02_03_04MDT, Calendar.SECOND), "Truncate Calendar.SECOND");
1747         assertEquals(oct31_01_02MDT, DateUtils.truncate(oct31_01_02_03_04MDT, Calendar.MINUTE), "Truncate Calendar.MINUTE");
1748         assertEquals(oct31_01MDT, DateUtils.truncate(oct31_01_02_03_04MDT, Calendar.HOUR_OF_DAY), "Truncate Calendar.HOUR_OF_DAY");
1749         assertEquals(oct31_01MDT, DateUtils.truncate(oct31_01_02_03_04MDT, Calendar.HOUR), "Truncate Calendar.HOUR");
1750         assertEquals(oct31MDT, DateUtils.truncate(oct31_01_02_03_04MDT, Calendar.DATE), "Truncate Calendar.DATE");
1751         // Test Round (down)
1752         assertEquals(oct31_01_02_03_04MDT, DateUtils.round(oct31_01_02_03_04MDT, Calendar.MILLISECOND), "Round Calendar.MILLISECOND");
1753         assertEquals(oct31_01_02_03MDT, DateUtils.round(oct31_01_02_03_04MDT, Calendar.SECOND), "Round Calendar.SECOND");
1754         assertEquals(oct31_01_02MDT, DateUtils.round(oct31_01_02_03_04MDT, Calendar.MINUTE), "Round Calendar.MINUTE");
1755         assertEquals(oct31_01MDT, DateUtils.round(oct31_01_02_03_04MDT, Calendar.HOUR_OF_DAY), "Round Calendar.HOUR_OF_DAY");
1756         assertEquals(oct31_01MDT, DateUtils.round(oct31_01_02_03_04MDT, Calendar.HOUR), "Round Calendar.HOUR");
1757         assertEquals(oct31MDT, DateUtils.round(oct31_01_02_03_04MDT, Calendar.DATE), "Round Calendar.DATE");
1758     }
1759 
1760     /**
1761      * Tests the calendar iterator for week ranges
1762      */
1763     @Test
1764     void testWeekIterator() {
1765         final Calendar now = Calendar.getInstance();
1766         for (int i = 0; i < 7; i++) {
1767             final Calendar today = DateUtils.truncate(now, Calendar.DATE);
1768             final Calendar sunday = DateUtils.truncate(now, Calendar.DATE);
1769             sunday.add(Calendar.DATE, 1 - sunday.get(Calendar.DAY_OF_WEEK));
1770             final Calendar monday = DateUtils.truncate(now, Calendar.DATE);
1771             if (monday.get(Calendar.DAY_OF_WEEK) == 1) {
1772                 // This is sunday... roll back 6 days
1773                 monday.add(Calendar.DATE, -6);
1774             } else {
1775                 monday.add(Calendar.DATE, 2 - monday.get(Calendar.DAY_OF_WEEK));
1776             }
1777             final Calendar centered = DateUtils.truncate(now, Calendar.DATE);
1778             centered.add(Calendar.DATE, -3);
1779 
1780             Iterator<?> it = DateUtils.iterator(now, DateUtils.RANGE_WEEK_SUNDAY);
1781             assertWeekIterator(it, sunday);
1782             it = DateUtils.iterator(now, DateUtils.RANGE_WEEK_MONDAY);
1783             assertWeekIterator(it, monday);
1784             it = DateUtils.iterator(now, DateUtils.RANGE_WEEK_RELATIVE);
1785             assertWeekIterator(it, today);
1786             it = DateUtils.iterator(now, DateUtils.RANGE_WEEK_CENTER);
1787             assertWeekIterator(it, centered);
1788 
1789             it = DateUtils.iterator((Object) now, DateUtils.RANGE_WEEK_CENTER);
1790             assertWeekIterator(it, centered);
1791             final Iterator<?> it2 = DateUtils.iterator((Object) now.getTime(), DateUtils.RANGE_WEEK_CENTER);
1792             assertWeekIterator(it2, centered);
1793             assertThrows(NoSuchElementException.class, it2::next);
1794             final Iterator<?> it3 = DateUtils.iterator(now, DateUtils.RANGE_WEEK_CENTER);
1795             it3.next();
1796             assertThrows(UnsupportedOperationException.class, it3::remove);
1797 
1798             now.add(Calendar.DATE, 1);
1799         }
1800     }
1801 }
1802