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.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertNotEquals;
21  
22  import java.text.DateFormat;
23  import java.text.SimpleDateFormat;
24  import java.util.Calendar;
25  import java.util.Date;
26  import java.util.Locale;
27  
28  import org.apache.commons.lang3.AbstractLangTest;
29  import org.junit.jupiter.api.BeforeEach;
30  import org.junit.jupiter.api.Test;
31  
32  /**
33   * These Unit-tests will check all possible extremes when using some rounding-methods of DateUtils.
34   * The extremes are tested at the switch-point in milliseconds
35   *
36   * According to the implementation SEMI_MONTH will either round/truncate to the 1st or 16th
37   * When rounding Calendar.MONTH it depends on the number of days within that month.
38   * A month with 28 days will be rounded up from the 15th
39   * A month with 29 or 30 days will be rounded up from the 16th
40   * A month with 31 days will be rounded up from the 17th
41   */
42  class DateUtilsRoundingTest extends AbstractLangTest {
43  
44      DateFormat dateTimeParser;
45  
46      Date januaryOneDate;
47      Date targetYearDate;
48      //No targetMonths, these must be tested for every type of month(28-31 days)
49      Date targetDateDate;
50      Date targetDayOfMonthDate;
51      Date targetAmDate;
52      Date targetPmDate;
53      Date targetHourOfDayDate;
54      Date targetHourDate;
55      Date targetMinuteDate;
56      Date targetSecondDate;
57      Date targetMilliSecondDate;
58  
59      Calendar januaryOneCalendar;
60      @SuppressWarnings("deprecation")
61      FastDateFormat fdf = DateFormatUtils.ISO_DATETIME_FORMAT;
62  
63      /**
64       * When using this basetest all extremes are tested.<br>
65       * It will test the Date, Calendar and Object-implementation<br>
66       * lastRoundDownDate should round down to roundedDownDate<br>
67       * lastRoundDownDate + 1 millisecond should round up to roundedUpDate
68       *
69       * @param roundedUpDate the next rounded date after <strong>roundedDownDate</strong> when using <strong>calendarField</strong>
70       * @param roundedDownDate the result if <strong>lastRoundDownDate</strong> was rounded with <strong>calendarField</strong>
71       * @param lastRoundDownDate rounding this value with <strong>calendarField</strong> will result in <strong>roundedDownDate</strong>
72       * @param calendarField a Calendar.field value
73       */
74      protected void baseRoundTest(final Date roundedUpDate, final Date roundedDownDate, final Date lastRoundDownDate, final int calendarField) {
75          final Date firstRoundUpDate = DateUtils.addMilliseconds(lastRoundDownDate, 1);
76  
77          // Date-comparison
78          assertEquals(roundedDownDate, DateUtils.round(roundedDownDate, calendarField));
79          assertEquals(roundedUpDate, DateUtils.round(roundedUpDate, calendarField));
80          assertEquals(roundedDownDate, DateUtils.round(lastRoundDownDate, calendarField));
81          assertEquals(roundedUpDate, DateUtils.round(firstRoundUpDate, calendarField));
82  
83          // Calendar-initiations
84          final Calendar roundedUpCalendar;
85          final Calendar roundedDownCalendar;
86          final Calendar lastRoundDownCalendar;
87          final Calendar firstRoundUpCalendar;
88          roundedDownCalendar = Calendar.getInstance();
89          roundedUpCalendar = Calendar.getInstance();
90          lastRoundDownCalendar = Calendar.getInstance();
91          firstRoundUpCalendar = Calendar.getInstance();
92          roundedDownCalendar.setTime(roundedDownDate);
93          roundedUpCalendar.setTime(roundedUpDate);
94          lastRoundDownCalendar.setTime(lastRoundDownDate);
95          firstRoundUpCalendar.setTime(firstRoundUpDate);
96  
97          // Calendar-comparison
98          assertEquals(roundedDownCalendar, DateUtils.round(roundedDownCalendar, calendarField));
99          assertEquals(roundedUpCalendar, DateUtils.round(roundedUpCalendar, calendarField));
100         assertEquals(roundedDownCalendar, DateUtils.round(lastRoundDownCalendar, calendarField));
101         assertEquals(roundedUpCalendar, DateUtils.round(firstRoundUpCalendar, calendarField));
102 
103         // Object-comparison
104         assertEquals(roundedDownDate, DateUtils.round((Object) roundedDownDate, calendarField));
105         assertEquals(roundedUpDate, DateUtils.round((Object) roundedUpDate, calendarField));
106         assertEquals(roundedDownDate, DateUtils.round((Object) lastRoundDownDate, calendarField));
107         assertEquals(roundedUpDate, DateUtils.round((Object) firstRoundUpDate, calendarField));
108         assertEquals(roundedDownDate, DateUtils.round((Object) roundedDownCalendar, calendarField));
109         assertEquals(roundedUpDate, DateUtils.round((Object) roundedUpCalendar, calendarField));
110         assertEquals(roundedDownDate, DateUtils.round((Object) lastRoundDownDate, calendarField));
111         assertEquals(roundedUpDate, DateUtils.round((Object) firstRoundUpDate, calendarField));
112     }
113 
114     /**
115      * When using this basetest all extremes are tested.<br>
116      * It will test the Date, Calendar and Object-implementation<br>
117      * lastTruncateDate should round down to truncatedDate<br>
118      * lastTruncateDate + 1 millisecond should never round down to truncatedDate
119      *
120      * @param truncatedDate expected Date when <strong>lastTruncateDate</strong> is truncated with <strong>calendarField</strong>
121      * @param lastTruncateDate the last possible Date which will truncate to <strong>truncatedDate</strong> with <strong>calendarField</strong>
122      * @param calendarField a Calendar.field value
123      */
124     protected void baseTruncateTest(final Date truncatedDate, final Date lastTruncateDate, final int calendarField) {
125         final Date nextTruncateDate = DateUtils.addMilliseconds(lastTruncateDate, 1);
126 
127         // Date-comparison
128         assertEquals(truncatedDate, DateUtils.truncate(truncatedDate, calendarField),
129                 "Truncating " + fdf.format(truncatedDate) + " as Date with CalendarField-value " + calendarField + " must return itself");
130         assertEquals(truncatedDate, DateUtils.truncate(lastTruncateDate, calendarField));
131         assertNotEquals(truncatedDate, DateUtils.truncate(nextTruncateDate, calendarField),
132                 fdf.format(lastTruncateDate) + " is not an extreme when truncating as Date with CalendarField-value " + calendarField);
133 
134         // Calendar-initiations
135         final Calendar truncatedCalendar;
136         final Calendar lastTruncateCalendar;
137         final Calendar nextTruncateCalendar;
138         truncatedCalendar = Calendar.getInstance();
139         lastTruncateCalendar = Calendar.getInstance();
140         nextTruncateCalendar = Calendar.getInstance();
141         truncatedCalendar.setTime(truncatedDate);
142         lastTruncateCalendar.setTime(lastTruncateDate);
143         nextTruncateCalendar.setTime(nextTruncateDate);
144 
145         // Calendar-comparison
146         assertEquals(truncatedCalendar, DateUtils.truncate(truncatedCalendar, calendarField),
147                 "Truncating " + fdf.format(truncatedCalendar) + " as Calendar with CalendarField-value " + calendarField + " must return itself");
148         assertEquals(truncatedCalendar, DateUtils.truncate(lastTruncateCalendar, calendarField));
149         assertNotEquals(truncatedCalendar, DateUtils.truncate(nextTruncateCalendar, calendarField),
150                 fdf.format(lastTruncateCalendar) + " is not an extreme when truncating as Calendar with CalendarField-value " + calendarField);
151 
152         // Object-comparison
153         assertEquals(truncatedDate, DateUtils.truncate((Object) truncatedDate, calendarField), "Truncating " + fdf.format(truncatedDate)
154                 + " as Date cast to Object with CalendarField-value " + calendarField + " must return itself as Date");
155         assertEquals(truncatedDate, DateUtils.truncate((Object) lastTruncateDate, calendarField));
156         assertNotEquals(truncatedDate, DateUtils.truncate((Object) nextTruncateDate, calendarField),
157                 fdf.format(lastTruncateDate) + " is not an extreme when truncating as Date cast to Object with CalendarField-value " + calendarField);
158         assertEquals(truncatedDate, DateUtils.truncate((Object) truncatedCalendar, calendarField), "Truncating " + fdf.format(truncatedCalendar)
159                 + " as Calendar cast to Object with CalendarField-value " + calendarField + " must return itself as Date");
160         assertEquals(truncatedDate, DateUtils.truncate((Object) lastTruncateCalendar, calendarField));
161         assertNotEquals(truncatedDate, DateUtils.truncate((Object) nextTruncateCalendar, calendarField),
162                 fdf.format(lastTruncateCalendar) + " is not an extreme when truncating as Calendar cast to Object with CalendarField-value " + calendarField);
163     }
164 
165     /**
166      *
167      * Any January 1 could be considered as the ultimate extreme.
168      * Instead of comparing the results if the input has a difference of 1 millisecond we check the output to be exactly January first.
169      *
170      * @param minDate the lower bound
171      * @param maxDate the upper bound
172      * @param calendarField a Calendar.field value
173      */
174     protected void roundToJanuaryFirst(final Date minDate, final Date maxDate, final int calendarField) {
175         assertEquals(januaryOneDate, DateUtils.round(januaryOneDate, calendarField),
176                 "Rounding " + fdf.format(januaryOneDate) + " as Date with CalendarField-value " + calendarField + " must return itself");
177         assertEquals(januaryOneDate, DateUtils.round(minDate, calendarField));
178         assertEquals(januaryOneDate, DateUtils.round(maxDate, calendarField));
179 
180         final Calendar minCalendar = Calendar.getInstance();
181         minCalendar.setTime(minDate);
182         final Calendar maxCalendar = Calendar.getInstance();
183         maxCalendar.setTime(maxDate);
184         assertEquals(januaryOneCalendar, DateUtils.round(januaryOneCalendar, calendarField),
185                 "Rounding " + fdf.format(januaryOneCalendar) + " as Date with CalendarField-value " + calendarField + " must return itself");
186         assertEquals(januaryOneCalendar, DateUtils.round(minCalendar, calendarField));
187         assertEquals(januaryOneCalendar, DateUtils.round(maxCalendar, calendarField));
188 
189         final Date toPrevRoundDate = DateUtils.addMilliseconds(minDate, -1);
190         final Date toNextRoundDate = DateUtils.addMilliseconds(maxDate, 1);
191         assertNotEquals(januaryOneDate, DateUtils.round(toPrevRoundDate, calendarField),
192                 fdf.format(minDate) + " is not an lower-extreme when rounding as Date with CalendarField-value " + calendarField);
193         assertNotEquals(januaryOneDate, DateUtils.round(toNextRoundDate, calendarField),
194                 fdf.format(maxDate) + " is not an upper-extreme when rounding as Date with CalendarField-value " + calendarField);
195 
196         final Calendar toPrevRoundCalendar = Calendar.getInstance();
197         toPrevRoundCalendar.setTime(toPrevRoundDate);
198         final Calendar toNextRoundCalendar = Calendar.getInstance();
199         toNextRoundCalendar.setTime(toNextRoundDate);
200         assertNotEquals(januaryOneDate, DateUtils.round(toPrevRoundDate, calendarField),
201                 fdf.format(minCalendar) + " is not an lower-extreme when rounding as Date with CalendarField-value " + calendarField);
202         assertNotEquals(januaryOneDate, DateUtils.round(toNextRoundDate, calendarField),
203                 fdf.format(maxCalendar) + " is not an upper-extreme when rounding as Date with CalendarField-value " + calendarField);
204     }
205 
206     @BeforeEach
207     public void setUp() throws Exception {
208 
209         dateTimeParser = new SimpleDateFormat("MMM dd, yyyy H:mm:ss.SSS", Locale.ENGLISH);
210 
211         targetYearDate = dateTimeParser.parse("January 1, 2007 0:00:00.000");
212         targetDateDate = targetDayOfMonthDate = dateTimeParser.parse("June 1, 2008 0:00:00.000");
213         targetAmDate = dateTimeParser.parse("June 1, 2008 0:00:00.000");
214         targetPmDate = dateTimeParser.parse("June 1, 2008 12:00:00.000");
215         targetHourDate = dateTimeParser.parse("June 1, 2008 8:00:00.000");
216         targetHourOfDayDate = dateTimeParser.parse("June 1, 2008 8:00:00.000");
217         targetMinuteDate = dateTimeParser.parse("June 1, 2008 8:15:00.000");
218         targetSecondDate = dateTimeParser.parse("June 1, 2008 8:15:14.000");
219         targetMilliSecondDate = dateTimeParser.parse("June 1, 2008 8:15:14.231");
220 
221         januaryOneDate = dateTimeParser.parse("January 1, 2008 0:00:00.000");
222         januaryOneCalendar = Calendar.getInstance();
223         januaryOneCalendar.setTime(januaryOneDate);
224     }
225 
226     /**
227      * Tests DateUtils.round()-method with Calendar.AM_PM
228      * Includes rounding the extremes of both AM and PM of one day
229      * Includes rounding to January 1
230      *
231      * @throws Exception so we don't have to catch it
232      */
233     @Test
234     void testRoundAmPm() throws Exception {
235         final int calendarField = Calendar.AM_PM;
236         // AM
237         Date roundedUpDate = dateTimeParser.parse("June 1, 2008 12:00:00.000");
238         Date roundedDownDate = targetAmDate;
239         Date lastRoundedDownDate = dateTimeParser.parse("June 1, 2008 5:59:59.999");
240         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
241 
242         // PM
243         roundedUpDate = dateTimeParser.parse("June 2, 2008 0:00:00.000");
244         roundedDownDate = targetPmDate;
245         lastRoundedDownDate = dateTimeParser.parse("June 1, 2008 17:59:59.999");
246         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
247 
248         // round to January 1
249         final Date minDate = dateTimeParser.parse("December 31, 2007 18:00:00.000");
250         final Date maxDate = dateTimeParser.parse("January 1, 2008 5:59:59.999");
251         roundToJanuaryFirst(minDate, maxDate, calendarField);
252     }
253 
254     /**
255      * Tests DateUtils.round()-method with Calendar.DATE
256      * Includes rounding the extremes of one day
257      * Includes rounding to January 1
258      *
259      * @throws Exception so we don't have to catch it
260      */
261     @Test
262     void testRoundDate() throws Exception {
263         final int calendarField = Calendar.DATE;
264         final Date roundedUpDate;
265         final Date roundedDownDate;
266         final Date lastRoundedDownDate;
267         final Date minDate;
268         final Date maxDate;
269 
270         roundedUpDate = dateTimeParser.parse("June 2, 2008 0:00:00.000");
271         roundedDownDate = targetDateDate;
272         lastRoundedDownDate = dateTimeParser.parse("June 1, 2008 11:59:59.999");
273         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
274 
275         // round to January 1
276         minDate = dateTimeParser.parse("December 31, 2007 12:00:00.000");
277         maxDate = dateTimeParser.parse("January 1, 2008 11:59:59.999");
278         roundToJanuaryFirst(minDate, maxDate, calendarField);
279     }
280 
281     /**
282      * Tests DateUtils.round()-method with Calendar.DAY_OF_MONTH
283      * Includes rounding the extremes of one day
284      * Includes rounding to January 1
285      *
286      * @throws Exception so we don't have to catch it
287      */
288     @Test
289     void testRoundDayOfMonth() throws Exception {
290         final int calendarField = Calendar.DAY_OF_MONTH;
291         final Date roundedUpDate;
292         final Date roundedDownDate;
293         final Date lastRoundedDownDate;
294         final Date minDate;
295         final Date maxDate;
296 
297         roundedUpDate = dateTimeParser.parse("June 2, 2008 0:00:00.000");
298         roundedDownDate = targetDayOfMonthDate;
299         lastRoundedDownDate = dateTimeParser.parse("June 1, 2008 11:59:59.999");
300         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
301 
302         // round to January 1
303         minDate = dateTimeParser.parse("December 31, 2007 12:00:00.000");
304         maxDate = dateTimeParser.parse("January 1, 2008 11:59:59.999");
305         roundToJanuaryFirst(minDate, maxDate, calendarField);
306     }
307 
308     /**
309      * Tests DateUtils.round()-method with Calendar.HOUR
310      * Includes rounding the extremes of one hour
311      * Includes rounding to January 1
312      *
313      * @throws Exception so we don't have to catch it
314      */
315     @Test
316     void testRoundHour() throws Exception {
317         final int calendarField = Calendar.HOUR;
318         final Date roundedUpDate;
319         final Date roundedDownDate;
320         final Date lastRoundedDownDate;
321         final Date minDate;
322         final Date maxDate;
323 
324         roundedUpDate = dateTimeParser.parse("June 1, 2008 9:00:00.000");
325         roundedDownDate = targetHourDate;
326         lastRoundedDownDate = dateTimeParser.parse("June 1, 2008 8:29:59.999");
327         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
328 
329         // round to January 1
330         minDate = dateTimeParser.parse("December 31, 2007 23:30:00.000");
331         maxDate = dateTimeParser.parse("January 1, 2008 0:29:59.999");
332         roundToJanuaryFirst(minDate, maxDate, calendarField);
333     }
334 
335     /**
336      * Tests DateUtils.round()-method with Calendar.HOUR_OF_DAY
337      * Includes rounding the extremes of one hour
338      * Includes rounding to January 1
339      *
340      * @throws Exception so we don't have to catch it
341      */
342     @Test
343     void testRoundHourOfDay() throws Exception {
344         final int calendarField = Calendar.HOUR_OF_DAY;
345         final Date roundedUpDate;
346         final Date roundedDownDate;
347         final Date lastRoundedDownDate;
348         final Date minDate;
349         final Date maxDate;
350 
351         roundedUpDate = dateTimeParser.parse("June 1, 2008 9:00:00.000");
352         roundedDownDate = targetHourOfDayDate;
353         lastRoundedDownDate = dateTimeParser.parse("June 1, 2008 8:29:59.999");
354         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
355 
356         // round to January 1
357         minDate = dateTimeParser.parse("December 31, 2007 23:30:00.000");
358         maxDate = dateTimeParser.parse("January 1, 2008 0:29:59.999");
359         roundToJanuaryFirst(minDate, maxDate, calendarField);
360     }
361 
362     /**
363      * Tests DateUtils.round()-method with Calendar.MILLISECOND
364      * Includes rounding the extremes of one second
365      * Includes rounding to January 1
366      *
367      * @throws Exception so we don't have to catch it
368      */
369     @Test
370     void testRoundMilliSecond() throws Exception {
371         final int calendarField = Calendar.MILLISECOND;
372         final Date roundedUpDate;
373         final Date roundedDownDate;
374         final Date lastRoundedDownDate;
375         final Date minDate;
376         final Date maxDate;
377 
378         roundedDownDate = lastRoundedDownDate = targetMilliSecondDate;
379         roundedUpDate = dateTimeParser.parse("June 1, 2008 8:15:14.232");
380         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
381 
382         // round to January 1
383         minDate = maxDate = januaryOneDate;
384         roundToJanuaryFirst(minDate, maxDate, calendarField);
385     }
386 
387     /**
388      * Tests DateUtils.round()-method with Calendar.MINUTE
389      * Includes rounding the extremes of one minute
390      * Includes rounding to January 1
391      *
392      * @throws Exception so we don't have to catch it
393      */
394     @Test
395     void testRoundMinute() throws Exception {
396         final int calendarField = Calendar.MINUTE;
397         final Date roundedUpDate;
398         final Date roundedDownDate;
399         final Date lastRoundedDownDate;
400         final Date minDate;
401         final Date maxDate;
402 
403         roundedUpDate = dateTimeParser.parse("June 1, 2008 8:16:00.000");
404         roundedDownDate = targetMinuteDate;
405         lastRoundedDownDate = dateTimeParser.parse("June 1, 2008 8:15:29.999");
406         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
407 
408         // round to January 1
409         minDate = dateTimeParser.parse("December 31, 2007 23:59:30.000");
410         maxDate = dateTimeParser.parse("January 1, 2008 0:00:29.999");
411         roundToJanuaryFirst(minDate, maxDate, calendarField);
412     }
413 
414     /**
415      * Tests DateUtils.round()-method with Calendar.MONTH
416      * Includes rounding months with 28, 29, 30 and 31 days
417      * Includes rounding to January 1
418      *
419      * @throws Exception so we don't have to catch it
420      */
421     @Test
422     void testRoundMonth() throws Exception {
423         final int calendarField = Calendar.MONTH;
424 
425         // month with 28 days
426         Date roundedUpDate = dateTimeParser.parse("March 1, 2007 0:00:00.000");
427         Date roundedDownDate = dateTimeParser.parse("February 1, 2007 0:00:00.000");
428         Date lastRoundedDownDate = dateTimeParser.parse("February 14, 2007 23:59:59.999");
429         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
430 
431         // month with 29 days
432         roundedUpDate = dateTimeParser.parse("March 1, 2008 0:00:00.000");
433         roundedDownDate = dateTimeParser.parse("February 1, 2008 0:00:00.000");
434         lastRoundedDownDate = dateTimeParser.parse("February 15, 2008 23:59:59.999");
435         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
436 
437         // month with 30 days
438         roundedUpDate = dateTimeParser.parse("May 1, 2008 0:00:00.000");
439         roundedDownDate = dateTimeParser.parse("April 1, 2008 0:00:00.000");
440         lastRoundedDownDate = dateTimeParser.parse("April 15, 2008 23:59:59.999");
441         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
442 
443         // month with 31 days
444         roundedUpDate = dateTimeParser.parse("June 1, 2008 0:00:00.000");
445         roundedDownDate = dateTimeParser.parse("May 1, 2008 0:00:00.000");
446         lastRoundedDownDate = dateTimeParser.parse("May 16, 2008 23:59:59.999");
447         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
448 
449         // round to January 1
450         final Date minDate = dateTimeParser.parse("December 17, 2007 00:00:00.000");
451         final Date maxDate = dateTimeParser.parse("January 16, 2008 23:59:59.999");
452         roundToJanuaryFirst(minDate, maxDate, calendarField);
453     }
454 
455     /**
456      * Tests DateUtils.round()-method with Calendar.SECOND
457      * Includes rounding the extremes of one second
458      * Includes rounding to January 1
459      *
460      * @throws Exception so we don't have to catch it
461      */
462     @Test
463     void testRoundSecond() throws Exception {
464         final int calendarField = Calendar.SECOND;
465         final Date roundedUpDate;
466         final Date roundedDownDate;
467         final Date lastRoundedDownDate;
468         final Date minDate;
469         final Date maxDate;
470 
471         roundedUpDate = dateTimeParser.parse("June 1, 2008 8:15:15.000");
472         roundedDownDate = targetSecondDate;
473         lastRoundedDownDate = dateTimeParser.parse("June 1, 2008 8:15:14.499");
474         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
475 
476         // round to January 1
477         minDate = dateTimeParser.parse("December 31, 2007 23:59:59.500");
478         maxDate = dateTimeParser.parse("January 1, 2008 0:00:00.499");
479         roundToJanuaryFirst(minDate, maxDate, calendarField);
480     }
481 
482     /**
483      * Tests DateUtils.round()-method with DateUtils.SEMI_MONTH
484      * Includes rounding months with 28, 29, 30 and 31 days, each with first and second half
485      * Includes rounding to January 1
486      *
487      * @throws Exception so we don't have to catch it
488      */
489     @Test
490     void testRoundSemiMonth() throws Exception {
491         final int calendarField = DateUtils.SEMI_MONTH;
492         // month with 28 days (1)
493         Date roundedUpDate = dateTimeParser.parse("February 16, 2007 0:00:00.000");
494         Date roundedDownDate = dateTimeParser.parse("February 1, 2007 0:00:00.000");
495         Date lastRoundedDownDate = dateTimeParser.parse("February 8, 2007 23:59:59.999");
496         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
497 
498         // month with 28 days (2)
499         roundedUpDate = dateTimeParser.parse("March 1, 2007 0:00:00.000");
500         roundedDownDate = dateTimeParser.parse("February 16, 2007 0:00:00.000");
501         lastRoundedDownDate = dateTimeParser.parse("February 23, 2007 23:59:59.999");
502         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
503 
504         // month with 29 days (1)
505         roundedUpDate = dateTimeParser.parse("February 16, 2008 0:00:00.000");
506         roundedDownDate = dateTimeParser.parse("February 1, 2008 0:00:00.000");
507         lastRoundedDownDate = dateTimeParser.parse("February 8, 2008 23:59:59.999");
508         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
509 
510         // month with 29 days (2)
511         roundedUpDate = dateTimeParser.parse("March 1, 2008 0:00:00.000");
512         roundedDownDate = dateTimeParser.parse("February 16, 2008 0:00:00.000");
513         lastRoundedDownDate = dateTimeParser.parse("February 23, 2008 23:59:59.999");
514         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
515 
516         // month with 30 days (1)
517         roundedUpDate = dateTimeParser.parse("April 16, 2008 0:00:00.000");
518         roundedDownDate = dateTimeParser.parse("April 1, 2008 0:00:00.000");
519         lastRoundedDownDate = dateTimeParser.parse("April 8, 2008 23:59:59.999");
520         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
521 
522         // month with 30 days (2)
523         roundedUpDate = dateTimeParser.parse("May 1, 2008 0:00:00.000");
524         roundedDownDate = dateTimeParser.parse("April 16, 2008 0:00:00.000");
525         lastRoundedDownDate = dateTimeParser.parse("April 23, 2008 23:59:59.999");
526         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
527 
528         // month with 31 days (1)
529         roundedUpDate = dateTimeParser.parse("May 16, 2008 0:00:00.000");
530         roundedDownDate = dateTimeParser.parse("May 1, 2008 0:00:00.000");
531         lastRoundedDownDate = dateTimeParser.parse("May 8, 2008 23:59:59.999");
532         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
533 
534         // month with 31 days (2)
535         roundedUpDate = dateTimeParser.parse("June 1, 2008 0:00:00.000");
536         roundedDownDate = dateTimeParser.parse("May 16, 2008 0:00:00.000");
537         lastRoundedDownDate = dateTimeParser.parse("May 23, 2008 23:59:59.999");
538         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
539 
540         // round to January 1
541         final Date minDate = dateTimeParser.parse("December 24, 2007 00:00:00.000");
542         final Date maxDate = dateTimeParser.parse("January 8, 2008 23:59:59.999");
543         roundToJanuaryFirst(minDate, maxDate, calendarField);
544     }
545 
546     /**
547      * Tests DateUtils.round()-method with Calendar.Year
548      *
549      * @throws Exception so we don't have to catch it
550      */
551     @Test
552     void testRoundYear() throws Exception {
553         final int calendarField = Calendar.YEAR;
554         final Date roundedUpDate = dateTimeParser.parse("January 1, 2008 0:00:00.000");
555         final Date roundedDownDate = targetYearDate;
556         final Date lastRoundedDownDate = dateTimeParser.parse("June 30, 2007 23:59:59.999");
557         baseRoundTest(roundedUpDate, roundedDownDate, lastRoundedDownDate, calendarField);
558     }
559 
560     /**
561      * Test DateUtils.truncate()-method with Calendar.AM_PM
562      * Includes truncating the extremes of both AM and PM of one day
563      *
564      * @throws Exception so we don't have to catch it
565      */
566     @Test
567     void testTruncateAmPm() throws Exception {
568         final int calendarField = Calendar.AM_PM;
569         // AM
570         Date lastTruncateDate = dateTimeParser.parse("June 1, 2008 11:59:59.999");
571         baseTruncateTest(targetAmDate, lastTruncateDate, calendarField);
572         // PM
573         lastTruncateDate = dateTimeParser.parse("June 1, 2008 23:59:59.999");
574         baseTruncateTest(targetPmDate, lastTruncateDate, calendarField);
575     }
576 
577     /**
578      * Test DateUtils.truncate()-method with Calendar.DATE
579      *
580      * @throws Exception so we don't have to catch it
581      */
582     @Test
583     void testTruncateDate() throws Exception {
584         final int calendarField = Calendar.DATE;
585         final Date lastTruncateDate = dateTimeParser.parse("June 1, 2008 23:59:59.999");
586         baseTruncateTest(targetDateDate, lastTruncateDate, calendarField);
587     }
588 
589     /**
590      * Test DateUtils.truncate()-method with Calendar.DAY_OF_MONTH
591      *
592      * @throws Exception so we don't have to catch it
593      */
594     @Test
595     void testTruncateDayOfMonth() throws Exception {
596         final int calendarField = Calendar.DAY_OF_MONTH;
597         final Date lastTruncateDate = dateTimeParser.parse("June 1, 2008 23:59:59.999");
598         baseTruncateTest(targetDayOfMonthDate, lastTruncateDate, calendarField);
599     }
600 
601     /**
602      * Test DateUtils.truncate()-method with Calendar.HOUR
603      *
604      * @throws Exception so we don't have to catch it
605      */
606     @Test
607     void testTruncateHour() throws Exception {
608         final int calendarField = Calendar.HOUR;
609         final Date lastTruncateDate = dateTimeParser.parse("June 1, 2008 8:59:59.999");
610         baseTruncateTest(targetHourDate, lastTruncateDate, calendarField);
611     }
612 
613     /**
614      * Test DateUtils.truncate()-method with Calendar.HOUR_OF_DAY
615      *
616      * @throws Exception so we don't have to catch it
617      */
618     @Test
619     void testTruncateHourOfDay() throws Exception {
620         final int calendarField = Calendar.HOUR_OF_DAY;
621         final Date lastTruncateDate = dateTimeParser.parse("June 1, 2008 8:59:59.999");
622         baseTruncateTest(targetHourOfDayDate, lastTruncateDate, calendarField);
623     }
624 
625     /**
626      * Test DateUtils.truncate()-method with Calendar.SECOND
627      */
628     @Test
629     void testTruncateMilliSecond() {
630         final int calendarField = Calendar.MILLISECOND;
631         baseTruncateTest(targetMilliSecondDate, targetMilliSecondDate, calendarField);
632     }
633 
634     /**
635      * Test DateUtils.truncate()-method with Calendar.MINUTE
636      *
637      * @throws Exception so we don't have to catch it
638      */
639     @Test
640     void testTruncateMinute() throws Exception {
641         final int calendarField = Calendar.MINUTE;
642         final Date lastTruncateDate = dateTimeParser.parse("June 1, 2008 8:15:59.999");
643         baseTruncateTest(targetMinuteDate, lastTruncateDate, calendarField);
644     }
645 
646     /**
647      * Test DateUtils.truncate()-method with Calendar.MONTH
648      *
649      * @throws Exception so we don't have to catch it
650      */
651     @Test
652     void testTruncateMonth() throws Exception {
653         final int calendarField = Calendar.MONTH;
654         final Date truncatedDate = dateTimeParser.parse("March 1, 2008 0:00:00.000");
655         final Date lastTruncateDate = dateTimeParser.parse("March 31, 2008 23:59:59.999");
656         baseTruncateTest(truncatedDate, lastTruncateDate, calendarField);
657     }
658 
659     /**
660      * Test DateUtils.truncate()-method with Calendar.SECOND
661      *
662      * @throws Exception so we don't have to catch it
663      */
664     @Test
665     void testTruncateSecond() throws Exception {
666         final int calendarField = Calendar.SECOND;
667         final Date lastTruncateDate = dateTimeParser.parse("June 1, 2008 8:15:14.999");
668         baseTruncateTest(targetSecondDate, lastTruncateDate, calendarField);
669     }
670 
671     /**
672      * Test DateUtils.truncate()-method with DateUtils.SEMI_MONTH
673      * Includes truncating months with 28, 29, 30 and 31 days, each with first and second half
674      *
675      * @throws Exception so we don't have to catch it
676      */
677     @Test
678     void testTruncateSemiMonth() throws Exception {
679         final int calendarField = DateUtils.SEMI_MONTH;
680         // month with 28 days (1)
681         Date truncatedDate = dateTimeParser.parse("February 1, 2007 0:00:00.000");
682         Date lastTruncateDate = dateTimeParser.parse("February 15, 2007 23:59:59.999");
683         baseTruncateTest(truncatedDate, lastTruncateDate, calendarField);
684 
685         // month with 28 days (2)
686         truncatedDate = dateTimeParser.parse("February 16, 2007 0:00:00.000");
687         lastTruncateDate = dateTimeParser.parse("February 28, 2007 23:59:59.999");
688         baseTruncateTest(truncatedDate, lastTruncateDate, calendarField);
689 
690         // month with 29 days (1)
691         truncatedDate = dateTimeParser.parse("February 1, 2008 0:00:00.000");
692         lastTruncateDate = dateTimeParser.parse("February 15, 2008 23:59:59.999");
693         baseTruncateTest(truncatedDate, lastTruncateDate, calendarField);
694 
695         // month with 29 days (2)
696         truncatedDate = dateTimeParser.parse("February 16, 2008 0:00:00.000");
697         lastTruncateDate = dateTimeParser.parse("February 29, 2008 23:59:59.999");
698         baseTruncateTest(truncatedDate, lastTruncateDate, calendarField);
699 
700         // month with 30 days (1)
701         truncatedDate = dateTimeParser.parse("April 1, 2008 0:00:00.000");
702         lastTruncateDate = dateTimeParser.parse("April 15, 2008 23:59:59.999");
703         baseTruncateTest(truncatedDate, lastTruncateDate, calendarField);
704 
705         // month with 30 days (2)
706         truncatedDate = dateTimeParser.parse("April 16, 2008 0:00:00.000");
707         lastTruncateDate = dateTimeParser.parse("April 30, 2008 23:59:59.999");
708         baseTruncateTest(truncatedDate, lastTruncateDate, calendarField);
709 
710         // month with 31 days (1)
711         truncatedDate = dateTimeParser.parse("March 1, 2008 0:00:00.000");
712         lastTruncateDate = dateTimeParser.parse("March 15, 2008 23:59:59.999");
713         baseTruncateTest(truncatedDate, lastTruncateDate, calendarField);
714 
715         // month with 31 days (2)
716         truncatedDate = dateTimeParser.parse("March 16, 2008 0:00:00.000");
717         lastTruncateDate = dateTimeParser.parse("March 31, 2008 23:59:59.999");
718         baseTruncateTest(truncatedDate, lastTruncateDate, calendarField);
719 
720     }
721 
722     /**
723      * Test DateUtils.truncate()-method with Calendar.YEAR
724      *
725      * @throws Exception so we don't have to catch it
726      */
727     @Test
728     void testTruncateYear() throws Exception {
729         final int calendarField = Calendar.YEAR;
730         final Date lastTruncateDate = dateTimeParser.parse("December 31, 2007 23:59:59.999");
731         baseTruncateTest(targetYearDate, lastTruncateDate, calendarField);
732     }
733 }