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