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 java.text.ParseException;
20  import java.text.ParsePosition;
21  import java.util.Calendar;
22  import java.util.Date;
23  import java.util.Iterator;
24  import java.util.Locale;
25  import java.util.NoSuchElementException;
26  import java.util.Objects;
27  import java.util.TimeZone;
28  import java.util.concurrent.TimeUnit;
29  
30  import org.apache.commons.lang3.LocaleUtils;
31  
32  /**
33   * A suite of utilities surrounding the use of the
34   * {@link java.util.Calendar} and {@link java.util.Date} object.
35   *
36   * <p>DateUtils contains a lot of common methods considering manipulations
37   * of Dates or Calendars. Some methods require some extra explanation.
38   * The truncate, ceiling and round methods could be considered the Math.floor(),
39   * Math.ceil() or Math.round versions for dates
40   * This way date-fields will be ignored in bottom-up order.
41   * As a complement to these methods we've introduced some fragment-methods.
42   * With these methods the Date-fields will be ignored in top-down order.
43   * Since a date without a year is not a valid date, you have to decide in what
44   * kind of date-field you want your result, for instance milliseconds or days.
45   * </p>
46   * <p>
47   * Several methods are provided for adding to {@link Date} objects, of the form
48   * {@code addXXX(Date date, int amount)}. It is important to note these methods
49   * use a {@link Calendar} internally (with default time zone and locale) and may
50   * be affected by changes to daylight saving time (DST).
51   * </p>
52   *
53   * @since 2.0
54   */
55  public class DateUtils {
56  
57      /**
58       * Date iterator.
59       */
60      static class DateIterator implements Iterator<Calendar> {
61          private final Calendar endFinal;
62          private final Calendar spot;
63  
64          /**
65           * Constructs a DateIterator that ranges from one date to another.
66           *
67           * @param startFinal start date (inclusive)
68           * @param endFinal end date (inclusive)
69           */
70          DateIterator(final Calendar startFinal, final Calendar endFinal) {
71              this.endFinal = endFinal;
72              spot = startFinal;
73              spot.add(Calendar.DATE, -1);
74          }
75  
76          /**
77           * Has the iterator not reached the end date yet?
78           *
79           * @return {@code true} if the iterator has yet to reach the end date
80           */
81          @Override
82          public boolean hasNext() {
83              return spot.before(endFinal);
84          }
85  
86          /**
87           * Returns the next calendar in the iteration
88           *
89           * @return Object calendar for the next date
90           */
91          @Override
92          public Calendar next() {
93              if (spot.equals(endFinal)) {
94                  throw new NoSuchElementException();
95              }
96              spot.add(Calendar.DATE, 1);
97              return (Calendar) spot.clone();
98          }
99  
100         /**
101          * Always throws UnsupportedOperationException.
102          *
103          * @throws UnsupportedOperationException Always thrown.
104          * @see java.util.Iterator#remove()
105          */
106         @Override
107         public void remove() {
108             throw new UnsupportedOperationException();
109         }
110     }
111     /**
112      * Calendar modification types.
113      */
114     private enum ModifyType {
115         /**
116          * Truncation.
117          */
118         TRUNCATE,
119 
120         /**
121          * Rounding.
122          */
123         ROUND,
124 
125         /**
126          * Ceiling.
127          */
128         CEILING
129     }
130     /**
131      * Number of milliseconds in a standard second.
132      * @since 2.1
133      */
134     public static final long MILLIS_PER_SECOND = 1000;
135     /**
136      * Number of milliseconds in a standard minute.
137      * @since 2.1
138      */
139     public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
140 
141     /**
142      * Number of milliseconds in a standard hour.
143      * @since 2.1
144      */
145     public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
146 
147     /**
148      * Number of milliseconds in a standard day.
149      * @since 2.1
150      */
151     public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
152 
153     /**
154      * This is half a month, so this represents whether a date is in the top
155      * or bottom half of the month.
156      */
157     public static final int SEMI_MONTH = 1001;
158     private static final int[][] fields = {
159             {Calendar.MILLISECOND},
160             {Calendar.SECOND},
161             {Calendar.MINUTE},
162             {Calendar.HOUR_OF_DAY, Calendar.HOUR},
163             {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM
164                 /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */
165             },
166             {Calendar.MONTH, SEMI_MONTH},
167             {Calendar.YEAR},
168             {Calendar.ERA}};
169     /**
170      * A week range, starting on Sunday.
171      */
172     public static final int RANGE_WEEK_SUNDAY = 1;
173     /**
174      * A week range, starting on Monday.
175      */
176     public static final int RANGE_WEEK_MONDAY = 2;
177     /**
178      * A week range, starting on the day focused.
179      */
180     public static final int RANGE_WEEK_RELATIVE = 3;
181     /**
182      * A week range, centered around the day focused.
183      */
184     public static final int RANGE_WEEK_CENTER = 4;
185 
186     /**
187      * A month range, the week starting on Sunday.
188      */
189     public static final int RANGE_MONTH_SUNDAY = 5;
190 
191     /**
192      * A month range, the week starting on Monday.
193      */
194     public static final int RANGE_MONTH_MONDAY = 6;
195 
196     /**
197      * Adds to a date returning a new object.
198      * The original {@link Date} is unchanged.
199      *
200      * @param date  the date, not null
201      * @param calendarField  the calendar field to add to
202      * @param amount  the amount to add, may be negative
203      * @return the new {@link Date} with the amount added
204      * @throws NullPointerException if the date is null
205      */
206     private static Date add(final Date date, final int calendarField, final int amount) {
207         validateDateNotNull(date);
208         final Calendar c = Calendar.getInstance();
209         c.setTime(date);
210         c.add(calendarField, amount);
211         return c.getTime();
212     }
213 
214     /**
215      * Adds a number of days to a date returning a new object.
216      * The original {@link Date} is unchanged.
217      *
218      * @param date  the date, not null
219      * @param amount  the amount to add, may be negative
220      * @return the new {@link Date} with the amount added
221      * @throws NullPointerException if the date is null
222      */
223     public static Date addDays(final Date date, final int amount) {
224         return add(date, Calendar.DAY_OF_MONTH, amount);
225     }
226 
227     /**
228      * Adds a number of hours to a date returning a new object.
229      * The original {@link Date} is unchanged.
230      *
231      * @param date  the date, not null
232      * @param amount  the amount to add, may be negative
233      * @return the new {@link Date} with the amount added
234      * @throws NullPointerException if the date is null
235      */
236     public static Date addHours(final Date date, final int amount) {
237         return add(date, Calendar.HOUR_OF_DAY, amount);
238     }
239 
240     /**
241      * Adds a number of milliseconds to a date returning a new object.
242      * The original {@link Date} is unchanged.
243      *
244      * @param date  the date, not null
245      * @param amount  the amount to add, may be negative
246      * @return the new {@link Date} with the amount added
247      * @throws NullPointerException if the date is null
248      */
249     public static Date addMilliseconds(final Date date, final int amount) {
250         return add(date, Calendar.MILLISECOND, amount);
251     }
252 
253     /**
254      * Adds a number of minutes to a date returning a new object.
255      * The original {@link Date} is unchanged.
256      *
257      * @param date  the date, not null
258      * @param amount  the amount to add, may be negative
259      * @return the new {@link Date} with the amount added
260      * @throws NullPointerException if the date is null
261      */
262     public static Date addMinutes(final Date date, final int amount) {
263         return add(date, Calendar.MINUTE, amount);
264     }
265 
266     /**
267      * Adds a number of months to a date returning a new object.
268      * The original {@link Date} is unchanged.
269      *
270      * @param date  the date, not null
271      * @param amount  the amount to add, may be negative
272      * @return the new {@link Date} with the amount added
273      * @throws NullPointerException if the date is null
274      */
275     public static Date addMonths(final Date date, final int amount) {
276         return add(date, Calendar.MONTH, amount);
277     }
278 
279     /**
280      * Adds a number of seconds to a date returning a new object.
281      * The original {@link Date} is unchanged.
282      *
283      * @param date  the date, not null
284      * @param amount  the amount to add, may be negative
285      * @return the new {@link Date} with the amount added
286      * @throws NullPointerException if the date is null
287      */
288     public static Date addSeconds(final Date date, final int amount) {
289         return add(date, Calendar.SECOND, amount);
290     }
291 
292     /**
293      * Adds a number of weeks to a date returning a new object.
294      * The original {@link Date} is unchanged.
295      *
296      * @param date  the date, not null
297      * @param amount  the amount to add, may be negative
298      * @return the new {@link Date} with the amount added
299      * @throws NullPointerException if the date is null
300      */
301     public static Date addWeeks(final Date date, final int amount) {
302         return add(date, Calendar.WEEK_OF_YEAR, amount);
303     }
304 
305     /**
306      * Adds a number of years to a date returning a new object.
307      * The original {@link Date} is unchanged.
308      *
309      * @param date  the date, not null
310      * @param amount  the amount to add, may be negative
311      * @return the new {@link Date} with the amount added
312      * @throws NullPointerException if the date is null
313      */
314     public static Date addYears(final Date date, final int amount) {
315         return add(date, Calendar.YEAR, amount);
316     }
317 
318     /**
319      * Gets a date ceiling, leaving the field specified as the most
320      * significant field.
321      *
322      * <p>For example, if you had the date-time of 28 Mar 2002
323      * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
324      * 2002 14:00:00.000.  If this was passed with MONTH, it would
325      * return 1 Apr 2002 0:00:00.000.</p>
326      *
327      * @param calendar  the date to work with, not null
328      * @param field  the field from {@link Calendar} or {@code SEMI_MONTH}
329      * @return the different ceil date, not null
330      * @throws NullPointerException if the date is {@code null}
331      * @throws ArithmeticException if the year is over 280 million
332      * @since 2.5
333      */
334     public static Calendar ceiling(final Calendar calendar, final int field) {
335         Objects.requireNonNull(calendar, "calendar");
336         return modify((Calendar) calendar.clone(), field, ModifyType.CEILING);
337     }
338 
339     /**
340      * Gets a date ceiling, leaving the field specified as the most
341      * significant field.
342      *
343      * <p>For example, if you had the date-time of 28 Mar 2002
344      * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
345      * 2002 14:00:00.000.  If this was passed with MONTH, it would
346      * return 1 Apr 2002 0:00:00.000.</p>
347      *
348      * @param date  the date to work with, not null
349      * @param field  the field from {@link Calendar} or {@code SEMI_MONTH}
350      * @return the different ceil date, not null
351      * @throws NullPointerException if the date is {@code null}
352      * @throws ArithmeticException if the year is over 280 million
353      * @since 2.5
354      */
355     public static Date ceiling(final Date date, final int field) {
356         return modify(toCalendar(date), field, ModifyType.CEILING).getTime();
357     }
358 
359     /**
360      * Gets a date ceiling, leaving the field specified as the most
361      * significant field.
362      *
363      * <p>For example, if you had the date-time of 28 Mar 2002
364      * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
365      * 2002 14:00:00.000.  If this was passed with MONTH, it would
366      * return 1 Apr 2002 0:00:00.000.</p>
367      *
368      * @param date  the date to work with, either {@link Date} or {@link Calendar}, not null
369      * @param field  the field from {@link Calendar} or {@code SEMI_MONTH}
370      * @return the different ceil date, not null
371      * @throws NullPointerException if the date is {@code null}
372      * @throws ClassCastException if the object type is not a {@link Date} or {@link Calendar}
373      * @throws ArithmeticException if the year is over 280 million
374      * @since 2.5
375      */
376     public static Date ceiling(final Object date, final int field) {
377         Objects.requireNonNull(date, "date");
378         if (date instanceof Date) {
379             return ceiling((Date) date, field);
380         }
381         if (date instanceof Calendar) {
382             return ceiling((Calendar) date, field).getTime();
383         }
384         throw new ClassCastException("Could not find ceiling of for type: " + date.getClass());
385     }
386 
387     /**
388      * Gets a Calendar fragment for any unit.
389      *
390      * @param calendar the calendar to work with, not null
391      * @param fragment the Calendar field part of calendar to calculate
392      * @param unit the time unit
393      * @return number of units within the fragment of the calendar
394      * @throws NullPointerException if the date is {@code null} or
395      * fragment is not supported
396      * @since 2.4
397      */
398     private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) {
399         Objects.requireNonNull(calendar, "calendar");
400         long result = 0;
401         final int offset = (unit == TimeUnit.DAYS) ? 0 : 1;
402 
403         // Fragments bigger than a day require a breakdown to days
404         switch (fragment) {
405             case Calendar.YEAR:
406                 result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS);
407                 break;
408             case Calendar.MONTH:
409                 result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS);
410                 break;
411             default:
412                 break;
413         }
414 
415         switch (fragment) {
416             // Number of days already calculated for these cases
417             case Calendar.YEAR:
418             case Calendar.MONTH:
419 
420             // The rest of the valid cases
421             case Calendar.DAY_OF_YEAR:
422             case Calendar.DATE:
423                 result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS);
424                 //$FALL-THROUGH$
425             case Calendar.HOUR_OF_DAY:
426                 result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES);
427                 //$FALL-THROUGH$
428             case Calendar.MINUTE:
429                 result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS);
430                 //$FALL-THROUGH$
431             case Calendar.SECOND:
432                 result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS);
433                 break;
434             case Calendar.MILLISECOND: break; //never useful
435                 default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported");
436         }
437         return result;
438     }
439 
440     /**
441      * Gets a Date fragment for any unit.
442      *
443      * @param date the date to work with, not null
444      * @param fragment the Calendar field part of date to calculate
445      * @param unit the time unit
446      * @return number of units within the fragment of the date
447      * @throws NullPointerException if the date is {@code null}
448      * @throws IllegalArgumentException if fragment is not supported
449      * @since 2.4
450      */
451     private static long getFragment(final Date date, final int fragment, final TimeUnit unit) {
452         validateDateNotNull(date);
453         final Calendar calendar = Calendar.getInstance();
454         calendar.setTime(date);
455         return getFragment(calendar, fragment, unit);
456     }
457 
458     /**
459      * Returns the number of days within the
460      * fragment. All datefields greater than the fragment will be ignored.
461      *
462      * <p>Asking the days of any date will only return the number of days
463      * of the current month (resulting in a number between 1 and 31). This
464      * method will retrieve the number of days for any fragment.
465      * For example, if you want to calculate the number of days past this year,
466      * your fragment is Calendar.YEAR. The result will be all days of the
467      * past month(s).</p>
468      *
469      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
470      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
471      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
472      * A fragment less than or equal to a DAY field will return 0.</p>
473      *
474      * <ul>
475      *  <li>January 28, 2008 with Calendar.MONTH as fragment will return 28
476      *   (equivalent to calendar.get(Calendar.DAY_OF_MONTH))</li>
477      *  <li>February 28, 2008 with Calendar.MONTH as fragment will return 28
478      *   (equivalent to calendar.get(Calendar.DAY_OF_MONTH))</li>
479      *  <li>January 28, 2008 with Calendar.YEAR as fragment will return 28
480      *   (equivalent to calendar.get(Calendar.DAY_OF_YEAR))</li>
481      *  <li>February 28, 2008 with Calendar.YEAR as fragment will return 59
482      *   (equivalent to calendar.get(Calendar.DAY_OF_YEAR))</li>
483      *  <li>January 28, 2008 with Calendar.MILLISECOND as fragment will return 0
484      *   (a millisecond cannot be split in days)</li>
485      * </ul>
486      *
487      * @param calendar the calendar to work with, not null
488      * @param fragment the {@link Calendar} field part of calendar to calculate
489      * @return number of days within the fragment of date
490      * @throws NullPointerException if the date is {@code null} or
491      * fragment is not supported
492      * @since 2.4
493      */
494     public static long getFragmentInDays(final Calendar calendar, final int fragment) {
495         return getFragment(calendar, fragment, TimeUnit.DAYS);
496     }
497 
498     /**
499      * Returns the number of days within the
500      * fragment. All date fields greater than the fragment will be ignored.
501      *
502      * <p>Asking the days of any date will only return the number of days
503      * of the current month (resulting in a number between 1 and 31). This
504      * method will retrieve the number of days for any fragment.
505      * For example, if you want to calculate the number of days past this year,
506      * your fragment is Calendar.YEAR. The result will be all days of the
507      * past month(s).</p>
508      *
509      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
510      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
511      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
512      * A fragment less than or equal to a DAY field will return 0.</p>
513      *
514      * <ul>
515      *  <li>January 28, 2008 with Calendar.MONTH as fragment will return 28
516      *   (equivalent to deprecated date.getDay())</li>
517      *  <li>February 28, 2008 with Calendar.MONTH as fragment will return 28
518      *   (equivalent to deprecated date.getDay())</li>
519      *  <li>January 28, 2008 with Calendar.YEAR as fragment will return 28</li>
520      *  <li>February 28, 2008 with Calendar.YEAR as fragment will return 59</li>
521      *  <li>January 28, 2008 with Calendar.MILLISECOND as fragment will return 0
522      *   (a millisecond cannot be split in days)</li>
523      * </ul>
524      *
525      * @param date the date to work with, not null
526      * @param fragment the {@link Calendar} field part of date to calculate
527      * @return number of days  within the fragment of date
528      * @throws NullPointerException if the date is {@code null}
529      * @throws IllegalArgumentException if the fragment is not supported
530      * @since 2.4
531      */
532     public static long getFragmentInDays(final Date date, final int fragment) {
533         return getFragment(date, fragment, TimeUnit.DAYS);
534     }
535 
536     /**
537      * Returns the number of hours within the
538      * fragment. All date fields greater than the fragment will be ignored.
539      *
540      * <p>Asking the hours of any date will only return the number of hours
541      * of the current day (resulting in a number between 0 and 23). This
542      * method will retrieve the number of hours for any fragment.
543      * For example, if you want to calculate the number of hours past this month,
544      * your fragment is Calendar.MONTH. The result will be all hours of the
545      * past day(s).</p>
546      *
547      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
548      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
549      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
550      * A fragment less than or equal to a HOUR field will return 0.</p>
551      *
552      * <ul>
553      *  <li>January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
554      *   (equivalent to calendar.get(Calendar.HOUR_OF_DAY))</li>
555      *  <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
556      *   (equivalent to calendar.get(Calendar.HOUR_OF_DAY))</li>
557      *  <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7</li>
558      *  <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)</li>
559      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
560      *   (a millisecond cannot be split in hours)</li>
561      * </ul>
562      *
563      * @param calendar the calendar to work with, not null
564      * @param fragment the {@link Calendar} field part of calendar to calculate
565      * @return number of hours within the fragment of date
566      * @throws NullPointerException if the date is {@code null} or
567      * fragment is not supported
568      * @since 2.4
569      */
570     public static long getFragmentInHours(final Calendar calendar, final int fragment) {
571         return getFragment(calendar, fragment, TimeUnit.HOURS);
572     }
573 
574     /**
575      * Returns the number of hours within the
576      * fragment. All date fields greater than the fragment will be ignored.
577      *
578      * <p>Asking the hours of any date will only return the number of hours
579      * of the current day (resulting in a number between 0 and 23). This
580      * method will retrieve the number of hours for any fragment.
581      * For example, if you want to calculate the number of hours past this month,
582      * your fragment is Calendar.MONTH. The result will be all hours of the
583      * past day(s).</p>
584      *
585      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
586      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
587      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
588      * A fragment less than or equal to a HOUR field will return 0.</p>
589      *
590      * <ul>
591      *  <li>January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
592      *   (equivalent to deprecated date.getHours())</li>
593      *  <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
594      *   (equivalent to deprecated date.getHours())</li>
595      *  <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7</li>
596      *  <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)</li>
597      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
598      *   (a millisecond cannot be split in hours)</li>
599      * </ul>
600      *
601      * @param date the date to work with, not null
602      * @param fragment the {@link Calendar} field part of date to calculate
603      * @return number of hours within the fragment of date
604      * @throws NullPointerException if the date is {@code null}
605      * @throws IllegalArgumentException if the fragment is not supported
606      * @since 2.4
607      */
608     public static long getFragmentInHours(final Date date, final int fragment) {
609         return getFragment(date, fragment, TimeUnit.HOURS);
610     }
611 
612     /**
613      * Returns the number of milliseconds within the
614      * fragment. All date fields greater than the fragment will be ignored.
615      *
616      * <p>Asking the milliseconds of any date will only return the number of milliseconds
617      * of the current second (resulting in a number between 0 and 999). This
618      * method will retrieve the number of milliseconds for any fragment.
619      * For example, if you want to calculate the number of seconds past today,
620      * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
621      * be all seconds of the past hour(s), minutes(s) and second(s).</p>
622      *
623      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
624      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
625      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
626      * A fragment less than or equal to a MILLISECOND field will return 0.</p>
627      *
628      * <ul>
629      *  <li>January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
630      *   (equivalent to calendar.get(Calendar.MILLISECOND))</li>
631      *  <li>January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
632      *   (equivalent to calendar.get(Calendar.MILLISECOND))</li>
633      *  <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538
634      *   (10*1000 + 538)</li>
635      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
636      *   (a millisecond cannot be split in milliseconds)</li>
637      * </ul>
638      *
639      * @param calendar the calendar to work with, not null
640      * @param fragment the {@link Calendar} field part of calendar to calculate
641      * @return number of milliseconds within the fragment of date
642      * @throws NullPointerException if the date is {@code null} or
643      * fragment is not supported
644      * @since 2.4
645      */
646   public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) {
647     return getFragment(calendar, fragment, TimeUnit.MILLISECONDS);
648   }
649 
650     /**
651      * Returns the number of milliseconds within the
652      * fragment. All date fields greater than the fragment will be ignored.
653      *
654      * <p>Asking the milliseconds of any date will only return the number of milliseconds
655      * of the current second (resulting in a number between 0 and 999). This
656      * method will retrieve the number of milliseconds for any fragment.
657      * For example, if you want to calculate the number of milliseconds past today,
658      * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
659      * be all milliseconds of the past hour(s), minutes(s) and second(s).</p>
660      *
661      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
662      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
663      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
664      * A fragment less than or equal to a SECOND field will return 0.</p>
665      *
666      * <ul>
667      *  <li>January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538</li>
668      *  <li>January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538</li>
669      *  <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)</li>
670      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
671      *   (a millisecond cannot be split in milliseconds)</li>
672      * </ul>
673      *
674      * @param date the date to work with, not null
675      * @param fragment the {@link Calendar} field part of date to calculate
676      * @return number of milliseconds within the fragment of date
677      * @throws NullPointerException if the date is {@code null}
678      * @throws IllegalArgumentException if the fragment is not supported
679      * @since 2.4
680      */
681     public static long getFragmentInMilliseconds(final Date date, final int fragment) {
682         return getFragment(date, fragment, TimeUnit.MILLISECONDS);
683     }
684 
685     /**
686      * Returns the number of minutes within the
687      * fragment. All date fields greater than the fragment will be ignored.
688      *
689      * <p>Asking the minutes of any date will only return the number of minutes
690      * of the current hour (resulting in a number between 0 and 59). This
691      * method will retrieve the number of minutes for any fragment.
692      * For example, if you want to calculate the number of minutes past this month,
693      * your fragment is Calendar.MONTH. The result will be all minutes of the
694      * past day(s) and hour(s).</p>
695      *
696      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
697      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
698      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
699      * A fragment less than or equal to a MINUTE field will return 0.</p>
700      *
701      * <ul>
702      *  <li>January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
703      *   (equivalent to calendar.get(Calendar.MINUTES))</li>
704      *  <li>January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
705      *   (equivalent to calendar.get(Calendar.MINUTES))</li>
706      *  <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15</li>
707      *  <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)</li>
708      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
709      *   (a millisecond cannot be split in minutes)</li>
710      * </ul>
711      *
712      * @param calendar the calendar to work with, not null
713      * @param fragment the {@link Calendar} field part of calendar to calculate
714      * @return number of minutes within the fragment of date
715      * @throws NullPointerException if the date is {@code null} or
716      * fragment is not supported
717      * @since 2.4
718      */
719     public static long getFragmentInMinutes(final Calendar calendar, final int fragment) {
720         return getFragment(calendar, fragment, TimeUnit.MINUTES);
721     }
722 
723     /**
724      * Returns the number of minutes within the
725      * fragment. All date fields greater than the fragment will be ignored.
726      *
727      * <p>Asking the minutes of any date will only return the number of minutes
728      * of the current hour (resulting in a number between 0 and 59). This
729      * method will retrieve the number of minutes for any fragment.
730      * For example, if you want to calculate the number of minutes past this month,
731      * your fragment is Calendar.MONTH. The result will be all minutes of the
732      * past day(s) and hour(s).</p>
733      *
734      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
735      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
736      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
737      * A fragment less than or equal to a MINUTE field will return 0.</p>
738      *
739      * <ul>
740      *  <li>January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
741      *   (equivalent to deprecated date.getMinutes())</li>
742      *  <li>January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
743      *   (equivalent to deprecated date.getMinutes())</li>
744      *  <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15</li>
745      *  <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)</li>
746      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
747      *   (a millisecond cannot be split in minutes)</li>
748      * </ul>
749      *
750      * @param date the date to work with, not null
751      * @param fragment the {@link Calendar} field part of date to calculate
752      * @return number of minutes within the fragment of date
753      * @throws NullPointerException if the date is {@code null}
754      * @throws IllegalArgumentException if the fragment is not supported
755      * @since 2.4
756      */
757     public static long getFragmentInMinutes(final Date date, final int fragment) {
758         return getFragment(date, fragment, TimeUnit.MINUTES);
759     }
760 
761     /**
762      * Returns the number of seconds within the
763      * fragment. All date fields greater than the fragment will be ignored.
764      *
765      * <p>Asking the seconds of any date will only return the number of seconds
766      * of the current minute (resulting in a number between 0 and 59). This
767      * method will retrieve the number of seconds for any fragment.
768      * For example, if you want to calculate the number of seconds past today,
769      * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
770      * be all seconds of the past hour(s) and minutes(s).</p>
771      *
772      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
773      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
774      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
775      * A fragment less than or equal to a SECOND field will return 0.</p>
776      *
777      * <ul>
778      *  <li>January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
779      *   (equivalent to calendar.get(Calendar.SECOND))</li>
780      *  <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
781      *   (equivalent to calendar.get(Calendar.SECOND))</li>
782      *  <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110
783      *   (7*3600 + 15*60 + 10)</li>
784      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
785      *   (a millisecond cannot be split in seconds)</li>
786      * </ul>
787      *
788      * @param calendar the calendar to work with, not null
789      * @param fragment the {@link Calendar} field part of calendar to calculate
790      * @return number of seconds within the fragment of date
791      * @throws NullPointerException if the date is {@code null} or
792      * fragment is not supported
793      * @since 2.4
794      */
795     public static long getFragmentInSeconds(final Calendar calendar, final int fragment) {
796         return getFragment(calendar, fragment, TimeUnit.SECONDS);
797     }
798 
799     /**
800      * Returns the number of seconds within the
801      * fragment. All date fields greater than the fragment will be ignored.
802      *
803      * <p>Asking the seconds of any date will only return the number of seconds
804      * of the current minute (resulting in a number between 0 and 59). This
805      * method will retrieve the number of seconds for any fragment.
806      * For example, if you want to calculate the number of seconds past today,
807      * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
808      * be all seconds of the past hour(s) and minutes(s).</p>
809      *
810      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both
811      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY,
812      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
813      * A fragment less than or equal to a SECOND field will return 0.</p>
814      *
815      * <ul>
816      *  <li>January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
817      *   (equivalent to deprecated date.getSeconds())</li>
818      *  <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
819      *   (equivalent to deprecated date.getSeconds())</li>
820      *  <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110
821      *   (7*3600 + 15*60 + 10)</li>
822      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
823      *   (a millisecond cannot be split in seconds)</li>
824      * </ul>
825      *
826      * @param date the date to work with, not null
827      * @param fragment the {@link Calendar} field part of date to calculate
828      * @return number of seconds within the fragment of date
829      * @throws NullPointerException if the date is {@code null}
830      * @throws IllegalArgumentException if the fragment is not supported
831      * @since 2.4
832      */
833     public static long getFragmentInSeconds(final Date date, final int fragment) {
834         return getFragment(date, fragment, TimeUnit.SECONDS);
835     }
836 
837     /**
838      * Checks if two calendar objects are on the same day ignoring time.
839      *
840      * <p>28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true.
841      * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false.
842      * </p>
843      *
844      * @param cal1  the first calendar, not altered, not null
845      * @param cal2  the second calendar, not altered, not null
846      * @return true if they represent the same day
847      * @throws NullPointerException if either calendar is {@code null}
848      * @since 2.1
849      */
850     public static boolean isSameDay(final Calendar cal1, final Calendar cal2) {
851         Objects.requireNonNull(cal1, "cal1");
852         Objects.requireNonNull(cal2, "cal2");
853         return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
854                 cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
855                 cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR);
856     }
857 
858     /**
859      * Checks if two date objects are on the same day ignoring time.
860      *
861      * <p>28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true.
862      * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false.
863      * </p>
864      *
865      * @param date1  the first date, not altered, not null
866      * @param date2  the second date, not altered, not null
867      * @return true if they represent the same day
868      * @throws NullPointerException if either date is {@code null}
869      * @since 2.1
870      */
871     public static boolean isSameDay(final Date date1, final Date date2) {
872         return isSameDay(toCalendar(date1), toCalendar(date2));
873     }
874 
875     /**
876      * Checks if two calendar objects represent the same instant in time.
877      *
878      * <p>This method compares the long millisecond time of the two objects.</p>
879      *
880      * @param cal1  the first calendar, not altered, not null
881      * @param cal2  the second calendar, not altered, not null
882      * @return true if they represent the same millisecond instant
883      * @throws NullPointerException if either date is {@code null}
884      * @since 2.1
885      */
886     public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) {
887         Objects.requireNonNull(cal1, "cal1");
888         Objects.requireNonNull(cal2, "cal2");
889         return cal1.getTime().getTime() == cal2.getTime().getTime();
890     }
891 
892     /**
893      * Checks if two date objects represent the same instant in time.
894      *
895      * <p>This method compares the long millisecond time of the two objects.</p>
896      *
897      * @param date1  the first date, not altered, not null
898      * @param date2  the second date, not altered, not null
899      * @return true if they represent the same millisecond instant
900      * @throws NullPointerException if either date is {@code null}
901      * @since 2.1
902      */
903     public static boolean isSameInstant(final Date date1, final Date date2) {
904         Objects.requireNonNull(date1, "date1");
905         Objects.requireNonNull(date2, "date2");
906         return date1.getTime() == date2.getTime();
907     }
908 
909     /**
910      * Checks if two calendar objects represent the same local time.
911      *
912      * <p>This method compares the values of the fields of the two objects.
913      * In addition, both calendars must be the same of the same type.</p>
914      *
915      * @param cal1  the first calendar, not altered, not null
916      * @param cal2  the second calendar, not altered, not null
917      * @return true if they represent the same millisecond instant
918      * @throws NullPointerException if either date is {@code null}
919      * @since 2.1
920      */
921     public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) {
922         Objects.requireNonNull(cal1, "cal1");
923         Objects.requireNonNull(cal2, "cal2");
924         return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) &&
925                 cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) &&
926                 cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) &&
927                 cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) &&
928                 cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) &&
929                 cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
930                 cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
931                 cal1.getClass() == cal2.getClass();
932     }
933 
934     /**
935      * Constructs an {@link Iterator} over each day in a date
936      * range defined by a focus date and range style.
937      *
938      * <p>For instance, passing Thursday, July 4, 2002 and a
939      * {@code RANGE_MONTH_SUNDAY} will return an {@link Iterator}
940      * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
941      * 2002, returning a Calendar instance for each intermediate day.</p>
942      *
943      * <p>This method provides an iterator that returns Calendar objects.
944      * The days are progressed using {@link Calendar#add(int, int)}.</p>
945      *
946      * @param calendar  the date to work with, not null
947      * @param rangeStyle  the style constant to use. Must be one of
948      * {@link DateUtils#RANGE_MONTH_SUNDAY},
949      * {@link DateUtils#RANGE_MONTH_MONDAY},
950      * {@link DateUtils#RANGE_WEEK_SUNDAY},
951      * {@link DateUtils#RANGE_WEEK_MONDAY},
952      * {@link DateUtils#RANGE_WEEK_RELATIVE},
953      * {@link DateUtils#RANGE_WEEK_CENTER}
954      * @return the date iterator, not null
955      * @throws NullPointerException if calendar is {@code null}
956      * @throws IllegalArgumentException if the rangeStyle is invalid
957      */
958     public static Iterator<Calendar> iterator(final Calendar calendar, final int rangeStyle) {
959         Objects.requireNonNull(calendar, "calendar");
960         final Calendar start;
961         final Calendar end;
962         int startCutoff = Calendar.SUNDAY;
963         int endCutoff = Calendar.SATURDAY;
964         switch (rangeStyle) {
965             case RANGE_MONTH_SUNDAY:
966             case RANGE_MONTH_MONDAY:
967                 //Set start to the first of the month
968                 start = truncate(calendar, Calendar.MONTH);
969                 //Set end to the last of the month
970                 end = (Calendar) start.clone();
971                 end.add(Calendar.MONTH, 1);
972                 end.add(Calendar.DATE, -1);
973                 //Loop start back to the previous sunday or monday
974                 if (rangeStyle == RANGE_MONTH_MONDAY) {
975                     startCutoff = Calendar.MONDAY;
976                     endCutoff = Calendar.SUNDAY;
977                 }
978                 break;
979             case RANGE_WEEK_SUNDAY:
980             case RANGE_WEEK_MONDAY:
981             case RANGE_WEEK_RELATIVE:
982             case RANGE_WEEK_CENTER:
983                 //Set start and end to the current date
984                 start = truncate(calendar, Calendar.DATE);
985                 end = truncate(calendar, Calendar.DATE);
986                 switch (rangeStyle) {
987                     case RANGE_WEEK_SUNDAY:
988                         //already set by default
989                         break;
990                     case RANGE_WEEK_MONDAY:
991                         startCutoff = Calendar.MONDAY;
992                         endCutoff = Calendar.SUNDAY;
993                         break;
994                     case RANGE_WEEK_RELATIVE:
995                         startCutoff = calendar.get(Calendar.DAY_OF_WEEK);
996                         endCutoff = startCutoff - 1;
997                         break;
998                     case RANGE_WEEK_CENTER:
999                         startCutoff = calendar.get(Calendar.DAY_OF_WEEK) - 3;
1000                         endCutoff = calendar.get(Calendar.DAY_OF_WEEK) + 3;
1001                         break;
1002                     default:
1003                         break;
1004                 }
1005                 break;
1006             default:
1007                 throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid.");
1008         }
1009         if (startCutoff < Calendar.SUNDAY) {
1010             startCutoff += 7;
1011         }
1012         if (startCutoff > Calendar.SATURDAY) {
1013             startCutoff -= 7;
1014         }
1015         if (endCutoff < Calendar.SUNDAY) {
1016             endCutoff += 7;
1017         }
1018         if (endCutoff > Calendar.SATURDAY) {
1019             endCutoff -= 7;
1020         }
1021         while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) {
1022             start.add(Calendar.DATE, -1);
1023         }
1024         while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) {
1025             end.add(Calendar.DATE, 1);
1026         }
1027         return new DateIterator(start, end);
1028     }
1029 
1030     /**
1031      * Constructs an {@link Iterator} over each day in a date
1032      * range defined by a focus date and range style.
1033      *
1034      * <p>For instance, passing Thursday, July 4, 2002 and a
1035      * {@code RANGE_MONTH_SUNDAY} will return an {@link Iterator}
1036      * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
1037      * 2002, returning a Calendar instance for each intermediate day.</p>
1038      *
1039      * <p>This method provides an iterator that returns Calendar objects.
1040      * The days are progressed using {@link Calendar#add(int, int)}.</p>
1041      *
1042      * @param focus  the date to work with, not null
1043      * @param rangeStyle  the style constant to use. Must be one of
1044      * {@link DateUtils#RANGE_MONTH_SUNDAY},
1045      * {@link DateUtils#RANGE_MONTH_MONDAY},
1046      * {@link DateUtils#RANGE_WEEK_SUNDAY},
1047      * {@link DateUtils#RANGE_WEEK_MONDAY},
1048      * {@link DateUtils#RANGE_WEEK_RELATIVE},
1049      * {@link DateUtils#RANGE_WEEK_CENTER}
1050      * @return the date iterator, not null, not null
1051      * @throws NullPointerException if the date is {@code null}
1052      * @throws IllegalArgumentException if the rangeStyle is invalid
1053      */
1054     public static Iterator<Calendar> iterator(final Date focus, final int rangeStyle) {
1055         return iterator(toCalendar(focus), rangeStyle);
1056     }
1057 
1058     /**
1059      * Constructs an {@link Iterator} over each day in a date
1060      * range defined by a focus date and range style.
1061      *
1062      * <p>For instance, passing Thursday, July 4, 2002 and a
1063      * {@code RANGE_MONTH_SUNDAY} will return an {@link Iterator}
1064      * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
1065      * 2002, returning a Calendar instance for each intermediate day.</p>
1066      *
1067      * @param calendar  the date to work with, either {@link Date} or {@link Calendar}, not null
1068      * @param rangeStyle  the style constant to use. Must be one of the range
1069      * styles listed for the {@link #iterator(Calendar, int)} method.
1070      * @return the date iterator, not null
1071      * @throws NullPointerException if the date is {@code null}
1072      * @throws ClassCastException if the object type is not a {@link Date} or {@link Calendar}
1073      */
1074     public static Iterator<?> iterator(final Object calendar, final int rangeStyle) {
1075         Objects.requireNonNull(calendar, "calendar");
1076         if (calendar instanceof Date) {
1077             return iterator((Date) calendar, rangeStyle);
1078         }
1079         if (calendar instanceof Calendar) {
1080             return iterator((Calendar) calendar, rangeStyle);
1081         }
1082         throw new ClassCastException("Could not iterate based on " + calendar);
1083     }
1084 
1085     /**
1086      * Internal calculation method.
1087      *
1088      * @param val  the calendar, not null
1089      * @param field  the field constant
1090      * @param modType  type to truncate, round or ceiling
1091      * @return the given calendar
1092      * @throws ArithmeticException if the year is over 280 million
1093      */
1094     private static Calendar modify(final Calendar val, final int field, final ModifyType modType) {
1095         if (val.get(Calendar.YEAR) > 280000000) {
1096             throw new ArithmeticException("Calendar value too large for accurate calculations");
1097         }
1098 
1099         if (field == Calendar.MILLISECOND) {
1100             return val;
1101         }
1102 
1103         // Fix for LANG-59 START
1104         // see https://issues.apache.org/jira/browse/LANG-59
1105         //
1106         // Manually truncate milliseconds, seconds and minutes, rather than using
1107         // Calendar methods.
1108 
1109         final Date date = val.getTime();
1110         long time = date.getTime();
1111         boolean done = false;
1112 
1113         // truncate milliseconds
1114         final int millisecs = val.get(Calendar.MILLISECOND);
1115         if (ModifyType.TRUNCATE == modType || millisecs < 500) {
1116             time = time - millisecs;
1117         }
1118         if (field == Calendar.SECOND) {
1119             done = true;
1120         }
1121 
1122         // truncate seconds
1123         final int seconds = val.get(Calendar.SECOND);
1124         if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) {
1125             time = time - (seconds * 1000L);
1126         }
1127         if (field == Calendar.MINUTE) {
1128             done = true;
1129         }
1130 
1131         // truncate minutes
1132         final int minutes = val.get(Calendar.MINUTE);
1133         if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) {
1134             time = time - (minutes * 60000L);
1135         }
1136 
1137         // reset time
1138         if (date.getTime() != time) {
1139             date.setTime(time);
1140             val.setTime(date);
1141         }
1142         // Fix for LANG-59 END
1143 
1144         boolean roundUp = false;
1145         for (final int[] aField : fields) {
1146             for (final int element : aField) {
1147                 if (element == field) {
1148                     //This is our field... we stop looping
1149                     if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) {
1150                         if (field == SEMI_MONTH) {
1151                             //This is a special case that's hard to generalize
1152                             //If the date is 1, we round up to 16, otherwise
1153                             //  we subtract 15 days and add 1 month
1154                             if (val.get(Calendar.DATE) == 1) {
1155                                 val.add(Calendar.DATE, 15);
1156                             } else {
1157                                 val.add(Calendar.DATE, -15);
1158                                 val.add(Calendar.MONTH, 1);
1159                             }
1160                         // Fix for LANG-440 START
1161                         } else if (field == Calendar.AM_PM) {
1162                             // This is a special case
1163                             // If the time is 0, we round up to 12, otherwise
1164                             //  we subtract 12 hours and add 1 day
1165                             if (val.get(Calendar.HOUR_OF_DAY) == 0) {
1166                                 val.add(Calendar.HOUR_OF_DAY, 12);
1167                             } else {
1168                                 val.add(Calendar.HOUR_OF_DAY, -12);
1169                                 val.add(Calendar.DATE, 1);
1170                             }
1171                             // Fix for LANG-440 END
1172                         } else {
1173                             //We need at add one to this field since the
1174                             //  last number causes us to round up
1175                             val.add(aField[0], 1);
1176                         }
1177                     }
1178                     return val;
1179                 }
1180             }
1181             //We have various fields that are not easy roundings
1182             int offset = 0;
1183             boolean offsetSet = false;
1184             //These are special types of fields that require different rounding rules
1185             switch (field) {
1186                 case SEMI_MONTH:
1187                     if (aField[0] == Calendar.DATE) {
1188                         //If we're going to drop the DATE field's value,
1189                         //  we want to do this our own way.
1190                         //We need to subtract 1 since the date has a minimum of 1
1191                         offset = val.get(Calendar.DATE) - 1;
1192                         //If we're above 15 days adjustment, that means we're in the
1193                         //  bottom half of the month and should stay accordingly.
1194                         if (offset >= 15) {
1195                             offset -= 15;
1196                         }
1197                         //Record whether we're in the top or bottom half of that range
1198                         roundUp = offset > 7;
1199                         offsetSet = true;
1200                     }
1201                     break;
1202                 case Calendar.AM_PM:
1203                     if (aField[0] == Calendar.HOUR_OF_DAY) {
1204                         //If we're going to drop the HOUR field's value,
1205                         //  we want to do this our own way.
1206                         offset = val.get(Calendar.HOUR_OF_DAY);
1207                         if (offset >= 12) {
1208                             offset -= 12;
1209                         }
1210                         roundUp = offset >= 6;
1211                         offsetSet = true;
1212                     }
1213                     break;
1214                 default:
1215                     break;
1216             }
1217             if (!offsetSet) {
1218                 final int min = val.getActualMinimum(aField[0]);
1219                 final int max = val.getActualMaximum(aField[0]);
1220                 //Calculate the offset from the minimum allowed value
1221                 offset = val.get(aField[0]) - min;
1222                 //Set roundUp if this is more than half way between the minimum and maximum
1223                 roundUp = offset > ((max - min) / 2);
1224             }
1225             //We need to remove this field
1226             if (offset != 0) {
1227                 val.set(aField[0], val.get(aField[0]) - offset);
1228             }
1229         }
1230         throw new IllegalArgumentException("The field " + field + " is not supported");
1231     }
1232 
1233     /**
1234      * Parses a string representing a date by trying a variety of different parsers,
1235      * using the default date format symbols for the given locale.
1236      *
1237      * <p>The parse will try each parse pattern in turn.
1238      * A parse is only deemed successful if it parses the whole of the input string.
1239      * If no parse patterns match, a ParseException is thrown.</p>
1240      * The parser will be lenient toward the parsed date.
1241      *
1242      * @param str  the date to parse, not null
1243      * @param locale the locale whose date format symbols should be used. If {@code null},
1244      * the system locale is used (as per {@link #parseDate(String, String...)}).
1245      * @param parsePatterns  the date format patterns to use, see SimpleDateFormat, not null
1246      * @return the parsed date
1247      * @throws NullPointerException if the date string or pattern array is null
1248      * @throws ParseException if none of the date patterns were suitable (or there were none)
1249      * @since 3.2
1250      */
1251     public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException {
1252         return parseDateWithLeniency(str, locale, parsePatterns, true);
1253     }
1254 
1255     /**
1256      * Parses a string representing a date by trying a variety of different parsers.
1257      *
1258      * <p>The parse will try each parse pattern in turn.
1259      * A parse is only deemed successful if it parses the whole of the input string.
1260      * If no parse patterns match, a ParseException is thrown.</p>
1261      * The parser will be lenient toward the parsed date.
1262      *
1263      * @param str  the date to parse, not null
1264      * @param parsePatterns  the date format patterns to use, see SimpleDateFormat, not null
1265      * @return the parsed date
1266      * @throws NullPointerException if the date string or pattern array is null
1267      * @throws ParseException if none of the date patterns were suitable (or there were none)
1268      */
1269     public static Date parseDate(final String str, final String... parsePatterns) throws ParseException {
1270         return parseDate(str, null, parsePatterns);
1271     }
1272 
1273     /**
1274      * Parses a string representing a date by trying a variety of different parsers,
1275      * using the default date format symbols for the given locale..
1276      *
1277      * <p>The parse will try each parse pattern in turn.
1278      * A parse is only deemed successful if it parses the whole of the input string.
1279      * If no parse patterns match, a ParseException is thrown.</p>
1280      * The parser parses strictly - it does not allow for dates such as "February 942, 1996".
1281      *
1282      * @param str  the date to parse, not null
1283      * @param locale the locale whose date format symbols should be used. If {@code null},
1284      * the system locale is used (as per {@link #parseDateStrictly(String, String...)}).
1285      * @param parsePatterns  the date format patterns to use, see SimpleDateFormat, not null
1286      * @return the parsed date
1287      * @throws NullPointerException if the date string or pattern array is null
1288      * @throws ParseException if none of the date patterns were suitable
1289      * @since 3.2
1290      */
1291     public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException {
1292         return parseDateWithLeniency(str, locale, parsePatterns, false);
1293     }
1294 
1295     /**
1296      * Parses a string representing a date by trying a variety of different parsers.
1297      *
1298      * <p>The parse will try each parse pattern in turn.
1299      * A parse is only deemed successful if it parses the whole of the input string.
1300      * If no parse patterns match, a ParseException is thrown.</p>
1301      * The parser parses strictly - it does not allow for dates such as "February 942, 1996".
1302      *
1303      * @param str  the date to parse, not null
1304      * @param parsePatterns  the date format patterns to use, see SimpleDateFormat, not null
1305      * @return the parsed date
1306      * @throws NullPointerException if the date string or pattern array is null
1307      * @throws ParseException if none of the date patterns were suitable
1308      * @since 2.5
1309      */
1310     public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException {
1311         return parseDateStrictly(str, null, parsePatterns);
1312     }
1313 
1314     /**
1315      * Parses a string representing a date by trying a variety of different parsers.
1316      *
1317      * <p>The parse will try each parse pattern in turn.
1318      * A parse is only deemed successful if it parses the whole of the input string.
1319      * If no parse patterns match, a ParseException is thrown.</p>
1320      *
1321      * @param dateStr  the date to parse, not null
1322      * @param locale the locale to use when interpreting the pattern, can be null in which
1323      * case the default system locale is used
1324      * @param parsePatterns  the date format patterns to use, see SimpleDateFormat, not null
1325      * @param lenient Specify whether or not date/time parsing is to be lenient.
1326      * @return the parsed date
1327      * @throws NullPointerException if the date string or pattern array is null
1328      * @throws ParseException if none of the date patterns were suitable
1329      * @see java.util.Calendar#isLenient()
1330      */
1331     private static Date parseDateWithLeniency(final String dateStr, final Locale locale, final String[] parsePatterns,
1332         final boolean lenient) throws ParseException {
1333         Objects.requireNonNull(dateStr, "str");
1334         Objects.requireNonNull(parsePatterns, "parsePatterns");
1335 
1336         final TimeZone tz = TimeZone.getDefault();
1337         final Locale lcl = LocaleUtils.toLocale(locale);
1338         final ParsePosition pos = new ParsePosition(0);
1339         final Calendar calendar = Calendar.getInstance(tz, lcl);
1340         calendar.setLenient(lenient);
1341 
1342         for (final String parsePattern : parsePatterns) {
1343             final FastDateParser fdp = new FastDateParser(parsePattern, tz, lcl);
1344             calendar.clear();
1345             try {
1346                 if (fdp.parse(dateStr, pos, calendar) && pos.getIndex() == dateStr.length()) {
1347                     return calendar.getTime();
1348                 }
1349             } catch (final IllegalArgumentException ignored) {
1350                 // leniency is preventing calendar from being set
1351             }
1352             pos.setIndex(0);
1353         }
1354         throw new ParseException("Unable to parse the date: " + dateStr, -1);
1355     }
1356 
1357     /**
1358      * Rounds a date, leaving the field specified as the most
1359      * significant field.
1360      *
1361      * <p>For example, if you had the date-time of 28 Mar 2002
1362      * 13:45:01.231, if this was passed with HOUR, it would return
1363      * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it
1364      * would return 1 April 2002 0:00:00.000.</p>
1365      *
1366      * <p>For a date in a time zone that handles the change to daylight
1367      * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows.
1368      * Suppose daylight saving time begins at 02:00 on March 30. Rounding a
1369      * date that crosses this time would produce the following values:
1370      * </p>
1371      * <ul>
1372      * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
1373      * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
1374      * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
1375      * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
1376      * </ul>
1377      *
1378      * @param calendar  the date to work with, not null
1379      * @param field  the field from {@link Calendar} or {@code SEMI_MONTH}
1380      * @return the different rounded date, not null
1381      * @throws NullPointerException if the date is {@code null}
1382      * @throws ArithmeticException if the year is over 280 million
1383      */
1384     public static Calendar round(final Calendar calendar, final int field) {
1385         Objects.requireNonNull(calendar, "calendar");
1386         return modify((Calendar) calendar.clone(), field, ModifyType.ROUND);
1387     }
1388 
1389     /**
1390      * Rounds a date, leaving the field specified as the most
1391      * significant field.
1392      *
1393      * <p>For example, if you had the date-time of 28 Mar 2002
1394      * 13:45:01.231, if this was passed with HOUR, it would return
1395      * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it
1396      * would return 1 April 2002 0:00:00.000.</p>
1397      *
1398      * <p>For a date in a time zone that handles the change to daylight
1399      * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows.
1400      * Suppose daylight saving time begins at 02:00 on March 30. Rounding a
1401      * date that crosses this time would produce the following values:
1402      * </p>
1403      * <ul>
1404      * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
1405      * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
1406      * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
1407      * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
1408      * </ul>
1409      *
1410      * @param date  the date to work with, not null
1411      * @param field  the field from {@link Calendar} or {@code SEMI_MONTH}
1412      * @return the different rounded date, not null
1413      * @throws NullPointerException if the date is null
1414      * @throws ArithmeticException if the year is over 280 million
1415      */
1416     public static Date round(final Date date, final int field) {
1417         return modify(toCalendar(date), field, ModifyType.ROUND).getTime();
1418     }
1419 
1420     /**
1421      * Rounds a date, leaving the field specified as the most
1422      * significant field.
1423      *
1424      * <p>For example, if you had the date-time of 28 Mar 2002
1425      * 13:45:01.231, if this was passed with HOUR, it would return
1426      * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it
1427      * would return 1 April 2002 0:00:00.000.</p>
1428      *
1429      * <p>For a date in a time zone that handles the change to daylight
1430      * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows.
1431      * Suppose daylight saving time begins at 02:00 on March 30. Rounding a
1432      * date that crosses this time would produce the following values:
1433      * </p>
1434      * <ul>
1435      * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
1436      * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
1437      * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
1438      * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
1439      * </ul>
1440      *
1441      * @param date  the date to work with, either {@link Date} or {@link Calendar}, not null
1442      * @param field  the field from {@link Calendar} or {@code SEMI_MONTH}
1443      * @return the different rounded date, not null
1444      * @throws NullPointerException if the date is {@code null}
1445      * @throws ClassCastException if the object type is not a {@link Date} or {@link Calendar}
1446      * @throws ArithmeticException if the year is over 280 million
1447      */
1448     public static Date round(final Object date, final int field) {
1449         Objects.requireNonNull(date, "date");
1450         if (date instanceof Date) {
1451             return round((Date) date, field);
1452         }
1453         if (date instanceof Calendar) {
1454             return round((Calendar) date, field).getTime();
1455         }
1456         throw new ClassCastException("Could not round " + date);
1457     }
1458 
1459     /**
1460      * Sets the specified field to a date returning a new object.
1461      * This does not use a lenient calendar.
1462      * The original {@link Date} is unchanged.
1463      *
1464      * @param date  the date, not null
1465      * @param calendarField  the {@link Calendar} field to set the amount to
1466      * @param amount the amount to set
1467      * @return a new {@link Date} set with the specified value
1468      * @throws NullPointerException if the date is null
1469      * @since 2.4
1470      */
1471     private static Date set(final Date date, final int calendarField, final int amount) {
1472         validateDateNotNull(date);
1473         // getInstance() returns a new object, so this method is thread safe.
1474         final Calendar c = Calendar.getInstance();
1475         c.setLenient(false);
1476         c.setTime(date);
1477         c.set(calendarField, amount);
1478         return c.getTime();
1479     }
1480 
1481     /**
1482      * Sets the day of month field to a date returning a new object.
1483      * The original {@link Date} is unchanged.
1484      *
1485      * @param date  the date, not null
1486      * @param amount the amount to set
1487      * @return a new {@link Date} set with the specified value
1488      * @throws NullPointerException if the date is null
1489      * @throws IllegalArgumentException if {@code amount} is not in the range
1490      *  {@code 1 <= amount <= 31}
1491      * @since 2.4
1492      */
1493     public static Date setDays(final Date date, final int amount) {
1494         return set(date, Calendar.DAY_OF_MONTH, amount);
1495     }
1496 
1497     /**
1498      * Sets the hours field to a date returning a new object.  Hours range
1499      * from  0-23.
1500      * The original {@link Date} is unchanged.
1501      *
1502      * @param date  the date, not null
1503      * @param amount the amount to set
1504      * @return a new {@link Date} set with the specified value
1505      * @throws NullPointerException if the date is null
1506      * @throws IllegalArgumentException if {@code amount} is not in the range
1507      *  {@code 0 <= amount <= 23}
1508      * @since 2.4
1509      */
1510     public static Date setHours(final Date date, final int amount) {
1511         return set(date, Calendar.HOUR_OF_DAY, amount);
1512     }
1513 
1514     /**
1515      * Sets the milliseconds field to a date returning a new object.
1516      * The original {@link Date} is unchanged.
1517      *
1518      * @param date  the date, not null
1519      * @param amount the amount to set
1520      * @return a new {@link Date} set with the specified value
1521      * @throws NullPointerException if the date is null
1522      * @throws IllegalArgumentException if {@code amount} is not in the range
1523      *  {@code 0 <= amount <= 999}
1524      * @since 2.4
1525      */
1526     public static Date setMilliseconds(final Date date, final int amount) {
1527         return set(date, Calendar.MILLISECOND, amount);
1528     }
1529 
1530     /**
1531      * Sets the minute field to a date returning a new object.
1532      * The original {@link Date} is unchanged.
1533      *
1534      * @param date  the date, not null
1535      * @param amount the amount to set
1536      * @return a new {@link Date} set with the specified value
1537      * @throws NullPointerException if the date is null
1538      * @throws IllegalArgumentException if {@code amount} is not in the range
1539      *  {@code 0 <= amount <= 59}
1540      * @since 2.4
1541      */
1542     public static Date setMinutes(final Date date, final int amount) {
1543         return set(date, Calendar.MINUTE, amount);
1544     }
1545 
1546     /**
1547      * Sets the months field to a date returning a new object.
1548      * The original {@link Date} is unchanged.
1549      *
1550      * @param date  the date, not null
1551      * @param amount the amount to set
1552      * @return a new {@link Date} set with the specified value
1553      * @throws NullPointerException if the date is null
1554      * @throws IllegalArgumentException if {@code amount} is not in the range
1555      *  {@code 0 <= amount <= 11}
1556      * @since 2.4
1557      */
1558     public static Date setMonths(final Date date, final int amount) {
1559         return set(date, Calendar.MONTH, amount);
1560     }
1561 
1562     /**
1563      * Sets the seconds field to a date returning a new object.
1564      * The original {@link Date} is unchanged.
1565      *
1566      * @param date  the date, not null
1567      * @param amount the amount to set
1568      * @return a new {@link Date} set with the specified value
1569      * @throws NullPointerException if the date is null
1570      * @throws IllegalArgumentException if {@code amount} is not in the range
1571      *  {@code 0 <= amount <= 59}
1572      * @since 2.4
1573      */
1574     public static Date setSeconds(final Date date, final int amount) {
1575         return set(date, Calendar.SECOND, amount);
1576     }
1577     /**
1578      * Sets the years field to a date returning a new object.
1579      * The original {@link Date} is unchanged.
1580      *
1581      * @param date  the date, not null
1582      * @param amount the amount to set
1583      * @return a new {@link Date} set with the specified value
1584      * @throws NullPointerException if the date is null
1585      * @since 2.4
1586      */
1587     public static Date setYears(final Date date, final int amount) {
1588         return set(date, Calendar.YEAR, amount);
1589     }
1590 
1591     /**
1592      * Converts a {@link Date} into a {@link Calendar}.
1593      *
1594      * @param date the date to convert to a Calendar
1595      * @return the created Calendar
1596      * @throws NullPointerException if null is passed in
1597      * @since 3.0
1598      */
1599     public static Calendar toCalendar(final Date date) {
1600         final Calendar c = Calendar.getInstance();
1601         c.setTime(Objects.requireNonNull(date, "date"));
1602         return c;
1603     }
1604 
1605     /**
1606      * Converts a {@link Date} of a given {@link TimeZone} into a {@link Calendar}
1607      * @param date the date to convert to a Calendar
1608      * @param tz the time zone of the {@code date}
1609      * @return the created Calendar
1610      * @throws NullPointerException if {@code date} or {@code tz} is null
1611      */
1612     public static Calendar toCalendar(final Date date, final TimeZone tz) {
1613         final Calendar c = Calendar.getInstance(tz);
1614         c.setTime(Objects.requireNonNull(date, "date"));
1615         return c;
1616     }
1617 
1618     /**
1619      * Truncates a date, leaving the field specified as the most
1620      * significant field.
1621      *
1622      * <p>For example, if you had the date-time of 28 Mar 2002
1623      * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
1624      * 2002 13:00:00.000.  If this was passed with MONTH, it would
1625      * return 1 Mar 2002 0:00:00.000.</p>
1626      *
1627      * @param date  the date to work with, not null
1628      * @param field  the field from {@link Calendar} or {@code SEMI_MONTH}
1629      * @return the different truncated date, not null
1630      * @throws NullPointerException if the date is {@code null}
1631      * @throws ArithmeticException if the year is over 280 million
1632      */
1633     public static Calendar truncate(final Calendar date, final int field) {
1634         Objects.requireNonNull(date, "date");
1635         return modify((Calendar) date.clone(), field, ModifyType.TRUNCATE);
1636     }
1637 
1638     /**
1639      * Truncates a date, leaving the field specified as the most
1640      * significant field.
1641      *
1642      * <p>For example, if you had the date-time of 28 Mar 2002
1643      * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
1644      * 2002 13:00:00.000.  If this was passed with MONTH, it would
1645      * return 1 Mar 2002 0:00:00.000.</p>
1646      *
1647      * @param date  the date to work with, not null
1648      * @param field  the field from {@link Calendar} or {@code SEMI_MONTH}
1649      * @return the different truncated date, not null
1650      * @throws NullPointerException if the date is {@code null}
1651      * @throws ArithmeticException if the year is over 280 million
1652      */
1653     public static Date truncate(final Date date, final int field) {
1654         return modify(toCalendar(date), field, ModifyType.TRUNCATE).getTime();
1655     }
1656 
1657     /**
1658      * Truncates a date, leaving the field specified as the most
1659      * significant field.
1660      *
1661      * <p>For example, if you had the date-time of 28 Mar 2002
1662      * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
1663      * 2002 13:00:00.000.  If this was passed with MONTH, it would
1664      * return 1 Mar 2002 0:00:00.000.</p>
1665      *
1666      * @param date  the date to work with, either {@link Date} or {@link Calendar}, not null
1667      * @param field  the field from {@link Calendar} or {@code SEMI_MONTH}
1668      * @return the different truncated date, not null
1669      * @throws NullPointerException if the date is {@code null}
1670      * @throws ClassCastException if the object type is not a {@link Date} or {@link Calendar}
1671      * @throws ArithmeticException if the year is over 280 million
1672      */
1673     public static Date truncate(final Object date, final int field) {
1674         Objects.requireNonNull(date, "date");
1675         if (date instanceof Date) {
1676             return truncate((Date) date, field);
1677         }
1678         if (date instanceof Calendar) {
1679             return truncate((Calendar) date, field).getTime();
1680         }
1681         throw new ClassCastException("Could not truncate " + date);
1682     }
1683 
1684     /**
1685      * Determines how two calendars compare up to no more than the specified
1686      * most significant field.
1687      *
1688      * @param cal1 the first calendar, not {@code null}
1689      * @param cal2 the second calendar, not {@code null}
1690      * @param field the field from {@link Calendar}
1691      * @return a negative integer, zero, or a positive integer as the first
1692      * calendar is less than, equal to, or greater than the second.
1693      * @throws NullPointerException if any argument is {@code null}
1694      * @see #truncate(Calendar, int)
1695      * @see #truncatedCompareTo(Date, Date, int)
1696      * @since 3.0
1697      */
1698     public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) {
1699         final Calendar truncatedCal1 = truncate(cal1, field);
1700         final Calendar truncatedCal2 = truncate(cal2, field);
1701         return truncatedCal1.compareTo(truncatedCal2);
1702     }
1703 
1704     /**
1705      * Determines how two dates compare up to no more than the specified
1706      * most significant field.
1707      *
1708      * @param date1 the first date, not {@code null}
1709      * @param date2 the second date, not {@code null}
1710      * @param field the field from {@link Calendar}
1711      * @return a negative integer, zero, or a positive integer as the first
1712      * date is less than, equal to, or greater than the second.
1713      * @throws NullPointerException if any argument is {@code null}
1714      * @see #truncate(Calendar, int)
1715      * @see #truncatedCompareTo(Date, Date, int)
1716      * @since 3.0
1717      */
1718     public static int truncatedCompareTo(final Date date1, final Date date2, final int field) {
1719         final Date truncatedDate1 = truncate(date1, field);
1720         final Date truncatedDate2 = truncate(date2, field);
1721         return truncatedDate1.compareTo(truncatedDate2);
1722     }
1723 
1724     /**
1725      * Determines if two calendars are equal up to no more than the specified
1726      * most significant field.
1727      *
1728      * @param cal1 the first calendar, not {@code null}
1729      * @param cal2 the second calendar, not {@code null}
1730      * @param field the field from {@link Calendar}
1731      * @return {@code true} if equal; otherwise {@code false}
1732      * @throws NullPointerException if any argument is {@code null}
1733      * @see #truncate(Calendar, int)
1734      * @see #truncatedEquals(Date, Date, int)
1735      * @since 3.0
1736      */
1737     public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) {
1738         return truncatedCompareTo(cal1, cal2, field) == 0;
1739     }
1740 
1741     /**
1742      * Determines if two dates are equal up to no more than the specified
1743      * most significant field.
1744      *
1745      * @param date1 the first date, not {@code null}
1746      * @param date2 the second date, not {@code null}
1747      * @param field the field from {@link Calendar}
1748      * @return {@code true} if equal; otherwise {@code false}
1749      * @throws NullPointerException if any argument is {@code null}
1750      * @see #truncate(Date, int)
1751      * @see #truncatedEquals(Calendar, Calendar, int)
1752      * @since 3.0
1753      */
1754     public static boolean truncatedEquals(final Date date1, final Date date2, final int field) {
1755         return truncatedCompareTo(date1, date2, field) == 0;
1756     }
1757 
1758     /**
1759      * @param date Date to validate.
1760      * @throws NullPointerException if {@code date == null}
1761      */
1762     private static void validateDateNotNull(final Date date) {
1763         Objects.requireNonNull(date, "date");
1764     }
1765 
1766     /**
1767      * {@link DateUtils} instances should NOT be constructed in
1768      * standard programming. Instead, the static methods on the class should
1769      * be used, such as {@code DateUtils.parseDate(str);}.
1770      *
1771      * <p>This constructor is public to permit tools that require a JavaBean
1772      * instance to operate.</p>
1773      */
1774     public DateUtils() {
1775     }
1776 
1777 }