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