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