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