View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3.time;
18  
19  import java.text.ParseException;
20  import java.text.ParsePosition;
21  import java.text.SimpleDateFormat;
22  import java.util.Calendar;
23  import java.util.Date;
24  import java.util.Iterator;
25  import java.util.Locale;
26  import java.util.NoSuchElementException;
27  import java.util.concurrent.TimeUnit;
28  
29  /**
30   * <p>A suite of utilities surrounding the use of the
31   * {@link java.util.Calendar} and {@link java.util.Date} object.</p>
32   * 
33   * <p>DateUtils contains a lot of common methods considering manipulations
34   * of Dates or Calendars. Some methods require some extra explanation.
35   * The truncate, ceiling and round methods could be considered the Math.floor(),
36   * Math.ceil() or Math.round versions for dates
37   * This way date-fields will be ignored in bottom-up order.
38   * As a complement to these methods we've introduced some fragment-methods.
39   * With these methods the Date-fields will be ignored in top-down order.
40   * Since a date without a year is not a valid date, you have to decide in what
41   * kind of date-field you want your result, for instance milliseconds or days.
42   * </p>
43   *
44   * @since 2.0
45   * @version $Id: DateUtils.java 1583482 2014-03-31 22:54:57Z niallp $
46   */
47  public class DateUtils {
48  
49      /**
50       * Number of milliseconds in a standard second.
51       * @since 2.1
52       */
53      public static final long MILLIS_PER_SECOND = 1000;
54      /**
55       * Number of milliseconds in a standard minute.
56       * @since 2.1
57       */
58      public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
59      /**
60       * Number of milliseconds in a standard hour.
61       * @since 2.1
62       */
63      public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
64      /**
65       * Number of milliseconds in a standard day.
66       * @since 2.1
67       */
68      public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
69  
70      /**
71       * This is half a month, so this represents whether a date is in the top
72       * or bottom half of the month.
73       */
74      public static final int SEMI_MONTH = 1001;
75  
76      private static final int[][] fields = {
77              {Calendar.MILLISECOND},
78              {Calendar.SECOND},
79              {Calendar.MINUTE},
80              {Calendar.HOUR_OF_DAY, Calendar.HOUR},
81              {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM 
82                  /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */
83              },
84              {Calendar.MONTH, DateUtils.SEMI_MONTH},
85              {Calendar.YEAR},
86              {Calendar.ERA}};
87  
88      /**
89       * A week range, starting on Sunday.
90       */
91      public static final int RANGE_WEEK_SUNDAY = 1;
92      /**
93       * A week range, starting on Monday.
94       */
95      public static final int RANGE_WEEK_MONDAY = 2;
96      /**
97       * A week range, starting on the day focused.
98       */
99      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      * </p>
688      * <ul>
689      * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
690      * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
691      * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
692      * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
693      * </ul>
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      * </p>
724      * <ul>
725      * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
726      * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
727      * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
728      * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
729      * </ul>
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      * </p>
760      * <ul>
761      * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
762      * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
763      * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
764      * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
765      * </ul>
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      * <ul>
1275      *  <li>January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538</li>
1276      *  <li>January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538</li>
1277      *  <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)</li>
1278      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1279      *   (a millisecond cannot be split in milliseconds)</li>
1280      * </ul>
1281      * 
1282      * @param date the date to work with, not null
1283      * @param fragment the {@code Calendar} field part of date to calculate 
1284      * @return number of milliseconds within the fragment of date
1285      * @throws IllegalArgumentException if the date is <code>null</code> or
1286      * fragment is not supported
1287      * @since 2.4
1288      */
1289     public static long getFragmentInMilliseconds(final Date date, final int fragment) {
1290         return getFragment(date, fragment, TimeUnit.MILLISECONDS);    
1291     }
1292     
1293     /**
1294      * <p>Returns the number of seconds within the 
1295      * fragment. All datefields greater than the fragment will be ignored.</p> 
1296      * 
1297      * <p>Asking the seconds of any date will only return the number of seconds
1298      * of the current minute (resulting in a number between 0 and 59). This 
1299      * method will retrieve the number of seconds for any fragment. 
1300      * For example, if you want to calculate the number of seconds past today, 
1301      * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
1302      * be all seconds of the past hour(s) and minutes(s).</p> 
1303      * 
1304      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1305      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1306      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1307      * A fragment less than or equal to a SECOND field will return 0.</p> 
1308      * 
1309      * <ul>
1310      *  <li>January 1, 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.MINUTE as fragment will return 10
1313      *   (equivalent to deprecated date.getSeconds())</li>
1314      *  <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110
1315      *   (7*3600 + 15*60 + 10)</li>
1316      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1317      *   (a millisecond cannot be split in seconds)</li>
1318      * </ul>
1319      * 
1320      * @param date the date to work with, not null
1321      * @param fragment the {@code Calendar} field part of date to calculate 
1322      * @return number of seconds within the fragment of date
1323      * @throws IllegalArgumentException if the date is <code>null</code> or
1324      * fragment is not supported
1325      * @since 2.4
1326      */
1327     public static long getFragmentInSeconds(final Date date, final int fragment) {
1328         return getFragment(date, fragment, TimeUnit.SECONDS);
1329     }
1330     
1331     /**
1332      * <p>Returns the number of minutes within the 
1333      * fragment. All datefields greater than the fragment will be ignored.</p> 
1334      * 
1335      * <p>Asking the minutes of any date will only return the number of minutes
1336      * of the current hour (resulting in a number between 0 and 59). This 
1337      * method will retrieve the number of minutes for any fragment. 
1338      * For example, if you want to calculate the number of minutes past this month, 
1339      * your fragment is Calendar.MONTH. The result will be all minutes of the 
1340      * past day(s) and hour(s).</p> 
1341      * 
1342      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1343      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1344      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1345      * A fragment less than or equal to a MINUTE field will return 0.</p> 
1346      * 
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      * 
1358      * @param date the date to work with, not null
1359      * @param fragment the {@code Calendar} field part of date to calculate 
1360      * @return number of minutes within the fragment of date
1361      * @throws IllegalArgumentException if the date is <code>null</code> or 
1362      * fragment is not supported
1363      * @since 2.4
1364      */
1365     public static long getFragmentInMinutes(final Date date, final int fragment) {
1366         return getFragment(date, fragment, TimeUnit.MINUTES);
1367     }
1368     
1369     /**
1370      * <p>Returns the number of hours within the 
1371      * fragment. All datefields greater than the fragment will be ignored.</p> 
1372      * 
1373      * <p>Asking the hours of any date will only return the number of hours
1374      * of the current day (resulting in a number between 0 and 23). This 
1375      * method will retrieve the number of hours for any fragment. 
1376      * For example, if you want to calculate the number of hours past this month, 
1377      * your fragment is Calendar.MONTH. The result will be all hours of the 
1378      * past day(s).</p> 
1379      * 
1380      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1381      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1382      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1383      * A fragment less than or equal to a HOUR field will return 0.</p> 
1384      * 
1385      * <ul>
1386      *  <li>January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
1387      *   (equivalent to deprecated date.getHours())</li>
1388      *  <li>January 6, 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 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7</li>
1391      *  <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)</li>
1392      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1393      *   (a millisecond cannot be split in hours)</li>
1394      * </ul>
1395      * 
1396      * @param date the date to work with, not null
1397      * @param fragment the {@code Calendar} field part of date to calculate 
1398      * @return number of hours within the fragment of date
1399      * @throws IllegalArgumentException if the date is <code>null</code> or 
1400      * fragment is not supported
1401      * @since 2.4
1402      */
1403     public static long getFragmentInHours(final Date date, final int fragment) {
1404         return getFragment(date, fragment, TimeUnit.HOURS);
1405     }
1406     
1407     /**
1408      * <p>Returns the number of days within the 
1409      * fragment. All datefields greater than the fragment will be ignored.</p> 
1410      * 
1411      * <p>Asking the days of any date will only return the number of days
1412      * of the current month (resulting in a number between 1 and 31). This 
1413      * method will retrieve the number of days for any fragment. 
1414      * For example, if you want to calculate the number of days past this year, 
1415      * your fragment is Calendar.YEAR. The result will be all days of the 
1416      * past month(s).</p> 
1417      * 
1418      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1419      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1420      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1421      * A fragment less than or equal to a DAY field will return 0.</p> 
1422      *  
1423      * <ul>
1424      *  <li>January 28, 2008 with Calendar.MONTH as fragment will return 28
1425      *   (equivalent to deprecated date.getDay())</li>
1426      *  <li>February 28, 2008 with Calendar.MONTH as fragment will return 28
1427      *   (equivalent to deprecated date.getDay())</li>
1428      *  <li>January 28, 2008 with Calendar.YEAR as fragment will return 28</li>
1429      *  <li>February 28, 2008 with Calendar.YEAR as fragment will return 59</li>
1430      *  <li>January 28, 2008 with Calendar.MILLISECOND as fragment will return 0
1431      *   (a millisecond cannot be split in days)</li>
1432      * </ul>
1433      * 
1434      * @param date the date to work with, not null
1435      * @param fragment the {@code Calendar} field part of date to calculate 
1436      * @return number of days  within the fragment of date
1437      * @throws IllegalArgumentException if the date is <code>null</code> or 
1438      * fragment is not supported
1439      * @since 2.4
1440      */
1441     public static long getFragmentInDays(final Date date, final int fragment) {
1442         return getFragment(date, fragment, TimeUnit.DAYS);
1443     }
1444 
1445     /**
1446      * <p>Returns the number of milliseconds within the 
1447      * fragment. All datefields greater than the fragment will be ignored.</p> 
1448      * 
1449      * <p>Asking the milliseconds of any date will only return the number of milliseconds
1450      * of the current second (resulting in a number between 0 and 999). This 
1451      * method will retrieve the number of milliseconds for any fragment. 
1452      * For example, if you want to calculate the number of seconds past today, 
1453      * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
1454      * be all seconds of the past hour(s), minutes(s) and second(s).</p> 
1455      * 
1456      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1457      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1458      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1459      * A fragment less than or equal to a MILLISECOND field will return 0.</p> 
1460      * 
1461      * <ul>
1462      *  <li>January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
1463      *   (equivalent to calendar.get(Calendar.MILLISECOND))</li>
1464      *  <li>January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538
1465      *   (equivalent to calendar.get(Calendar.MILLISECOND))</li>
1466      *  <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538
1467      *   (10*1000 + 538)</li>
1468      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1469      *   (a millisecond cannot be split in milliseconds)</li>
1470      * </ul>
1471      * 
1472      * @param calendar the calendar to work with, not null
1473      * @param fragment the {@code Calendar} field part of calendar to calculate 
1474      * @return number of milliseconds within the fragment of date
1475      * @throws IllegalArgumentException if the date is <code>null</code> or 
1476      * fragment is not supported
1477      * @since 2.4
1478      */
1479   public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) {
1480     return getFragment(calendar, fragment, TimeUnit.MILLISECONDS);
1481   }
1482     /**
1483      * <p>Returns the number of seconds within the 
1484      * fragment. All datefields greater than the fragment will be ignored.</p> 
1485      * 
1486      * <p>Asking the seconds of any date will only return the number of seconds
1487      * of the current minute (resulting in a number between 0 and 59). This 
1488      * method will retrieve the number of seconds for any fragment. 
1489      * For example, if you want to calculate the number of seconds past today, 
1490      * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will
1491      * be all seconds of the past hour(s) and minutes(s).</p> 
1492      * 
1493      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1494      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1495      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1496      * A fragment less than or equal to a SECOND field will return 0.</p> 
1497      * 
1498      * <ul>
1499      *  <li>January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
1500      *   (equivalent to calendar.get(Calendar.SECOND))</li>
1501      *  <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10
1502      *   (equivalent to calendar.get(Calendar.SECOND))</li>
1503      *  <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110
1504      *   (7*3600 + 15*60 + 10)</li>
1505      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1506      *   (a millisecond cannot be split in seconds)</li>
1507      * </ul>
1508      * 
1509      * @param calendar the calendar to work with, not null
1510      * @param fragment the {@code Calendar} field part of calendar to calculate 
1511      * @return number of seconds within the fragment of date
1512      * @throws IllegalArgumentException if the date is <code>null</code> or 
1513      * fragment is not supported
1514      * @since 2.4
1515      */
1516     public static long getFragmentInSeconds(final Calendar calendar, final int fragment) {
1517         return getFragment(calendar, fragment, TimeUnit.SECONDS);
1518     }
1519     
1520     /**
1521      * <p>Returns the number of minutes within the 
1522      * fragment. All datefields greater than the fragment will be ignored.</p> 
1523      * 
1524      * <p>Asking the minutes of any date will only return the number of minutes
1525      * of the current hour (resulting in a number between 0 and 59). This 
1526      * method will retrieve the number of minutes for any fragment. 
1527      * For example, if you want to calculate the number of minutes past this month, 
1528      * your fragment is Calendar.MONTH. The result will be all minutes of the 
1529      * past day(s) and hour(s).</p> 
1530      * 
1531      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1532      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1533      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1534      * A fragment less than or equal to a MINUTE field will return 0.</p> 
1535      * 
1536      * <ul>
1537      *  <li>January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
1538      *   (equivalent to calendar.get(Calendar.MINUTES))</li>
1539      *  <li>January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15
1540      *   (equivalent to calendar.get(Calendar.MINUTES))</li>
1541      *  <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15</li>
1542      *  <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)</li>
1543      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1544      *   (a millisecond cannot be split in minutes)</li>
1545      * </ul>
1546      * 
1547      * @param calendar the calendar to work with, not null
1548      * @param fragment the {@code Calendar} field part of calendar to calculate 
1549      * @return number of minutes within the fragment of date
1550      * @throws IllegalArgumentException if the date is <code>null</code> or 
1551      * fragment is not supported
1552      * @since 2.4
1553      */
1554     public static long getFragmentInMinutes(final Calendar calendar, final int fragment) {
1555         return getFragment(calendar, fragment, TimeUnit.MINUTES);
1556     }
1557     
1558     /**
1559      * <p>Returns the number of hours within the 
1560      * fragment. All datefields greater than the fragment will be ignored.</p> 
1561      * 
1562      * <p>Asking the hours of any date will only return the number of hours
1563      * of the current day (resulting in a number between 0 and 23). This 
1564      * method will retrieve the number of hours for any fragment. 
1565      * For example, if you want to calculate the number of hours past this month, 
1566      * your fragment is Calendar.MONTH. The result will be all hours of the 
1567      * past day(s).</p> 
1568      * 
1569      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1570      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1571      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1572      * A fragment less than or equal to a HOUR field will return 0.</p> 
1573      *  
1574      * <ul>
1575      *  <li>January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
1576      *   (equivalent to calendar.get(Calendar.HOUR_OF_DAY))</li>
1577      *  <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7
1578      *   (equivalent to calendar.get(Calendar.HOUR_OF_DAY))</li>
1579      *  <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7</li>
1580      *  <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)</li>
1581      *  <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0
1582      *   (a millisecond cannot be split in hours)</li>
1583      * </ul>
1584      *  
1585      * @param calendar the calendar to work with, not null
1586      * @param fragment the {@code Calendar} field part of calendar to calculate 
1587      * @return number of hours within the fragment of date
1588      * @throws IllegalArgumentException if the date is <code>null</code> or 
1589      * fragment is not supported
1590      * @since 2.4
1591      */
1592     public static long getFragmentInHours(final Calendar calendar, final int fragment) {
1593         return getFragment(calendar, fragment, TimeUnit.HOURS);
1594     }
1595     
1596     /**
1597      * <p>Returns the number of days within the 
1598      * fragment. All datefields greater than the fragment will be ignored.</p> 
1599      * 
1600      * <p>Asking the days of any date will only return the number of days
1601      * of the current month (resulting in a number between 1 and 31). This 
1602      * method will retrieve the number of days for any fragment. 
1603      * For example, if you want to calculate the number of days past this year, 
1604      * your fragment is Calendar.YEAR. The result will be all days of the 
1605      * past month(s).</p> 
1606      * 
1607      * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 
1608      * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 
1609      * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND
1610      * A fragment less than or equal to a DAY field will return 0.</p> 
1611      * 
1612      * <ul>
1613      *  <li>January 28, 2008 with Calendar.MONTH as fragment will return 28
1614      *   (equivalent to calendar.get(Calendar.DAY_OF_MONTH))</li>
1615      *  <li>February 28, 2008 with Calendar.MONTH as fragment will return 28
1616      *   (equivalent to calendar.get(Calendar.DAY_OF_MONTH))</li>
1617      *  <li>January 28, 2008 with Calendar.YEAR as fragment will return 28
1618      *   (equivalent to calendar.get(Calendar.DAY_OF_YEAR))</li>
1619      *  <li>February 28, 2008 with Calendar.YEAR as fragment will return 59
1620      *   (equivalent to calendar.get(Calendar.DAY_OF_YEAR))</li>
1621      *  <li>January 28, 2008 with Calendar.MILLISECOND as fragment will return 0
1622      *   (a millisecond cannot be split in days)</li>
1623      * </ul>
1624      * 
1625      * @param calendar the calendar to work with, not null
1626      * @param fragment the {@code Calendar} field part of calendar to calculate 
1627      * @return number of days within the fragment of date
1628      * @throws IllegalArgumentException if the date is <code>null</code> or 
1629      * fragment is not supported
1630      * @since 2.4
1631      */
1632     public static long getFragmentInDays(final Calendar calendar, final int fragment) {
1633         return getFragment(calendar, fragment, TimeUnit.DAYS);
1634     }
1635     
1636     /**
1637      * Gets a Date fragment for any unit.
1638      * 
1639      * @param date the date to work with, not null
1640      * @param fragment the Calendar field part of date to calculate 
1641      * @param unit the time unit
1642      * @return number of units within the fragment of the date
1643      * @throws IllegalArgumentException if the date is <code>null</code> or 
1644      * fragment is not supported
1645      * @since 2.4
1646      */
1647     private static long getFragment(final Date date, final int fragment, final TimeUnit unit) {
1648         if(date == null) {
1649             throw  new IllegalArgumentException("The date must not be null");
1650         }
1651         final Calendar calendar = Calendar.getInstance();
1652         calendar.setTime(date);
1653         return getFragment(calendar, fragment, unit);
1654     }
1655 
1656     /**
1657      * Gets a Calendar fragment for any unit.
1658      * 
1659      * @param calendar the calendar to work with, not null
1660      * @param fragment the Calendar field part of calendar to calculate 
1661      * @param unit the time unit
1662      * @return number of units within the fragment of the calendar
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 Calendar calendar, final int fragment, final TimeUnit unit) {
1668         if(calendar == null) {
1669             throw  new IllegalArgumentException("The date must not be null"); 
1670         }
1671 
1672         long result = 0;
1673         
1674         int offset = (unit == TimeUnit.DAYS) ? 0 : 1;
1675         
1676         // Fragments bigger than a day require a breakdown to days
1677         switch (fragment) {
1678             case Calendar.YEAR:
1679                 result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS);
1680                 break;
1681             case Calendar.MONTH:
1682                 result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS);
1683                 break;
1684             default:
1685                 break;
1686         }
1687 
1688         switch (fragment) {
1689             // Number of days already calculated for these cases
1690             case Calendar.YEAR:
1691             case Calendar.MONTH:
1692             
1693             // The rest of the valid cases
1694             case Calendar.DAY_OF_YEAR:
1695             case Calendar.DATE:
1696                 result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS);
1697                 //$FALL-THROUGH$
1698             case Calendar.HOUR_OF_DAY:
1699                 result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES);
1700                 //$FALL-THROUGH$
1701             case Calendar.MINUTE:
1702                 result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS);
1703                 //$FALL-THROUGH$
1704             case Calendar.SECOND:
1705                 result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS);
1706                 break;
1707             case Calendar.MILLISECOND: break;//never useful
1708                 default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported");
1709         }
1710         return result;
1711     }
1712     
1713     /**
1714      * Determines if two calendars are equal up to no more than the specified 
1715      * most significant field.
1716      * 
1717      * @param cal1 the first calendar, not <code>null</code>
1718      * @param cal2 the second calendar, not <code>null</code>
1719      * @param field the field from {@code Calendar}
1720      * @return <code>true</code> if equal; otherwise <code>false</code>
1721      * @throws IllegalArgumentException if any argument is <code>null</code>
1722      * @see #truncate(Calendar, int)
1723      * @see #truncatedEquals(Date, Date, int)
1724      * @since 3.0
1725      */
1726     public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) {
1727         return truncatedCompareTo(cal1, cal2, field) == 0;
1728     }
1729 
1730     /**
1731      * Determines if two dates are equal up to no more than the specified 
1732      * most significant field.
1733      * 
1734      * @param date1 the first date, not <code>null</code>
1735      * @param date2 the second date, not <code>null</code>
1736      * @param field the field from {@code Calendar}
1737      * @return <code>true</code> if equal; otherwise <code>false</code>
1738      * @throws IllegalArgumentException if any argument is <code>null</code>
1739      * @see #truncate(Date, int)
1740      * @see #truncatedEquals(Calendar, Calendar, int)
1741      * @since 3.0
1742      */
1743     public static boolean truncatedEquals(final Date date1, final Date date2, final int field) {
1744         return truncatedCompareTo(date1, date2, field) == 0;
1745     }
1746 
1747     /**
1748      * Determines how two calendars compare up to no more than the specified 
1749      * most significant field.
1750      * 
1751      * @param cal1 the first calendar, not <code>null</code>
1752      * @param cal2 the second calendar, not <code>null</code>
1753      * @param field the field from {@code Calendar}
1754      * @return a negative integer, zero, or a positive integer as the first 
1755      * calendar is less than, equal to, or greater than the second.
1756      * @throws IllegalArgumentException if any argument is <code>null</code>
1757      * @see #truncate(Calendar, int)
1758      * @see #truncatedCompareTo(Date, Date, int)
1759      * @since 3.0
1760      */
1761     public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) {
1762         final Calendar truncatedCal1 = truncate(cal1, field);
1763         final Calendar truncatedCal2 = truncate(cal2, field);
1764         return truncatedCal1.compareTo(truncatedCal2);
1765     }
1766 
1767     /**
1768      * Determines how two dates compare up to no more than the specified 
1769      * most significant field.
1770      * 
1771      * @param date1 the first date, not <code>null</code>
1772      * @param date2 the second date, not <code>null</code>
1773      * @param field the field from <code>Calendar</code>
1774      * @return a negative integer, zero, or a positive integer as the first 
1775      * date 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 Date date1, final Date date2, final int field) {
1782         final Date truncatedDate1 = truncate(date1, field);
1783         final Date truncatedDate2 = truncate(date2, field);
1784         return truncatedDate1.compareTo(truncatedDate2);
1785     }
1786 
1787 
1788     //-----------------------------------------------------------------------
1789     /**
1790      * <p>Date iterator.</p>
1791      */
1792     static class DateIterator implements Iterator<Calendar> {
1793         private final Calendar endFinal;
1794         private final Calendar spot;
1795         
1796         /**
1797          * Constructs a DateIterator that ranges from one date to another. 
1798          *
1799          * @param startFinal start date (inclusive)
1800          * @param endFinal end date (inclusive)
1801          */
1802         DateIterator(final Calendar startFinal, final Calendar endFinal) {
1803             super();
1804             this.endFinal = endFinal;
1805             spot = startFinal;
1806             spot.add(Calendar.DATE, -1);
1807         }
1808 
1809         /**
1810          * Has the iterator not reached the end date yet?
1811          *
1812          * @return <code>true</code> if the iterator has yet to reach the end date
1813          */
1814         @Override
1815         public boolean hasNext() {
1816             return spot.before(endFinal);
1817         }
1818 
1819         /**
1820          * Return the next calendar in the iteration
1821          *
1822          * @return Object calendar for the next date
1823          */
1824         @Override
1825         public Calendar next() {
1826             if (spot.equals(endFinal)) {
1827                 throw new NoSuchElementException();
1828             }
1829             spot.add(Calendar.DATE, 1);
1830             return (Calendar) spot.clone();
1831         }
1832 
1833         /**
1834          * Always throws UnsupportedOperationException.
1835          * 
1836          * @throws UnsupportedOperationException
1837          * @see java.util.Iterator#remove()
1838          */
1839         @Override
1840         public void remove() {
1841             throw new UnsupportedOperationException();
1842         }
1843     }
1844 
1845 }