KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > lang > time > DateUtils


1 /*
2  * Copyright 2002-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.commons.lang.time;
17
18 import java.text.ParseException JavaDoc;
19 import java.text.ParsePosition JavaDoc;
20 import java.text.SimpleDateFormat JavaDoc;
21 import java.util.Calendar JavaDoc;
22 import java.util.Date JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.NoSuchElementException JavaDoc;
25 import java.util.TimeZone JavaDoc;
26
27 /**
28  * <p>A suite of utilities surrounding the use of the
29  * {@link java.util.Calendar} and {@link java.util.Date} object.</p>
30  *
31  * @author <a HREF="mailto:sergek@lokitech.com">Serge Knystautas</a>
32  * @author Stephen Colebourne
33  * @author Janek Bogucki
34  * @author <a HREF="mailto:ggregory@seagullsw.com">Gary Gregory</a>
35  * @author Phil Steitz
36  * @since 2.0
37  * @version $Id: DateUtils.java 161243 2005-04-14 04:30:28Z ggregory $
38  */

39 public class DateUtils {
40     
41     /**
42      * The UTC time zone (often referred to as GMT).
43      */

44     public static final TimeZone JavaDoc UTC_TIME_ZONE = TimeZone.getTimeZone("GMT");
45     /**
46      * Number of milliseconds in a standard second.
47      * @since 2.1
48      */

49     public static final long MILLIS_PER_SECOND = 1000;
50     /**
51      * Number of milliseconds in a standard minute.
52      * @since 2.1
53      */

54     public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
55     /**
56      * Number of milliseconds in a standard hour.
57      * @since 2.1
58      */

59     public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
60     /**
61      * Number of milliseconds in a standard day.
62      * @since 2.1
63      */

64     public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
65
66     /**
67      * This is half a month, so this represents whether a date is in the top
68      * or bottom half of the month.
69      */

70     public final static int SEMI_MONTH = 1001;
71
72     private static final int[][] fields = {
73             {Calendar.MILLISECOND},
74             {Calendar.SECOND},
75             {Calendar.MINUTE},
76             {Calendar.HOUR_OF_DAY, Calendar.HOUR},
77             {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM
78                 /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */
79             },
80             {Calendar.MONTH, DateUtils.SEMI_MONTH},
81             {Calendar.YEAR},
82             {Calendar.ERA}};
83
84     /**
85      * A week range, starting on Sunday.
86      */

87     public final static int RANGE_WEEK_SUNDAY = 1;
88
89     /**
90      * A week range, starting on Monday.
91      */

92     public final static int RANGE_WEEK_MONDAY = 2;
93
94     /**
95      * A week range, starting on the day focused.
96      */

97     public final static int RANGE_WEEK_RELATIVE = 3;
98
99     /**
100      * A week range, centered around the day focused.
101      */

102     public final static int RANGE_WEEK_CENTER = 4;
103
104     /**
105      * A month range, the week starting on Sunday.
106      */

107     public final static int RANGE_MONTH_SUNDAY = 5;
108
109     /**
110      * A month range, the week starting on Monday.
111      */

112     public final static int RANGE_MONTH_MONDAY = 6;
113
114     /**
115      * <p><code>DateUtils</code> instances should NOT be constructed in
116      * standard programming. Instead, the class should be used as
117      * <code>DateUtils.parse(str);</code>.</p>
118      *
119      * <p>This constructor is public to permit tools that require a JavaBean
120      * instance to operate.</p>
121      */

122     public DateUtils() {
123     }
124
125     //-----------------------------------------------------------------------
126
/**
127      * <p>Checks if two date objects are on the same day ignoring time.</p>
128      *
129      * <p>28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true.
130      * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false.
131      * </p>
132      *
133      * @param date1 the first date, not altered, not null
134      * @param date2 the second date, not altered, not null
135      * @return true if they represent the same day
136      * @throws IllegalArgumentException if either date is <code>null</code>
137      * @since 2.1
138      */

139     public static boolean isSameDay(Date JavaDoc date1, Date JavaDoc date2) {
140         if (date1 == null || date2 == null) {
141             throw new IllegalArgumentException JavaDoc("The date must not be null");
142         }
143         Calendar JavaDoc cal1 = Calendar.getInstance();
144         cal1.setTime(date1);
145         Calendar JavaDoc cal2 = Calendar.getInstance();
146         cal2.setTime(date2);
147         return isSameDay(cal1, cal2);
148     }
149
150     /**
151      * <p>Checks if two calendar objects are on the same day ignoring time.</p>
152      *
153      * <p>28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true.
154      * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false.
155      * </p>
156      *
157      * @param cal1 the first calendar, not altered, not null
158      * @param cal2 the second calendar, not altered, not null
159      * @return true if they represent the same day
160      * @throws IllegalArgumentException if either calendar is <code>null</code>
161      * @since 2.1
162      */

163     public static boolean isSameDay(Calendar JavaDoc cal1, Calendar JavaDoc cal2) {
164         if (cal1 == null || cal2 == null) {
165             throw new IllegalArgumentException JavaDoc("The date must not be null");
166         }
167         return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
168                 cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
169                 cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR));
170     }
171
172     //-----------------------------------------------------------------------
173
/**
174      * <p>Checks if two date objects represent the same instant in time.</p>
175      *
176      * <p>This method compares the long millisecond time of the two objects.</p>
177      *
178      * @param date1 the first date, not altered, not null
179      * @param date2 the second date, not altered, not null
180      * @return true if they represent the same millisecond instant
181      * @throws IllegalArgumentException if either date is <code>null</code>
182      * @since 2.1
183      */

