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