source: src/main/java/agents/org/apache/commons/lang/time/DateUtils.java

Last change on this file was 127, checked in by Wouter Pasman, 6 years ago

#41 ROLL BACK of rev.126 . So this version is equal to rev. 125

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