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