184     public static boolean isSameInstant(Date JavaDoc date1, Date JavaDoc date2) {
185         if (date1 == null || date2 == null) {
186             throw new IllegalArgumentException JavaDoc("The date must not be null");
187         }
188         return date1.getTime() == date2.getTime();
189     }
190
191     /**
192      * <p>Checks if two calendar objects represent the same instant in time.</p>
193      *
194      * <p>This method compares the long millisecond time of the two objects.</p>
195      *
196      * @param cal1 the first calendar, not altered, not null
197      * @param cal2 the second calendar, not altered, not null
198      * @return true if they represent the same millisecond instant
199      * @throws IllegalArgumentException if either date is <code>null</code>
200      * @since 2.1
201      */

202     public static boolean isSameInstant(Calendar JavaDoc cal1, Calendar JavaDoc cal2) {
203         if (cal1 == null || cal2 == null) {
204             throw new IllegalArgumentException JavaDoc("The date must not be null");
205         }
206         return cal1.getTime().getTime() == cal2.getTime().getTime();
207     }
208
209     //-----------------------------------------------------------------------
210
/**
211      * <p>Checks if two calendar objects represent the same local time.</p>
212      *
213      * <p>This method compares the values of the fields of the two objects.
214      * In addition, both calendars must be the same of the same type.</p>
215      *
216      * @param cal1 the first calendar, not altered, not null
217      * @param cal2 the second calendar, not altered, not null
218      * @return true if they represent the same millisecond instant
219      * @throws IllegalArgumentException if either date is <code>null</code>
220      * @since 2.1
221      */

222     public static boolean isSameLocalTime(Calendar JavaDoc cal1, Calendar JavaDoc cal2) {
223         if (cal1 == null || cal2 == null) {
224             throw new IllegalArgumentException JavaDoc("The date must not be null");
225         }
226         return (cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) &&
227                 cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) &&
228                 cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) &&
229                 cal1.get(Calendar.HOUR) == cal2.get(Calendar.HOUR) &&
230                 cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) &&
231                 cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
232                 cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
233                 cal1.getClass() == cal2.getClass());
234     }
235
236     //-----------------------------------------------------------------------
237
/**
238      * <p>Parses a string representing a date by trying a variety of different parsers.</p>
239      *
240      * <p>The parse will try each parse pattern in turn.
241      * A parse is only deemed sucessful if it parses the whole of the input string.
242      * If no parse patterns match, a ParseException is thrown.</p>
243      *
244      * @param str the date to parse, not null
245      * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null
246      * @return the parsed date
247      * @throws IllegalArgumentException if the date string or pattern array is null
248      * @throws ParseException if none of the date patterns were suitable
249      */

