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