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