250     public static Date JavaDoc parseDate(String JavaDoc str, String JavaDoc[] parsePatterns) throws ParseException JavaDoc {
251         if (str == null || parsePatterns == null) {
252             throw new IllegalArgumentException JavaDoc("Date and Patterns must not be null");
253         }
254         
255         SimpleDateFormat JavaDoc parser = null;
256         ParsePosition JavaDoc pos = new ParsePosition JavaDoc(0);
257         for (int i = 0; i < parsePatterns.length; i++) {
258             if (i == 0) {
259                 parser = new SimpleDateFormat JavaDoc(parsePatterns[0]);
260             } else {
261                 parser.applyPattern(parsePatterns[i]);
262             }
263             pos.setIndex(0);
264             Date JavaDoc date = parser.parse(str, pos);
265             if (date != null && pos.getIndex() == str.length()) {
266                 return date;
267             }
268         }
269         throw new ParseException JavaDoc("Unable to parse the date: " + str, -1);
270     }
271
272     //-----------------------------------------------------------------------
273
/**
274      * <p>Round this date, leaving the field specified as the most
275      * significant field.</p>
276      *
277      * <p>For example, if you had the datetime of 28 Mar 2002
278      * 13:45:01.231, if this was passed with HOUR, it would return
279      * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it
280      * would return 1 April 2002 0:00:00.000.</p>
281      *
282      * <p>For a date in a timezone that handles the change to daylight
283      * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows.
284      * Suppose daylight saving time begins at 02:00 on March 30. Rounding a
285      * date that crosses this time would produce the following values:
286      * <ul>
287      * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
288      * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
289      * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
290      * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
291      * </ul>
292      * </p>
293      *
294      * @param date the date to work with
295      * @param field the field from <code>Calendar</code>
296      * or <code>SEMI_MONTH</code>
297      * @return the rounded date
298      * @throws IllegalArgumentException if the date is <code>null</code>
299      * @throws ArithmeticException if the year is over 280 million
300      */

301     public static Date JavaDoc round(Date JavaDoc date, int field) {
302         if (date == null) {
303             throw new IllegalArgumentException JavaDoc("The date must not be null");
304         }
305         Calendar JavaDoc gval = Calendar.getInstance();
306         gval.setTime(date);
307         modify(gval, field, true);
308         return gval.getTime();
309     }
310
311     /**
312      * <p>Round this date, leaving the field specified as the most
313      * significant field.</p>
314      *
315      * <p>For example, if you had the datetime of 28 Mar 2002
316      * 13:45:01.231, if this was passed with HOUR, it would return
317      * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it
318      * would return 1 April 2002 0:00:00.000.</p>
319      *
320      * <p>For a date in a timezone that handles the change to daylight
321      * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows.
322      * Suppose daylight saving time begins at 02:00 on March 30. Rounding a
323      * date that crosses this time would produce the following values:
324      * <ul>
325      * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
326      * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
327      * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
328      * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
329      * </ul>
330      * </p>
331      *
332      * @param date the date to work with
333      * @param field the field from <code>Calendar</code>
334      * or <code>SEMI_MONTH</code>
335      * @return the rounded date (a different object)
336      * @throws IllegalArgumentException if the date is <code>null</code>
337      * @throws ArithmeticException if the year is over 280 million
338      */

339     public static Calendar JavaDoc round(Calendar JavaDoc date, int field) {
340         if (date == null) {
341             throw new IllegalArgumentException JavaDoc("The date must not be null");
342         }
343         Calendar JavaDoc rounded = (Calendar JavaDoc) date.clone();
344         modify(rounded, field, true);
345         return rounded;
346     }
347
348     /**
349      * <p>Round this date, leaving the field specified as the most
350      * significant field.</p>
351      *
352      * <p>For example, if you had the datetime of 28 Mar 2002
353      * 13:45:01.231, if this was passed with HOUR, it would return
354      * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it
355      * would return 1 April 2002 0:00:00.000.</p>
356      *
357      * <p>For a date in a timezone that handles the change to daylight
358      * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows.
359      * Suppose daylight saving time begins at 02:00 on March 30. Rounding a
360      * date that crosses this time would produce the following values:
361      * <ul>
362      * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li>
363      * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li>
364      * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li>
365      * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li>
366      * </ul>
367      * </p>
368      *
369      * @param date the date to work with, either Date or Calendar
370      * @param field the field from <code>Calendar</code>
371      * or <code>SEMI_MONTH</code>
372      * @return the rounded date
373      * @throws IllegalArgumentException if the date is <code>null</code>
374      * @throws ClassCastException if the object type is not a <code>Date</code>
375      * or <code>Calendar</code>
376      * @throws ArithmeticException if the year is over 280 million
377      */

