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