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