378     public static Date JavaDoc round(Object JavaDoc date, int field) {
379         if (date == null) {
380             throw new IllegalArgumentException JavaDoc("The date must not be null");
381         }
382         if (date instanceof Date JavaDoc) {
383             return round((Date JavaDoc) date, field);
384         } else if (date instanceof Calendar JavaDoc) {
385             return round((Calendar JavaDoc) date, field).getTime();
386         } else {
387             throw new ClassCastException JavaDoc("Could not round " + date);
388         }
389     }
390
391     //-----------------------------------------------------------------------
392
/**
393      * <p>Truncate this date, leaving the field specified as the most
394      * significant field.</p>
395      *
396      * <p>For example, if you had the datetime of 28 Mar 2002
397      * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
398      * 2002 13:00:00.000. If this was passed with MONTH, it would
399      * return 1 Mar 2002 0:00:00.000.</p>
400      *
401      * @param date the date to work with
402      * @param field the field from <code>Calendar</code>
403      * or <code>SEMI_MONTH</code>
404      * @return the rounded date
405      * @throws IllegalArgumentException if the date is <code>null</code>
406      * @throws ArithmeticException if the year is over 280 million
407      */

408     public static Date JavaDoc truncate(Date JavaDoc date, int field) {
409         if (date == null) {
410             throw new IllegalArgumentException JavaDoc("The date must not be null");
411         }
412         Calendar JavaDoc gval = Calendar.getInstance();
413         gval.setTime(date);
414         modify(gval, field, false);
415         return gval.getTime();
416     }
417
418     /**
419      * <p>Truncate this date, leaving the field specified as the most
420      * significant field.</p>
421      *
422      * <p>For example, if you had the datetime of 28 Mar 2002
423      * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
424      * 2002 13:00:00.000. If this was passed with MONTH, it would
425      * return 1 Mar 2002 0:00:00.000.</p>
426      *
427      * @param date the date to work with
428      * @param field the field from <code>Calendar</code>
429      * or <code>SEMI_MONTH</code>
430      * @return the rounded date (a different object)
431      * @throws IllegalArgumentException if the date is <code>null</code>
432      * @throws ArithmeticException if the year is over 280 million
433      */

434     public static Calendar JavaDoc truncate(Calendar JavaDoc date, int field) {
435         if (date == null) {
436             throw new IllegalArgumentException JavaDoc("The date must not be null");
437         }
438         Calendar JavaDoc truncated = (Calendar JavaDoc) date.clone();
439         modify(truncated, field, false);
440         return truncated;
441     }
442
443     /**
444      * <p>Truncate this date, leaving the field specified as the most
445      * significant field.</p>
446      *
447      * <p>For example, if you had the datetime of 28 Mar 2002
448      * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
449      * 2002 13:00:00.000. If this was passed with MONTH, it would
450      * return 1 Mar 2002 0:00:00.000.</p>
451      *
452      * @param date the date to work with, either <code>Date</code>
453      * or <code>Calendar</code>
454      * @param field the field from <code>Calendar</code>
455      * or <code>SEMI_MONTH</code>
456      * @return the rounded date
457      * @throws IllegalArgumentException if the date
458      * is <code>null</code>
459      * @throws ClassCastException if the object type is not a
460      * <code>Date</code> or <code>Calendar</code>
461      * @throws ArithmeticException if the year is over 280 million
462      */

463     public static Date JavaDoc truncate(Object JavaDoc date, int field) {
464         if (date == null) {
465             throw new IllegalArgumentException JavaDoc("The date must not be null");
466         }
467         if (date instanceof Date JavaDoc) {
468             return truncate((Date JavaDoc) date, field);
469         } else if (date instanceof Calendar JavaDoc) {
470             return truncate((Calendar JavaDoc) date, field).getTime();
471         } else {
472             throw new ClassCastException JavaDoc("Could not truncate " + date);
473         }
474     }
475
476     //-----------------------------------------------------------------------
477
/**
478      * <p>Internal calculation method.</p>
479      *
480      * @param val the calendar
481      * @param field the field constant
482      * @param round true to round, false to truncate
483      * @throws ArithmeticException if the year is over 280 million
484      */

