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 1436770 2013-01-22 07:09:45Z ggregory $
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.Calender#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            }
1077            if (!offsetSet) {
1078                final int min = val.getActualMinimum(aField[0]);
1079                final int max = val.getActualMaximum(aField[0]);
1080                //Calculate the offset from the minimum allowed value
1081                offset = val.get(aField[0]) - min;
1082                //Set roundUp if this is more than half way between the minimum and maximum
1083                roundUp = offset > ((max - min) / 2);
1084            }
1085            //We need to remove this field
1086            if (offset != 0) {
1087                val.set(aField[0], val.get(aField[0]) - offset);
1088            }
1089        }
1090        throw new IllegalArgumentException("The field " + field + " is not supported");
1091
1092    }
1093
1094    //-----------------------------------------------------------------------
1095    /**
1096     * <p>Constructs an <code>Iterator</code> over each day in a date
1097     * range defined by a focus date and range style.</p>
1098     *
1099     * <p>For instance, passing Thursday, July 4, 2002 and a
1100     * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code>
1101     * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
1102     * 2002, returning a Calendar instance for each intermediate day.</p>
1103     *
1104     * <p>This method provides an iterator that returns Calendar objects.
1105     * The days are progressed using {@link Calendar#add(int, int)}.</p>
1106     *
1107     * @param focus  the date to work with, not null
1108     * @param rangeStyle  the style constant to use. Must be one of
1109     * {@link DateUtils#RANGE_MONTH_SUNDAY}, 
1110     * {@link DateUtils#RANGE_MONTH_MONDAY},
1111     * {@link DateUtils#RANGE_WEEK_SUNDAY},
1112     * {@link DateUtils#RANGE_WEEK_MONDAY},
1113     * {@link DateUtils#RANGE_WEEK_RELATIVE},
1114     * {@link DateUtils#RANGE_WEEK_CENTER}
1115     * @return the date iterator, not null, not null
1116     * @throws IllegalArgumentException if the date is <code>null</code>
1117     * @throws IllegalArgumentException if the rangeStyle is invalid
1118     */
1119    public static Iterator<Calendar> iterator(final Date focus, final int rangeStyle) {
1120        if (focus == null) {
1121            throw new IllegalArgumentException("The date must not be null");
1122        }
1123        final Calendar gval = Calendar.getInstance();
1124        gval.setTime(focus);
1125        return iterator(gval, rangeStyle);
1126    }
1127
1128    /**
1129     * <p>Constructs an <code>Iterator</code> over each day in a date
1130     * range defined by a focus date and range style.</p>
1131     *
1132     * <p>For instance, passing Thursday, July 4, 2002 and a
1133     * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code>
1134     * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
1135     * 2002, returning a Calendar instance for each intermediate day.</p>
1136     *
1137     * <p>This method provides an iterator that returns Calendar objects.
1138     * The days are progressed using {@link Calendar#add(int, int)}.</p>
1139     *
1140     * @param focus  the date to work with, not null
1141     * @param rangeStyle  the style constant to use. Must be one of
1142     * {@link DateUtils#RANGE_MONTH_SUNDAY}, 
1143     * {@link DateUtils#RANGE_MONTH_MONDAY},
1144     * {@link DateUtils#RANGE_WEEK_SUNDAY},
1145     * {@link DateUtils#RANGE_WEEK_MONDAY},
1146     * {@link DateUtils#RANGE_WEEK_RELATIVE},
1147     * {@link DateUtils#RANGE_WEEK_CENTER}
1148     * @return the date iterator, not null
1149     * @throws IllegalArgumentException if the date is <code>null</code>
1150     * @throws IllegalArgumentException if the rangeStyle is invalid
1151     */
1152    public static Iterator<Calendar> iterator(final Calendar focus, final int rangeStyle) {
1153        if (focus == null) {
1154            throw new IllegalArgumentException("The date must not be null");
1155        }
1156        Calendar start = null;
1157        Calendar end = null;
1158        int startCutoff = Calendar.SUNDAY;
1159        int endCutoff = Calendar.SATURDAY;
1160        switch (rangeStyle) {
1161            case RANGE_MONTH_SUNDAY:
1162            case RANGE_MONTH_MONDAY:
1163                //Set start to the first of the month
1164                start = truncate(focus, Calendar.MONTH);
1165                //Set end to the last of the month
1166                end = (Calendar) start.clone();
1167                end.add(Calendar.MONTH, 1);
1168                end.add(Calendar.DATE, -1);
1169                //Loop start back to the previous sunday or monday
1170                if (rangeStyle == RANGE_MONTH_MONDAY) {
1171                    startCutoff = Calendar.MONDAY;
1172                    endCutoff = Calendar.SUNDAY;
1173                }
1174                break;
1175            case RANGE_WEEK_SUNDAY:
1176            case RANGE_WEEK_MONDAY:
1177            case RANGE_WEEK_RELATIVE:
1178            case RANGE_WEEK_CENTER:
1179                //Set start and end to the current date
1180                start = truncate(focus, Calendar.DATE);
1181                end = truncate(focus, Calendar.DATE);
1182                switch (rangeStyle) {
1183                    case RANGE_WEEK_SUNDAY:
1184                        //already set by default
1185                        break;
1186                    case RANGE_WEEK_MONDAY:
1187                        startCutoff = Calendar.MONDAY;
1188                        endCutoff = Calendar.SUNDAY;
1189                        break;
1190                    case RANGE_WEEK_RELATIVE:
1191                        startCutoff = focus.get(Calendar.DAY_OF_WEEK);
1192                        endCutoff = startCutoff - 1;
1193                        break;
1194                    case RANGE_WEEK_CENTER:
1195                        startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3;
1196                        endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3;
1197                        break;
1198                }
1199                break;
1200            default:
1201                throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid.");
1202        }
1203        if (startCutoff < Calendar.SUNDAY) {
1204            startCutoff += 7;
1205        }
1206        if (startCutoff > Calendar.SATURDAY) {
1207            startCutoff -= 7;
1208        }
1209        if (endCutoff < Calendar.SUNDAY) {
1210            endCutoff += 7;
1211        }
1212        if (endCutoff > Calendar.SATURDAY) {
1213            endCutoff -= 7;
1214        }
1215        while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) {
1216            start.add(Calendar.DATE, -1);
1217        }
1218        while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) {
1219            end.add(Calendar.DATE, 1);
1220        }
1221        return new DateIterator(start, end);
1222    }
1223
1224    /**
1225     * <p>Constructs an <code>Iterator</code> over each day in a date
1226     * range defined by a focus date and range style.</p>
1227     *
1228     * <p>For instance, passing Thursday, July 4, 2002 and a
1229     * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code>
1230     * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
1231     * 2002, returning a Calendar instance for each intermediate day.</p>
1232     *
1233     * @param focus  the date to work with, either {@code Date} or {@code Calendar}, not null
1234     * @param rangeStyle  the style constant to use. Must be one of the range
1235     * styles listed for the {@link #iterator(Calendar, int)} method.
1236     * @return the date iterator, not null
1237     * @throws IllegalArgumentException if the date is <code>null</code>
1238     * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar}
1239     */
1240    public static Iterator<?> iterator(final Object focus, final int rangeStyle) {
1241        if (focus == null) {
1242            throw new IllegalArgumentException("The date must not be null");
1243        }
1244        if (focus instanceof Date) {
1245            return iterator((Date) focus, rangeStyle);
1246        } else if (focus instanceof Calendar) {
1247            return iterator((Calendar) focus, rangeStyle);
1248        } else {
1249            throw new ClassCastException("Could not iterate based on " + focus);
1250        }
1251    }
1252    
1253    /**
1254     * <p>Returns the number of milliseconds within the 
1255     * fragment. All datefields greater than the fragment will be ignored.</p>
1256     * 
1257     * <p>Asking the milliseconds of any date will only return the number of milliseconds
1258     * of the current second (resulting in a number between 0 and 999). This 
1259     * method will retrieve the number of milliseconds for any fragment. 
1260     * For example, if you want to calculate the number of milliseconds past today, 
1261     * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
1262     * be all milliseconds of the past hour(s), minutes(s) and second(s).</p>
1263     * 
1264     * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1265     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1266     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1267     * A fragment less than or equal to a SECOND field will return 0.</p> 
1268     * 
1269     * <p>
1270     * <ul>
1271     *  <li>January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538</li>
1272     *  <li>January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538</li>
1273     *  <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)</li>
1274     *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1275     *   (a millisecond cannot be split in milliseconds)</li>
1276     * </ul>
1277     * </p>
1278     * 
1279     * @param date the date to work with, not null
1280     * @param fragment the {@code Calendar} field part of date to calculate 
1281     * @return number of milliseconds within the fragment of date
1282     * @throws IllegalArgumentException if the date is <code>null</code> or
1283     * fragment is not supported
1284     * @since 2.4
1285     */
1286    public static long getFragmentInMilliseconds(final Date date, final int fragment) {
1287        return getFragment(date, fragment, Calendar.MILLISECOND);    
1288    }
1289    
1290    /**
1291     * <p>Returns the number of seconds within the 
1292     * fragment. All datefields greater than the fragment will be ignored.</p> 
1293     * 
1294     * <p>Asking the seconds of any date will only return the number of seconds
1295     * of the current minute (resulting in a number between 0 and 59). This 
1296     * method will retrieve the number of seconds for any fragment. 
1297     * For example, if you want to calculate the number of seconds past today, 
1298     * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
1299     * be all seconds of the past hour(s) and minutes(s).</p> 
1300     * 
1301     * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1302     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1303     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1304     * A fragment less than or equal to a SECOND field will return 0.</p> 
1305     * 
1306     * <p>
1307     * <ul>
1308     *  <li>January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
1309     *   (equivalent to deprecated date.getSeconds())</li>
1310     *  <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
1311     *   (equivalent to deprecated date.getSeconds())</li>
1312     *  <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110
1313     *   (7*3600 + 15*60 + 10)</li>
1314     *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1315     *   (a millisecond cannot be split in seconds)</li>
1316     * </ul>
1317     * </p>
1318     * 
1319     * @param date the date to work with, not null
1320     * @param fragment the {@code Calendar} field part of date to calculate 
1321     * @return number of seconds within the fragment of date
1322     * @throws IllegalArgumentException if the date is <code>null</code> or
1323     * fragment is not supported
1324     * @since 2.4
1325     */
1326    public static long getFragmentInSeconds(final Date date, final int fragment) {
1327        return getFragment(date, fragment, Calendar.SECOND);
1328    }
1329    
1330    /**
1331     * <p>Returns the number of minutes within the 
1332     * fragment. All datefields greater than the fragment will be ignored.</p> 
1333     * 
1334     * <p>Asking the minutes of any date will only return the number of minutes
1335     * of the current hour (resulting in a number between 0 and 59). This 
1336     * method will retrieve the number of minutes for any fragment. 
1337     * For example, if you want to calculate the number of minutes past this month, 
1338     * your fragment is Calendar.MONTH. The result will be all minutes of the 
1339     * past day(s) and hour(s).</p> 
1340     * 
1341     * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1342     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1343     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1344     * A fragment less than or equal to a MINUTE field will return 0.</p> 
1345     * 
1346     * <p>
1347     * <ul>
1348     *  <li>January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
1349     *   (equivalent to deprecated date.getMinutes())</li>
1350     *  <li>January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
1351     *   (equivalent to deprecated date.getMinutes())</li>
1352     *  <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15</li>
1353     *  <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)</li>
1354     *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1355     *   (a millisecond cannot be split in minutes)</li>
1356     * </ul>
1357     * </p>
1358     * 
1359     * @param date the date to work with, not null
1360     * @param fragment the {@code Calendar} field part of date to calculate 
1361     * @return number of minutes within the fragment of date
1362     * @throws IllegalArgumentException if the date is <code>null</code> or 
1363     * fragment is not supported
1364     * @since 2.4
1365     */
1366    public static long getFragmentInMinutes(final Date date, final int fragment) {
1367        return getFragment(date, fragment, Calendar.MINUTE);
1368    }
1369    
1370    /**
1371     * <p>Returns the number of hours within the 
1372     * fragment. All datefields greater than the fragment will be ignored.</p> 
1373     * 
1374     * <p>Asking the hours of any date will only return the number of hours
1375     * of the current day (resulting in a number between 0 and 23). This 
1376     * method will retrieve the number of hours for any fragment. 
1377     * For example, if you want to calculate the number of hours past this month, 
1378     * your fragment is Calendar.MONTH. The result will be all hours of the 
1379     * past day(s).</p> 
1380     * 
1381     * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1382     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1383     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1384     * A fragment less than or equal to a HOUR field will return 0.</p> 
1385     * 
1386     * <p>
1387     * <ul>
1388     *  <li>January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
1389     *   (equivalent to deprecated date.getHours())</li>
1390     *  <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
1391     *   (equivalent to deprecated date.getHours())</li>
1392     *  <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7</li>
1393     *  <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)</li>
1394     *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1395     *   (a millisecond cannot be split in hours)</li>
1396     * </ul>
1397     * </p>
1398     * 
1399     * @param date the date to work with, not null
1400     * @param fragment the {@code Calendar} field part of date to calculate 
1401     * @return number of hours within the fragment of date
1402     * @throws IllegalArgumentException if the date is <code>null</code> or 
1403     * fragment is not supported
1404     * @since 2.4
1405     */
1406    public static long getFragmentInHours(final Date date, final int fragment) {
1407        return getFragment(date, fragment, Calendar.HOUR_OF_DAY);
1408    }
1409    
1410    /**
1411     * <p>Returns the number of days within the 
1412     * fragment. All datefields greater than the fragment will be ignored.</p> 
1413     * 
1414     * <p>Asking the days of any date will only return the number of days
1415     * of the current month (resulting in a number between 1 and 31). This 
1416     * method will retrieve the number of days for any fragment. 
1417     * For example, if you want to calculate the number of days past this year, 
1418     * your fragment is Calendar.YEAR. The result will be all days of the 
1419     * past month(s).</p> 
1420     * 
1421     * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1422     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1423     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1424     * A fragment less than or equal to a DAY field will return 0.</p> 
1425     *  
1426     * <p>
1427     * <ul>
1428     *  <li>January 28, 2008 with Calendar.MONTH as fragment will return 28
1429     *   (equivalent to deprecated date.getDay())</li>
1430     *  <li>February 28, 2008 with Calendar.MONTH as fragment will return 28
1431     *   (equivalent to deprecated date.getDay())</li>
1432     *  <li>January 28, 2008 with Calendar.YEAR as fragment will return 28</li>
1433     *  <li>February 28, 2008 with Calendar.YEAR as fragment will return 59</li>
1434     *  <li>January 28, 2008 with Calendar.MILLISECOND as fragment will return 0
1435     *   (a millisecond cannot be split in days)</li>
1436     * </ul>
1437     * </p>
1438     * 
1439     * @param date the date to work with, not null
1440     * @param fragment the {@code Calendar} field part of date to calculate 
1441     * @return number of days  within the fragment of date
1442     * @throws IllegalArgumentException if the date is <code>null</code> or 
1443     * fragment is not supported
1444     * @since 2.4
1445     */
1446    public static long getFragmentInDays(final Date date, final int fragment) {
1447        return getFragment(date, fragment, Calendar.DAY_OF_YEAR);
1448    }
1449
1450    /**
1451     * <p>Returns the number of milliseconds within the 
1452     * fragment. All datefields greater than the fragment will be ignored.</p> 
1453     * 
1454     * <p>Asking the milliseconds of any date will only return the number of milliseconds
1455     * of the current second (resulting in a number between 0 and 999). This 
1456     * method will retrieve the number of milliseconds for any fragment. 
1457     * For example, if you want to calculate the number of seconds past today, 
1458     * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
1459     * be all seconds of the past hour(s), minutes(s) and second(s).</p> 
1460     * 
1461     * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1462     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1463     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1464     * A fragment less than or equal to a MILLISECOND field will return 0.</p> 
1465     * 
1466     * <p>
1467     * <ul>
1468     *  <li>January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
1469     *   (equivalent to calendar.get(Calendar.MILLISECOND))</li>
1470     *  <li>January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
1471     *   (equivalent to calendar.get(Calendar.MILLISECOND))</li>
1472     *  <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538
1473     *   (10*1000 + 538)</li>
1474     *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1475     *   (a millisecond cannot be split in milliseconds)</li>
1476     * </ul>
1477     * </p>
1478     * 
1479     * @param calendar the calendar to work with, not null
1480     * @param fragment the {@code Calendar} field part of calendar to calculate 
1481     * @return number of milliseconds within the fragment of date
1482     * @throws IllegalArgumentException if the date is <code>null</code> or 
1483     * fragment is not supported
1484     * @since 2.4
1485     */
1486  public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) {
1487    return getFragment(calendar, fragment, Calendar.MILLISECOND);
1488  }
1489    /**
1490     * <p>Returns the number of seconds within the 
1491     * fragment. All datefields greater than the fragment will be ignored.</p> 
1492     * 
1493     * <p>Asking the seconds of any date will only return the number of seconds
1494     * of the current minute (resulting in a number between 0 and 59). This 
1495     * method will retrieve the number of seconds for any fragment. 
1496     * For example, if you want to calculate the number of seconds past today, 
1497     * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
1498     * be all seconds of the past hour(s) and minutes(s).</p> 
1499     * 
1500     * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1501     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1502     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1503     * A fragment less than or equal to a SECOND field will return 0.</p> 
1504     * 
1505     * <p>
1506     * <ul>
1507     *  <li>January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
1508     *   (equivalent to calendar.get(Calendar.SECOND))</li>
1509     *  <li>January 6, 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.DAY_OF_YEAR as fragment will return 26110
1512     *   (7*3600 + 15*60 + 10)</li>
1513     *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1514     *   (a millisecond cannot be split in seconds)</li>
1515     * </ul>
1516     * </p>
1517     * 
1518     * @param calendar the calendar to work with, not null
1519     * @param fragment the {@code Calendar} field part of calendar to calculate 
1520     * @return number of seconds within the fragment of date
1521     * @throws IllegalArgumentException if the date is <code>null</code> or 
1522     * fragment is not supported
1523     * @since 2.4
1524     */
1525    public static long getFragmentInSeconds(final Calendar calendar, final int fragment) {
1526        return getFragment(calendar, fragment, Calendar.SECOND);
1527    }
1528    
1529    /**
1530     * <p>Returns the number of minutes within the 
1531     * fragment. All datefields greater than the fragment will be ignored.</p> 
1532     * 
1533     * <p>Asking the minutes of any date will only return the number of minutes
1534     * of the current hour (resulting in a number between 0 and 59). This 
1535     * method will retrieve the number of minutes for any fragment. 
1536     * For example, if you want to calculate the number of minutes past this month, 
1537     * your fragment is Calendar.MONTH. The result will be all minutes of the 
1538     * past day(s) and hour(s).</p> 
1539     * 
1540     * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1541     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1542     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1543     * A fragment less than or equal to a MINUTE field will return 0.</p> 
1544     * 
1545     * <p>
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     * </p>
1557     * 
1558     * @param calendar the calendar to work with, not null
1559     * @param fragment the {@code Calendar} field part of calendar to calculate 
1560     * @return number of minutes within the fragment of date
1561     * @throws IllegalArgumentException if the date is <code>null</code> or 
1562     * fragment is not supported
1563     * @since 2.4
1564     */
1565    public static long getFragmentInMinutes(final Calendar calendar, final int fragment) {
1566        return getFragment(calendar, fragment, Calendar.MINUTE);
1567    }
1568    
1569    /**
1570     * <p>Returns the number of hours within the 
1571     * fragment. All datefields greater than the fragment will be ignored.</p> 
1572     * 
1573     * <p>Asking the hours of any date will only return the number of hours
1574     * of the current day (resulting in a number between 0 and 23). This 
1575     * method will retrieve the number of hours for any fragment. 
1576     * For example, if you want to calculate the number of hours past this month, 
1577     * your fragment is Calendar.MONTH. The result will be all hours of the 
1578     * past day(s).</p> 
1579     * 
1580     * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1581     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1582     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1583     * A fragment less than or equal to a HOUR field will return 0.</p> 
1584     *  
1585     * <p>
1586     * <ul>
1587     *  <li>January 1, 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 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
1590     *   (equivalent to calendar.get(Calendar.HOUR_OF_DAY))</li>
1591     *  <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7</li>
1592     *  <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)</li>
1593     *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1594     *   (a millisecond cannot be split in hours)</li>
1595     * </ul>
1596     * </p>
1597     *  
1598     * @param calendar the calendar to work with, not null
1599     * @param fragment the {@code Calendar} field part of calendar to calculate 
1600     * @return number of hours within the fragment of date
1601     * @throws IllegalArgumentException if the date is <code>null</code> or 
1602     * fragment is not supported
1603     * @since 2.4
1604     */
1605    public static long getFragmentInHours(final Calendar calendar, final int fragment) {
1606        return getFragment(calendar, fragment, Calendar.HOUR_OF_DAY);
1607    }
1608    
1609    /**
1610     * <p>Returns the number of days within the 
1611     * fragment. All datefields greater than the fragment will be ignored.</p> 
1612     * 
1613     * <p>Asking the days of any date will only return the number of days
1614     * of the current month (resulting in a number between 1 and 31). This 
1615     * method will retrieve the number of days for any fragment. 
1616     * For example, if you want to calculate the number of days past this year, 
1617     * your fragment is Calendar.YEAR. The result will be all days of the 
1618     * past month(s).</p> 
1619     * 
1620     * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1621     * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1622     * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1623     * A fragment less than or equal to a DAY field will return 0.</p> 
1624     * 
1625     * <p>
1626     * <ul>
1627     *  <li>January 28, 2008 with Calendar.MONTH as fragment will return 28
1628     *   (equivalent to calendar.get(Calendar.DAY_OF_MONTH))</li>
1629     *  <li>February 28, 2008 with Calendar.MONTH as fragment will return 28
1630     *   (equivalent to calendar.get(Calendar.DAY_OF_MONTH))</li>
1631     *  <li>January 28, 2008 with Calendar.YEAR as fragment will return 28
1632     *   (equivalent to calendar.get(Calendar.DAY_OF_YEAR))</li>
1633     *  <li>February 28, 2008 with Calendar.YEAR as fragment will return 59
1634     *   (equivalent to calendar.get(Calendar.DAY_OF_YEAR))</li>
1635     *  <li>January 28, 2008 with Calendar.MILLISECOND as fragment will return 0
1636     *   (a millisecond cannot be split in days)</li>
1637     * </ul>
1638     * </p>
1639     * 
1640     * @param calendar the calendar to work with, not null
1641     * @param fragment the {@code Calendar} field part of calendar to calculate 
1642     * @return number of days within the fragment of date
1643     * @throws IllegalArgumentException if the date is <code>null</code> or 
1644     * fragment is not supported
1645     * @since 2.4
1646     */
1647    public static long getFragmentInDays(final Calendar calendar, final int fragment) {
1648        return getFragment(calendar, fragment, Calendar.DAY_OF_YEAR);
1649    }
1650    
1651    /**
1652     * Gets a Date fragment for any unit.
1653     * 
1654     * @param date the date to work with, not null
1655     * @param fragment the Calendar field part of date to calculate 
1656     * @param unit the {@code Calendar} field defining the unit
1657     * @return number of units within the fragment of the date
1658     * @throws IllegalArgumentException if the date is <code>null</code> or 
1659     * fragment is not supported
1660     * @since 2.4
1661     */
1662    private static long getFragment(final Date date, final int fragment, final int unit) {
1663        if(date == null) {
1664            throw  new IllegalArgumentException("The date must not be null");
1665        }
1666        final Calendar calendar = Calendar.getInstance();
1667        calendar.setTime(date);
1668        return getFragment(calendar, fragment, unit);
1669    }
1670
1671    /**
1672     * Gets a Calendar fragment for any unit.
1673     * 
1674     * @param calendar the calendar to work with, not null
1675     * @param fragment the Calendar field part of calendar to calculate 
1676     * @param unit the {@code Calendar} field defining the unit
1677     * @return number of units within the fragment of the calendar
1678     * @throws IllegalArgumentException if the date is <code>null</code> or 
1679     * fragment is not supported
1680     * @since 2.4
1681     */
1682    private static long getFragment(final Calendar calendar, final int fragment, final int unit) {
1683        if(calendar == null) {
1684            throw  new IllegalArgumentException("The date must not be null"); 
1685        }
1686        final long millisPerUnit = getMillisPerUnit(unit);
1687        long result = 0;
1688        
1689        // Fragments bigger than a day require a breakdown to days
1690        switch (fragment) {
1691            case Calendar.YEAR:
1692                result += (calendar.get(Calendar.DAY_OF_YEAR) * MILLIS_PER_DAY) / millisPerUnit;
1693                break;
1694            case Calendar.MONTH:
1695                result += (calendar.get(Calendar.DAY_OF_MONTH) * MILLIS_PER_DAY) / millisPerUnit;
1696                break;
1697        }
1698
1699        switch (fragment) {
1700            // Number of days already calculated for these cases
1701            case Calendar.YEAR:
1702            case Calendar.MONTH:
1703            
1704            // The rest of the valid cases
1705            case Calendar.DAY_OF_YEAR:
1706            case Calendar.DATE:
1707                result += (calendar.get(Calendar.HOUR_OF_DAY) * MILLIS_PER_HOUR) / millisPerUnit;
1708                //$FALL-THROUGH$
1709            case Calendar.HOUR_OF_DAY:
1710                result += (calendar.get(Calendar.MINUTE) * MILLIS_PER_MINUTE) / millisPerUnit;
1711                //$FALL-THROUGH$
1712            case Calendar.MINUTE:
1713                result += (calendar.get(Calendar.SECOND) * MILLIS_PER_SECOND) / millisPerUnit;
1714                //$FALL-THROUGH$
1715            case Calendar.SECOND:
1716                result += (calendar.get(Calendar.MILLISECOND) * 1) / millisPerUnit;
1717                break;
1718            case Calendar.MILLISECOND: break;//never useful
1719                default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported");
1720        }
1721        return result;
1722    }
1723    
1724    /**
1725     * Determines if two calendars are equal up to no more than the specified 
1726     * most significant field.
1727     * 
1728     * @param cal1 the first calendar, not <code>null</code>
1729     * @param cal2 the second calendar, not <code>null</code>
1730     * @param field the field from {@code Calendar}
1731     * @return <code>true</code> if equal; otherwise <code>false</code>
1732     * @throws IllegalArgumentException if any argument is <code>null</code>
1733     * @see #truncate(Calendar, int)
1734     * @see #truncatedEquals(Date, Date, int)
1735     * @since 3.0
1736     */
1737    public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) {
1738        return truncatedCompareTo(cal1, cal2, field) == 0;
1739    }
1740
1741    /**
1742     * Determines if two dates are equal up to no more than the specified 
1743     * most significant field.
1744     * 
1745     * @param date1 the first date, not <code>null</code>
1746     * @param date2 the second date, not <code>null</code>
1747     * @param field the field from {@code Calendar}
1748     * @return <code>true</code> if equal; otherwise <code>false</code>
1749     * @throws IllegalArgumentException if any argument is <code>null</code>
1750     * @see #truncate(Date, int)
1751     * @see #truncatedEquals(Calendar, Calendar, int)
1752     * @since 3.0
1753     */
1754    public static boolean truncatedEquals(final Date date1, final Date date2, final int field) {
1755        return truncatedCompareTo(date1, date2, field) == 0;
1756    }
1757
1758    /**
1759     * Determines how two calendars compare up to no more than the specified 
1760     * most significant field.
1761     * 
1762     * @param cal1 the first calendar, not <code>null</code>
1763     * @param cal2 the second calendar, not <code>null</code>
1764     * @param field the field from {@code Calendar}
1765     * @return a negative integer, zero, or a positive integer as the first 
1766     * calendar is less than, equal to, or greater than the second.
1767     * @throws IllegalArgumentException if any argument is <code>null</code>
1768     * @see #truncate(Calendar, int)
1769     * @see #truncatedCompareTo(Date, Date, int)
1770     * @since 3.0
1771     */
1772    public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) {
1773        final Calendar truncatedCal1 = truncate(cal1, field);
1774        final Calendar truncatedCal2 = truncate(cal2, field);
1775        return truncatedCal1.compareTo(truncatedCal2);
1776    }
1777
1778    /**
1779     * Determines how two dates compare up to no more than the specified 
1780     * most significant field.
1781     * 
1782     * @param date1 the first date, not <code>null</code>
1783     * @param date2 the second date, not <code>null</code>
1784     * @param field the field from <code>Calendar</code>
1785     * @return a negative integer, zero, or a positive integer as the first 
1786     * date is less than, equal to, or greater than the second.
1787     * @throws IllegalArgumentException if any argument is <code>null</code>
1788     * @see #truncate(Calendar, int)
1789     * @see #truncatedCompareTo(Date, Date, int)
1790     * @since 3.0
1791     */
1792    public static int truncatedCompareTo(final Date date1, final Date date2, final int field) {
1793        final Date truncatedDate1 = truncate(date1, field);
1794        final Date truncatedDate2 = truncate(date2, field);
1795        return truncatedDate1.compareTo(truncatedDate2);
1796    }
1797
1798    /**
1799     * Returns the number of milliseconds of a {@code Calendar} field, if this is a constant value.
1800     * This handles millisecond, second, minute, hour and day (even though days can very in length).
1801     * 
1802     * @param unit  a {@code Calendar} field constant which is a valid unit for a fragment
1803     * @return the number of milliseconds in the field
1804     * @throws IllegalArgumentException if date can't be represented in milliseconds
1805     * @since 2.4 
1806     */
1807    private static long getMillisPerUnit(final int unit) {
1808        long result = Long.MAX_VALUE;
1809        switch (unit) {
1810            case Calendar.DAY_OF_YEAR:
1811            case Calendar.DATE:
1812                result = MILLIS_PER_DAY;
1813                break;
1814            case Calendar.HOUR_OF_DAY:
1815                result = MILLIS_PER_HOUR;
1816                break;
1817            case Calendar.MINUTE:
1818                result = MILLIS_PER_MINUTE;
1819                break;
1820            case Calendar.SECOND:
1821                result = MILLIS_PER_SECOND;
1822                break;
1823            case Calendar.MILLISECOND:
1824                result = 1;
1825                break;
1826            default: throw new IllegalArgumentException("The unit " + unit + " cannot be represented is milleseconds");
1827        }
1828        return result;
1829    }
1830
1831    //-----------------------------------------------------------------------
1832    /**
1833     * <p>Date iterator.</p>
1834     */
1835    static class DateIterator implements Iterator<Calendar> {
1836        private final Calendar endFinal;
1837        private final Calendar spot;
1838        
1839        /**
1840         * Constructs a DateIterator that ranges from one date to another. 
1841         *
1842         * @param startFinal start date (inclusive)
1843         * @param endFinal end date (inclusive)
1844         */
1845        DateIterator(final Calendar startFinal, final Calendar endFinal) {
1846            super();
1847            this.endFinal = endFinal;
1848            spot = startFinal;
1849            spot.add(Calendar.DATE, -1);
1850        }
1851
1852        /**
1853         * Has the iterator not reached the end date yet?
1854         *
1855         * @return <code>true</code> if the iterator has yet to reach the end date
1856         */
1857        @Override
1858        public boolean hasNext() {
1859            return spot.before(endFinal);
1860        }
1861
1862        /**
1863         * Return the next calendar in the iteration
1864         *
1865         * @return Object calendar for the next date
1866         */
1867        @Override
1868        public Calendar next() {
1869            if (spot.equals(endFinal)) {
1870                throw new NoSuchElementException();
1871            }
1872            spot.add(Calendar.DATE, 1);
1873            return (Calendar) spot.clone();
1874        }
1875
1876        /**
1877         * Always throws UnsupportedOperationException.
1878         * 
1879         * @throws UnsupportedOperationException
1880         * @see java.util.Iterator#remove()
1881         */
1882        @Override
1883        public void remove() {
1884            throw new UnsupportedOperationException();
1885        }
1886    }
1887
1888}