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