485     private static void modify(Calendar JavaDoc val, int field, boolean round) {
486         if (val.get(Calendar.YEAR) > 280000000) {
487             throw new ArithmeticException JavaDoc("Calendar value too large for accurate calculations");
488         }
489         
490         boolean roundUp = false;
491         for (int i = 0; i < fields.length; i++) {
492             for (int j = 0; j < fields[i].length; j++) {
493                 if (fields[i][j] == field) {
494                     //This is our field... we stop looping
495
if (round && roundUp) {
496                         if (field == DateUtils.SEMI_MONTH) {
497                             //This is a special case that's hard to generalize
498
//If the date is 1, we round up to 16, otherwise
499
// we subtract 15 days and add 1 month
500
if (val.get(Calendar.DATE) == 1) {
501                                 val.add(Calendar.DATE, 15);
502                             } else {
503                                 val.add(Calendar.DATE, -15);
504                                 val.add(Calendar.MONTH, 1);
505                             }
506                         } else {
507                             //We need at add one to this field since the
508
// last number causes us to round up
509
val.add(fields[i][0], 1);
510                         }
511                     }
512                     return;
513                 }
514             }
515             //We have various fields that are not easy roundings
516
int offset = 0;
517             boolean offsetSet = false;
518             //These are special types of fields that require different rounding rules
519
switch (field) {
520                 case DateUtils.SEMI_MONTH:
521                     if (fields[i][0] == Calendar.DATE) {
522                         //If we're going to drop the DATE field's value,
523
// we want to do this our own way.
524
//We need to subtrace 1 since the date has a minimum of 1
525
offset = val.get(Calendar.DATE) - 1;
526                         //If we're above 15 days adjustment, that means we're in the
527
// bottom half of the month and should stay accordingly.
528
if (offset >= 15) {
529                             offset -= 15;
530                         }
531                         //Record whether we're in the top or bottom half of that range
532
roundUp = offset > 7;
533                         offsetSet = true;
534                     }
535                     break;
536                 case Calendar.AM_PM:
537                     if (fields[i][0] == Calendar.HOUR_OF_DAY) {
538                         //If we're going to drop the HOUR field's value,
539
// we want to do this our own way.
540
offset = val.get(Calendar.HOUR_OF_DAY);
541                         if (offset >= 12) {
542                             offset -= 12;
543                         }
544                         roundUp = offset > 6;
545                         offsetSet = true;
546                     }
547                     break;
548             }
549             if (!offsetSet) {
550                 int min = val.getActualMinimum(fields[i][0]);
551                 int max = val.getActualMaximum(fields[i][0]);
552                 //Calculate the offset from the minimum allowed value
553
offset = val.get(fields[i][0]) - min;
554                 //Set roundUp if this is more than half way between the minimum and maximum
555
roundUp = offset > ((max - min) / 2);
556             }
557             //We need to remove this field
558
val.set(fields[i][0], val.get(fields[i][0]) - offset);
559         }
560         throw new IllegalArgumentException JavaDoc("The field " + field + " is not supported");
561
562     }
563
564     //-----------------------------------------------------------------------
565
/**
566      * <p>This constructs an <code>Iterator</code> that will
567      * start and stop over a date range based on the focused
568      * date and the range style.</p>
569      *
570      * <p>For instance, passing Thursday, July 4, 2002 and a
571      * <code>RANGE_MONTH_SUNDAY</code> will return an
572      * <code>Iterator</code> that starts with Sunday, June 30,
573      * 2002 and ends with Saturday, August 3, 2002.
574      *
575      * @param focus the date to work with
576      * @param rangeStyle the style constant to use. Must be one of the range
577      * styles listed for the {@link #iterator(Calendar, int)} method.
578      *
579      * @return the date iterator
580      * @throws IllegalArgumentException if the date is <code>null</code> or if
581      * the rangeStyle is not
582      */

