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