001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.lang3.time; 018 019import java.text.ParseException; 020import java.text.ParsePosition; 021import java.time.LocalDateTime; 022import java.time.OffsetDateTime; 023import java.time.ZoneId; 024import java.time.ZonedDateTime; 025import java.util.Calendar; 026import java.util.Date; 027import java.util.Iterator; 028import java.util.Locale; 029import java.util.NoSuchElementException; 030import java.util.Objects; 031import java.util.TimeZone; 032import java.util.concurrent.TimeUnit; 033 034import org.apache.commons.lang3.LocaleUtils; 035 036/** 037 * A suite of utilities surrounding the use of the 038 * {@link java.util.Calendar} and {@link java.util.Date} object. 039 * 040 * <p> 041 * DateUtils contains a lot of common methods considering manipulations 042 * of Dates or Calendars. Some methods require some extra explanation. 043 * The truncate, ceiling and round methods could be considered the Math.floor(), 044 * Math.ceil() or Math.round versions for dates 045 * This way date-fields will be ignored in bottom-up order. 046 * As a complement to these methods we've introduced some fragment-methods. 047 * With these methods the Date-fields will be ignored in top-down order. 048 * Since a date without a year is not a valid date, you have to decide in what 049 * kind of date-field you want your result, for instance milliseconds or days. 050 * </p> 051 * <p> 052 * Several methods are provided for adding to {@link Date} objects, of the form 053 * {@code addXXX(Date date, int amount)}. It is important to note these methods 054 * use a {@link Calendar} internally (with default time zone and locale) and may 055 * be affected by changes to daylight saving time (DST). 056 * </p> 057 * 058 * @since 2.0 059 */ 060public class DateUtils { 061 062 /** 063 * Date iterator. 064 */ 065 static final class DateIterator implements Iterator<Calendar> { 066 private final Calendar endFinal; 067 private final Calendar spot; 068 069 /** 070 * Constructs a DateIterator that ranges from one date to another. 071 * 072 * @param startFinal start date (inclusive). 073 * @param endFinal end date (inclusive). 074 */ 075 DateIterator(final Calendar startFinal, final Calendar endFinal) { 076 this.endFinal = endFinal; 077 spot = startFinal; 078 spot.add(Calendar.DATE, -1); 079 } 080 081 /** 082 * Tests whether the iterator has more dates before the end date. 083 * 084 * @return {@code true} if the iterator has yet to reach the end date. 085 */ 086 @Override 087 public boolean hasNext() { 088 return spot.before(endFinal); 089 } 090 091 /** 092 * Returns the next calendar in the iteration. 093 * 094 * @return Object calendar for the next date. 095 */ 096 @Override 097 public Calendar next() { 098 if (spot.equals(endFinal)) { 099 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}