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