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