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