583     public static Iterator JavaDoc iterator(Date JavaDoc focus, int rangeStyle) {
584         if (focus == null) {
585             throw new IllegalArgumentException JavaDoc("The date must not be null");
586         }
587         Calendar JavaDoc gval = Calendar.getInstance();
588         gval.setTime(focus);
589         return iterator(gval, rangeStyle);
590     }
591
592     /**
593      * <p>This constructs an <code>Iterator</code> that will
594      * start and stop over a date range based on the focused
595      * date and the range style.</p>
596      *
597      * <p>For instance, passing Thursday, July 4, 2002 and a
598      * <code>RANGE_MONTH_SUNDAY</code> will return an
599      * <code>Iterator</code> that starts with Sunday, June 30,
600      * 2002 and ends with Saturday, August 3, 2002.
601      *
602      * @param focus the date to work with
603      * @param rangeStyle the style constant to use. Must be one of
604      * {@link DateUtils#RANGE_MONTH_SUNDAY},
605      * {@link DateUtils#RANGE_MONTH_MONDAY},
606      * {@link DateUtils#RANGE_WEEK_SUNDAY},
607      * {@link DateUtils#RANGE_WEEK_MONDAY},
608      * {@link DateUtils#RANGE_WEEK_RELATIVE},
609      * {@link DateUtils#RANGE_WEEK_CENTER}
610      * @return the date iterator
611      * @throws IllegalArgumentException if the date is <code>null</code>
612      */

613     public static Iterator JavaDoc iterator(Calendar JavaDoc focus, int rangeStyle) {
614         if (focus == null) {
615             throw new IllegalArgumentException JavaDoc("The date must not be null");
616         }
617         Calendar JavaDoc start = null;
618         Calendar JavaDoc end = null;
619         int startCutoff = Calendar.SUNDAY;
620         int endCutoff = Calendar.SATURDAY;
621         switch (rangeStyle) {
622             case RANGE_MONTH_SUNDAY:
623             case RANGE_MONTH_MONDAY:
624                 //Set start to the first of the month
625
start = truncate(focus, Calendar.MONTH);
626                 //Set end to the last of the month
627
end = (Calendar JavaDoc) start.clone();
628                 end.add(Calendar.MONTH, 1);
629                 end.add(Calendar.DATE, -1);
630                 //Loop start back to the previous sunday or monday
631
if (rangeStyle == RANGE_MONTH_MONDAY) {
632                     startCutoff = Calendar.MONDAY;
633                     endCutoff = Calendar.SUNDAY;
634                 }
635                 break;
636             case RANGE_WEEK_SUNDAY:
637             case RANGE_WEEK_MONDAY:
638             case RANGE_WEEK_RELATIVE:
639             case RANGE_WEEK_CENTER:
640                 //Set start and end to the current date
641
start = truncate(focus, Calendar.DATE);
642                 end = truncate(focus, Calendar.DATE);
643                 switch (rangeStyle) {
644                     case RANGE_WEEK_SUNDAY:
645                         //already set by default
646
break;
647                     case RANGE_WEEK_MONDAY:
648                         startCutoff = Calendar.MONDAY;
649                         endCutoff = Calendar.SUNDAY;
650                         break;
651                     case RANGE_WEEK_RELATIVE:
652                         startCutoff = focus.get(Calendar.DAY_OF_WEEK);
653                         endCutoff = startCutoff - 1;
654                         break;
655                     case RANGE_WEEK_CENTER:
656                         startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3;
657                         endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3;
658                         break;
659                 }
660                 break;
661             default:
662                 throw new IllegalArgumentException JavaDoc("The range style " + rangeStyle + " is not valid.");
663         }
664         if (startCutoff < Calendar.SUNDAY) {
665             startCutoff += 7;
666         }
667         if (startCutoff > Calendar.SATURDAY) {
668             startCutoff -= 7;
669         }
670         if (endCutoff < Calendar.SUNDAY) {
671             endCutoff += 7;
672         }
673         if (endCutoff > Calendar.SATURDAY) {
674             endCutoff -= 7;
675         }
676         while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) {
677             start.add(Calendar.DATE, -1);
678         }
679         while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) {
680             end.add(Calendar.DATE, 1);
681         }
682         return new DateIterator(start, end);
683     }
684
685     /**
686      * <p>This constructs an <code>Iterator</code> that will
687      * start and stop over a date range based on the focused
688      * date and the range style.</p>
689      *
690      * <p>For instance, passing Thursday, July 4, 2002 and a
691      * <code>RANGE_MONTH_SUNDAY</code> will return an
692      * <code>Iterator</code> that starts with Sunday, June 30,
693      * 2002 and ends with Saturday, August 3, 2002.</p>
694      *
695      * @param focus the date to work with, either
696      * <code>Date</code> or <code>Calendar</code>
697      * @param rangeStyle the style constant to use. Must be one of the range
698      * styles listed for the {@link #iterator(Calendar, int)} method.
699      * @return the date iterator
700      * @throws IllegalArgumentException if the date
701      * is <code>null</code>
702      * @throws ClassCastException if the object type is
703      * not a <code>Date</code> or <code>Calendar</code>
704      */

