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