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