705     public static Iterator JavaDoc iterator(Object JavaDoc focus, int rangeStyle) {
706         if (focus == null) {
707             throw new IllegalArgumentException JavaDoc("The date must not be null");
708         }
709         if (focus instanceof Date JavaDoc) {
710             return iterator((Date JavaDoc) focus, rangeStyle);
711         } else if (focus instanceof Calendar JavaDoc) {
712             return iterator((Calendar JavaDoc) focus, rangeStyle);
713         } else {
714             throw new ClassCastException JavaDoc("Could not iterate based on " + focus);
715         }
716     }
717
718     /**
719      * <p>Date iterator.</p>
720      */

721     static class DateIterator implements Iterator JavaDoc {
722         private final Calendar JavaDoc endFinal;
723         private final Calendar JavaDoc spot;
724         
725         /**
726          * Constructs a DateIterator that ranges from one date to another.
727          *
728          * @param startFinal start date (inclusive)
729          * @param endFinal end date (not inclusive)
730          */

731         DateIterator(Calendar JavaDoc startFinal, Calendar JavaDoc endFinal) {
732             super();
733             this.endFinal = endFinal;
734             spot = startFinal;
735             spot.add(Calendar.DATE, -1);
736         }
737
738         /**
739          * Has the iterator not reached the end date yet?
740          *
741          * @return <code>true</code> if the iterator has yet to reach the end date
742          */

743         public boolean hasNext() {
744             return spot.before(endFinal);
745         }
746
747         /**
748          * Return the next calendar in the iteration
749          *
750          * @return Object calendar for the next date
751          */

752         public Object JavaDoc next() {
753             if (spot.equals(endFinal)) {
754                 throw new NoSuchElementException JavaDoc();
755             }
756             spot.add(Calendar.DATE, 1);
757             return spot.clone();
758         }
759
760         /**
761          * Always throws UnsupportedOperationException.
762          *
763          * @throws UnsupportedOperationException
764          * @see java.util.Iterator#remove()
765          */

766         public void remove() {
767             throw new UnsupportedOperationException JavaDoc();
768         }
769     }
770     
771     //-------------------------------------------------------------------------
772
// Deprecated int constants
773
// TODO: Remove in 3.0
774

775     /**
776      * Number of milliseconds in a standard second.
777      *
778      * @deprecated Use MILLIS_PER_SECOND. This will be removed in Commons Lang 3.0.
779      */

780     public static final int MILLIS_IN_SECOND = 1000;
781     /**
782      * Number of milliseconds in a standard minute.
783      *
784      * @deprecated Use MILLIS_PER_MINUTE. This will be removed in Commons Lang 3.0.
785      */

786     public static final int MILLIS_IN_MINUTE = 60 * 1000;
787     /**
788      * Number of milliseconds in a standard hour.
789      *
790      * @deprecated Use MILLIS_PER_HOUR. This will be removed in Commons Lang 3.0.
791      */

792     public static final int MILLIS_IN_HOUR = 60 * 60 * 1000;
793     /**
794      * Number of milliseconds in a standard day.
795      *
796      * @deprecated Use MILLIS_PER_DAY. This will be removed in Commons Lang 3.0.
797      */

798     public static final int MILLIS_IN_DAY = 24 * 60 * 60 * 1000;
799     
800 }
801
Popular Tags