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