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</code> 164 * @since 2.1 165 */ 166 public static boolean isSameDay(final Date date1, final Date date2) { 167 if (date1 == null || date2 == null) { 168 throw new IllegalArgumentException("The date must not be null"); 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</code> 188 * @since 2.1 189 */ 190 public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { 191 if (cal1 == null || cal2 == null) { 192 throw new IllegalArgumentException("The date must not be null"); 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</code> 209 * @since 2.1 210 */ 211 public static boolean isSameInstant(final Date date1, final Date date2) { 212 if (date1 == null || date2 == null) { 213 throw new IllegalArgumentException("The date must not be null"); 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</code> 227 * @since 2.1 228 */ 229 public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { 230 if (cal1 == null || cal2 == null) { 231 throw new IllegalArgumentException("The date must not be null"); 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</code> 247 * @since 2.1 248 */ 249 public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { 250 if (cal1 == null || cal2 == null) { 251 throw new IllegalArgumentException("The date must not be null"); 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</code>, 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</code>, 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 interpretting 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( 365 final String str, final Locale locale, final String[] parsePatterns, 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 IllegalArgumentException 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</code> 738 * @return the different rounded date, not null 739 * @throws IllegalArgumentException if the date is <code>null</code> 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 new IllegalArgumentException("The date must not be null"); 745 } 746 final Calendar rounded = (Calendar) date.clone(); 747 modify(rounded, field, ModifyType.ROUND); 748 return rounded; 749 } 750 751 /** 752 * <p>Rounds a date, leaving the field specified as the most 753 * significant field.</p> 754 * 755 * <p>For example, if you had the date-time of 28 Mar 2002 756 * 13:45:01.231, if this was passed with HOUR, it would return 757 * 28 Mar 2002 14:00:00.000. If this was passed with MONTH, it 758 * would return 1 April 2002 0:00:00.000.</p> 759 * 760 * <p>For a date in a timezone that handles the change to daylight 761 * saving time, rounding to Calendar.HOUR_OF_DAY will behave as follows. 762 * Suppose daylight saving time begins at 02:00 on March 30. Rounding a 763 * date that crosses this time would produce the following values: 764 * </p> 765 * <ul> 766 * <li>March 30, 2003 01:10 rounds to March 30, 2003 01:00</li> 767 * <li>March 30, 2003 01:40 rounds to March 30, 2003 03:00</li> 768 * <li>March 30, 2003 02:10 rounds to March 30, 2003 03:00</li> 769 * <li>March 30, 2003 02:40 rounds to March 30, 2003 04:00</li> 770 * </ul> 771 * 772 * @param date the date to work with, either {@code Date} or {@code Calendar}, not null 773 * @param field the field from {@code Calendar} or <code>SEMI_MONTH</code> 774 * @return the different rounded date, not null 775 * @throws IllegalArgumentException if the date is <code>null</code> 776 * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} 777 * @throws ArithmeticException if the year is over 280 million 778 */ 779 public static Date round(final Object date, final int field) { 780 if (date == null) { 781 throw new IllegalArgumentException("The date must not be null"); 782 } 783 if (date instanceof Date) { 784 return round((Date) date, field); 785 } else if (date instanceof Calendar) { 786 return round((Calendar) date, field).getTime(); 787 } else { 788 throw new ClassCastException("Could not round " + date); 789 } 790 } 791 792 //----------------------------------------------------------------------- 793 /** 794 * <p>Truncates a date, leaving the field specified as the most 795 * significant field.</p> 796 * 797 * <p>For example, if you had the date-time of 28 Mar 2002 798 * 13:45:01.231, if you passed with HOUR, it would return 28 Mar 799 * 2002 13:00:00.000. If this was passed with MONTH, it would 800 * return 1 Mar 2002 0:00:00.000.</p> 801 * 802 * @param date the date to work with, not null 803 * @param field the field from {@code Calendar} or <code>SEMI_MONTH</code> 804 * @return the different truncated date, not null 805 * @throws IllegalArgumentException if the date is <code>null</code> 806 * @throws ArithmeticException if the year is over 280 million 807 */ 808 public static Date truncate(final Date date, final int field) { 809 validateDateNotNull(date); 810 final Calendar gval = Calendar.getInstance(); 811 gval.setTime(date); 812 modify(gval, field, ModifyType.TRUNCATE); 813 return gval.getTime(); 814 } 815 816 /** 817 * <p>Truncates a date, leaving the field specified as the most 818 * significant field.</p> 819 * 820 * <p>For example, if you had the date-time of 28 Mar 2002 821 * 13:45:01.231, if you passed with HOUR, it would return 28 Mar 822 * 2002 13:00:00.000. If this was passed with MONTH, it would 823 * return 1 Mar 2002 0:00:00.000.</p> 824 * 825 * @param date the date to work with, not null 826 * @param field the field from {@code Calendar} or <code>SEMI_MONTH</code> 827 * @return the different truncated date, not null 828 * @throws IllegalArgumentException if the date is <code>null</code> 829 * @throws ArithmeticException if the year is over 280 million 830 */ 831 public static Calendar truncate(final Calendar date, final int field) { 832 if (date == null) { 833 throw new IllegalArgumentException("The date must not be null"); 834 } 835 final Calendar truncated = (Calendar) date.clone(); 836 modify(truncated, field, ModifyType.TRUNCATE); 837 return truncated; 838 } 839 840 /** 841 * <p>Truncates a date, leaving the field specified as the most 842 * significant field.</p> 843 * 844 * <p>For example, if you had the date-time of 28 Mar 2002 845 * 13:45:01.231, if you passed with HOUR, it would return 28 Mar 846 * 2002 13:00:00.000. If this was passed with MONTH, it would 847 * return 1 Mar 2002 0:00:00.000.</p> 848 * 849 * @param date the date to work with, either {@code Date} or {@code Calendar}, not null 850 * @param field the field from {@code Calendar} or <code>SEMI_MONTH</code> 851 * @return the different truncated date, not null 852 * @throws IllegalArgumentException if the date is <code>null</code> 853 * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} 854 * @throws ArithmeticException if the year is over 280 million 855 */ 856 public static Date truncate(final Object date, final int field) { 857 if (date == null) { 858 throw new IllegalArgumentException("The date must not be null"); 859 } 860 if (date instanceof Date) { 861 return truncate((Date) date, field); 862 } else if (date instanceof Calendar) { 863 return truncate((Calendar) date, field).getTime(); 864 } else { 865 throw new ClassCastException("Could not truncate " + date); 866 } 867 } 868 869 //----------------------------------------------------------------------- 870 /** 871 * <p>Gets a date ceiling, leaving the field specified as the most 872 * significant field.</p> 873 * 874 * <p>For example, if you had the date-time of 28 Mar 2002 875 * 13:45:01.231, if you passed with HOUR, it would return 28 Mar 876 * 2002 14:00:00.000. If this was passed with MONTH, it would 877 * return 1 Apr 2002 0:00:00.000.</p> 878 * 879 * @param date the date to work with, not null 880 * @param field the field from {@code Calendar} or <code>SEMI_MONTH</code> 881 * @return the different ceil date, not null 882 * @throws IllegalArgumentException if the date is <code>null</code> 883 * @throws ArithmeticException if the year is over 280 million 884 * @since 2.5 885 */ 886 public static Date ceiling(final Date date, final int field) { 887 validateDateNotNull(date); 888 final Calendar gval = Calendar.getInstance(); 889 gval.setTime(date); 890 modify(gval, field, ModifyType.CEILING); 891 return gval.getTime(); 892 } 893 894 /** 895 * <p>Gets a date ceiling, leaving the field specified as the most 896 * significant field.</p> 897 * 898 * <p>For example, if you had the date-time of 28 Mar 2002 899 * 13:45:01.231, if you passed with HOUR, it would return 28 Mar 900 * 2002 14:00:00.000. If this was passed with MONTH, it would 901 * return 1 Apr 2002 0:00:00.000.</p> 902 * 903 * @param date the date to work with, not null 904 * @param field the field from {@code Calendar} or <code>SEMI_MONTH</code> 905 * @return the different ceil date, not null 906 * @throws IllegalArgumentException if the date is <code>null</code> 907 * @throws ArithmeticException if the year is over 280 million 908 * @since 2.5 909 */ 910 public static Calendar ceiling(final Calendar date, final int field) { 911 if (date == null) { 912 throw new IllegalArgumentException("The date must not be null"); 913 } 914 final Calendar ceiled = (Calendar) date.clone(); 915 modify(ceiled, field, ModifyType.CEILING); 916 return ceiled; 917 } 918 919 /** 920 * <p>Gets a date ceiling, leaving the field specified as the most 921 * significant field.</p> 922 * 923 * <p>For example, if you had the date-time of 28 Mar 2002 924 * 13:45:01.231, if you passed with HOUR, it would return 28 Mar 925 * 2002 14:00:00.000. If this was passed with MONTH, it would 926 * return 1 Apr 2002 0:00:00.000.</p> 927 * 928 * @param date the date to work with, either {@code Date} or {@code Calendar}, not null 929 * @param field the field from {@code Calendar} or <code>SEMI_MONTH</code> 930 * @return the different ceil date, not null 931 * @throws IllegalArgumentException if the date is <code>null</code> 932 * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} 933 * @throws ArithmeticException if the year is over 280 million 934 * @since 2.5 935 */ 936 public static Date ceiling(final Object date, final int field) { 937 if (date == null) { 938 throw new IllegalArgumentException("The date must not be null"); 939 } 940 if (date instanceof Date) { 941 return ceiling((Date) date, field); 942 } else if (date instanceof Calendar) { 943 return ceiling((Calendar) date, field).getTime(); 944 } else { 945 throw new ClassCastException("Could not find ceiling of for type: " + date.getClass()); 946 } 947 } 948 949 //----------------------------------------------------------------------- 950 /** 951 * <p>Internal calculation method.</p> 952 * 953 * @param val the calendar, not null 954 * @param field the field constant 955 * @param modType type to truncate, round or ceiling 956 * @throws ArithmeticException if the year is over 280 million 957 */ 958 private static void modify(final Calendar val, final int field, final ModifyType modType) { 959 if (val.get(Calendar.YEAR) > 280000000) { 960 throw new ArithmeticException("Calendar value too large for accurate calculations"); 961 } 962 963 if (field == Calendar.MILLISECOND) { 964 return; 965 } 966 967 // ----------------- Fix for LANG-59 ---------------------- START --------------- 968 // see http://issues.apache.org/jira/browse/LANG-59 969 // 970 // Manually truncate milliseconds, seconds and minutes, rather than using 971 // Calendar methods. 972 973 final Date date = val.getTime(); 974 long time = date.getTime(); 975 boolean done = false; 976 977 // truncate milliseconds 978 final int millisecs = val.get(Calendar.MILLISECOND); 979 if (ModifyType.TRUNCATE == modType || millisecs < 500) { 980 time = time - millisecs; 981 } 982 if (field == Calendar.SECOND) { 983 done = true; 984 } 985 986 // truncate seconds 987 final int seconds = val.get(Calendar.SECOND); 988 if (!done && (ModifyType.TRUNCATE == modType || seconds < 30)) { 989 time = time - (seconds * 1000L); 990 } 991 if (field == Calendar.MINUTE) { 992 done = true; 993 } 994 995 // truncate minutes 996 final int minutes = val.get(Calendar.MINUTE); 997 if (!done && (ModifyType.TRUNCATE == modType || minutes < 30)) { 998 time = time - (minutes * 60000L); 999 } 1000 1001 // reset time 1002 if (date.getTime() != time) { 1003 date.setTime(time); 1004 val.setTime(date); 1005 } 1006 // ----------------- Fix for LANG-59 ----------------------- END ---------------- 1007 1008 boolean roundUp = false; 1009 for (final int[] aField : fields) { 1010 for (final int element : aField) { 1011 if (element == field) { 1012 //This is our field... we stop looping 1013 if (modType == ModifyType.CEILING || modType == ModifyType.ROUND && roundUp) { 1014 if (field == SEMI_MONTH) { 1015 //This is a special case that's hard to generalize 1016 //If the date is 1, we round up to 16, otherwise 1017 // we subtract 15 days and add 1 month 1018 if (val.get(Calendar.DATE) == 1) { 1019 val.add(Calendar.DATE, 15); 1020 } else { 1021 val.add(Calendar.DATE, -15); 1022 val.add(Calendar.MONTH, 1); 1023 } 1024// ----------------- Fix for LANG-440 ---------------------- START --------------- 1025 } else if (field == Calendar.AM_PM) { 1026 // This is a special case 1027 // If the time is 0, we round up to 12, otherwise 1028 // we subtract 12 hours and add 1 day 1029 if (val.get(Calendar.HOUR_OF_DAY) == 0) { 1030 val.add(Calendar.HOUR_OF_DAY, 12); 1031 } else { 1032 val.add(Calendar.HOUR_OF_DAY, -12); 1033 val.add(Calendar.DATE, 1); 1034 } 1035// ----------------- Fix for LANG-440 ---------------------- END --------------- 1036 } else { 1037 //We need at add one to this field since the 1038 // last number causes us to round up 1039 val.add(aField[0], 1); 1040 } 1041 } 1042 return; 1043 } 1044 } 1045 //We have various fields that are not easy roundings 1046 int offset = 0; 1047 boolean offsetSet = false; 1048 //These are special types of fields that require different rounding rules 1049 switch (field) { 1050 case SEMI_MONTH: 1051 if (aField[0] == Calendar.DATE) { 1052 //If we're going to drop the DATE field's value, 1053 // we want to do this our own way. 1054 //We need to subtrace 1 since the date has a minimum of 1 1055 offset = val.get(Calendar.DATE) - 1; 1056 //If we're above 15 days adjustment, that means we're in the 1057 // bottom half of the month and should stay accordingly. 1058 if (offset >= 15) { 1059 offset -= 15; 1060 } 1061 //Record whether we're in the top or bottom half of that range 1062 roundUp = offset > 7; 1063 offsetSet = true; 1064 } 1065 break; 1066 case Calendar.AM_PM: 1067 if (aField[0] == Calendar.HOUR_OF_DAY) { 1068 //If we're going to drop the HOUR field's value, 1069 // we want to do this our own way. 1070 offset = val.get(Calendar.HOUR_OF_DAY); 1071 if (offset >= 12) { 1072 offset -= 12; 1073 } 1074 roundUp = offset >= 6; 1075 offsetSet = true; 1076 } 1077 break; 1078 default: 1079 break; 1080 } 1081 if (!offsetSet) { 1082 final int min = val.getActualMinimum(aField[0]); 1083 final int max = val.getActualMaximum(aField[0]); 1084 //Calculate the offset from the minimum allowed value 1085 offset = val.get(aField[0]) - min; 1086 //Set roundUp if this is more than half way between the minimum and maximum 1087 roundUp = offset > ((max - min) / 2); 1088 } 1089 //We need to remove this field 1090 if (offset != 0) { 1091 val.set(aField[0], val.get(aField[0]) - offset); 1092 } 1093 } 1094 throw new IllegalArgumentException("The field " + field + " is not supported"); 1095 1096 } 1097 1098 //----------------------------------------------------------------------- 1099 /** 1100 * <p>Constructs an <code>Iterator</code> over each day in a date 1101 * range defined by a focus date and range style.</p> 1102 * 1103 * <p>For instance, passing Thursday, July 4, 2002 and a 1104 * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code> 1105 * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, 1106 * 2002, returning a Calendar instance for each intermediate day.</p> 1107 * 1108 * <p>This method provides an iterator that returns Calendar objects. 1109 * The days are progressed using {@link Calendar#add(int, int)}.</p> 1110 * 1111 * @param focus the date to work with, not null 1112 * @param rangeStyle the style constant to use. Must be one of 1113 * {@link DateUtils#RANGE_MONTH_SUNDAY}, 1114 * {@link DateUtils#RANGE_MONTH_MONDAY}, 1115 * {@link DateUtils#RANGE_WEEK_SUNDAY}, 1116 * {@link DateUtils#RANGE_WEEK_MONDAY}, 1117 * {@link DateUtils#RANGE_WEEK_RELATIVE}, 1118 * {@link DateUtils#RANGE_WEEK_CENTER} 1119 * @return the date iterator, not null, not null 1120 * @throws IllegalArgumentException if the date is <code>null</code> 1121 * @throws IllegalArgumentException if the rangeStyle is invalid 1122 */ 1123 public static Iterator<Calendar> iterator(final Date focus, final int rangeStyle) { 1124 validateDateNotNull(focus); 1125 final Calendar gval = Calendar.getInstance(); 1126 gval.setTime(focus); 1127 return iterator(gval, rangeStyle); 1128 } 1129 1130 /** 1131 * <p>Constructs an <code>Iterator</code> over each day in a date 1132 * range defined by a focus date and range style.</p> 1133 * 1134 * <p>For instance, passing Thursday, July 4, 2002 and a 1135 * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code> 1136 * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, 1137 * 2002, returning a Calendar instance for each intermediate day.</p> 1138 * 1139 * <p>This method provides an iterator that returns Calendar objects. 1140 * The days are progressed using {@link Calendar#add(int, int)}.</p> 1141 * 1142 * @param focus the date to work with, not null 1143 * @param rangeStyle the style constant to use. Must be one of 1144 * {@link DateUtils#RANGE_MONTH_SUNDAY}, 1145 * {@link DateUtils#RANGE_MONTH_MONDAY}, 1146 * {@link DateUtils#RANGE_WEEK_SUNDAY}, 1147 * {@link DateUtils#RANGE_WEEK_MONDAY}, 1148 * {@link DateUtils#RANGE_WEEK_RELATIVE}, 1149 * {@link DateUtils#RANGE_WEEK_CENTER} 1150 * @return the date iterator, not null 1151 * @throws IllegalArgumentException if the date is <code>null</code> 1152 * @throws IllegalArgumentException if the rangeStyle is invalid 1153 */ 1154 public static Iterator<Calendar> iterator(final Calendar focus, final int rangeStyle) { 1155 if (focus == null) { 1156 throw new IllegalArgumentException("The date must not be null"); 1157 } 1158 Calendar start = null; 1159 Calendar end = null; 1160 int startCutoff = Calendar.SUNDAY; 1161 int endCutoff = Calendar.SATURDAY; 1162 switch (rangeStyle) { 1163 case RANGE_MONTH_SUNDAY: 1164 case RANGE_MONTH_MONDAY: 1165 //Set start to the first of the month 1166 start = truncate(focus, Calendar.MONTH); 1167 //Set end to the last of the month 1168 end = (Calendar) start.clone(); 1169 end.add(Calendar.MONTH, 1); 1170 end.add(Calendar.DATE, -1); 1171 //Loop start back to the previous sunday or monday 1172 if (rangeStyle == RANGE_MONTH_MONDAY) { 1173 startCutoff = Calendar.MONDAY; 1174 endCutoff = Calendar.SUNDAY; 1175 } 1176 break; 1177 case RANGE_WEEK_SUNDAY: 1178 case RANGE_WEEK_MONDAY: 1179 case RANGE_WEEK_RELATIVE: 1180 case RANGE_WEEK_CENTER: 1181 //Set start and end to the current date 1182 start = truncate(focus, Calendar.DATE); 1183 end = truncate(focus, Calendar.DATE); 1184 switch (rangeStyle) { 1185 case RANGE_WEEK_SUNDAY: 1186 //already set by default 1187 break; 1188 case RANGE_WEEK_MONDAY: 1189 startCutoff = Calendar.MONDAY; 1190 endCutoff = Calendar.SUNDAY; 1191 break; 1192 case RANGE_WEEK_RELATIVE: 1193 startCutoff = focus.get(Calendar.DAY_OF_WEEK); 1194 endCutoff = startCutoff - 1; 1195 break; 1196 case RANGE_WEEK_CENTER: 1197 startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3; 1198 endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3; 1199 break; 1200 default: 1201 break; 1202 } 1203 break; 1204 default: 1205 throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid."); 1206 } 1207 if (startCutoff < Calendar.SUNDAY) { 1208 startCutoff += 7; 1209 } 1210 if (startCutoff > Calendar.SATURDAY) { 1211 startCutoff -= 7; 1212 } 1213 if (endCutoff < Calendar.SUNDAY) { 1214 endCutoff += 7; 1215 } 1216 if (endCutoff > Calendar.SATURDAY) { 1217 endCutoff -= 7; 1218 } 1219 while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) { 1220 start.add(Calendar.DATE, -1); 1221 } 1222 while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) { 1223 end.add(Calendar.DATE, 1); 1224 } 1225 return new DateIterator(start, end); 1226 } 1227 1228 /** 1229 * <p>Constructs an <code>Iterator</code> over each day in a date 1230 * range defined by a focus date and range style.</p> 1231 * 1232 * <p>For instance, passing Thursday, July 4, 2002 and a 1233 * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code> 1234 * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3, 1235 * 2002, returning a Calendar instance for each intermediate day.</p> 1236 * 1237 * @param focus the date to work with, either {@code Date} or {@code Calendar}, not null 1238 * @param rangeStyle the style constant to use. Must be one of the range 1239 * styles listed for the {@link #iterator(Calendar, int)} method. 1240 * @return the date iterator, not null 1241 * @throws IllegalArgumentException if the date is <code>null</code> 1242 * @throws ClassCastException if the object type is not a {@code Date} or {@code Calendar} 1243 */ 1244 public static Iterator<?> iterator(final Object focus, final int rangeStyle) { 1245 if (focus == null) { 1246 throw new IllegalArgumentException("The date must not be null"); 1247 } 1248 if (focus instanceof Date) { 1249 return iterator((Date) focus, rangeStyle); 1250 } else if (focus instanceof Calendar) { 1251 return iterator((Calendar) focus, rangeStyle); 1252 } else { 1253 throw new ClassCastException("Could not iterate based on " + focus); 1254 } 1255 } 1256 1257 /** 1258 * <p>Returns the number of milliseconds within the 1259 * fragment. All datefields greater than the fragment will be ignored.</p> 1260 * 1261 * <p>Asking the milliseconds of any date will only return the number of milliseconds 1262 * of the current second (resulting in a number between 0 and 999). This 1263 * method will retrieve the number of milliseconds for any fragment. 1264 * For example, if you want to calculate the number of milliseconds past today, 1265 * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will 1266 * be all milliseconds of the past hour(s), minutes(s) and second(s).</p> 1267 * 1268 * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 1269 * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 1270 * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND 1271 * A fragment less than or equal to a SECOND field will return 0.</p> 1272 * 1273 * <ul> 1274 * <li>January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538</li> 1275 * <li>January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538</li> 1276 * <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 (10*1000 + 538)</li> 1277 * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 1278 * (a millisecond cannot be split in milliseconds)</li> 1279 * </ul> 1280 * 1281 * @param date the date to work with, not null 1282 * @param fragment the {@code Calendar} field part of date to calculate 1283 * @return number of milliseconds within the fragment of date 1284 * @throws IllegalArgumentException if the date is <code>null</code> or 1285 * fragment is not supported 1286 * @since 2.4 1287 */ 1288 public static long getFragmentInMilliseconds(final Date date, final int fragment) { 1289 return getFragment(date, fragment, TimeUnit.MILLISECONDS); 1290 } 1291 1292 /** 1293 * <p>Returns the number of seconds within the 1294 * fragment. All datefields greater than the fragment will be ignored.</p> 1295 * 1296 * <p>Asking the seconds of any date will only return the number of seconds 1297 * of the current minute (resulting in a number between 0 and 59). This 1298 * method will retrieve the number of seconds for any fragment. 1299 * For example, if you want to calculate the number of seconds past today, 1300 * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will 1301 * be all seconds of the past hour(s) and minutes(s).</p> 1302 * 1303 * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 1304 * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 1305 * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND 1306 * A fragment less than or equal to a SECOND field will return 0.</p> 1307 * 1308 * <ul> 1309 * <li>January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 1310 * (equivalent to deprecated date.getSeconds())</li> 1311 * <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 1312 * (equivalent to deprecated date.getSeconds())</li> 1313 * <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 1314 * (7*3600 + 15*60 + 10)</li> 1315 * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 1316 * (a millisecond cannot be split in seconds)</li> 1317 * </ul> 1318 * 1319 * @param date the date to work with, not null 1320 * @param fragment the {@code Calendar} field part of date to calculate 1321 * @return number of seconds within the fragment of date 1322 * @throws IllegalArgumentException if the date is <code>null</code> or 1323 * fragment is not supported 1324 * @since 2.4 1325 */ 1326 public static long getFragmentInSeconds(final Date date, final int fragment) { 1327 return getFragment(date, fragment, TimeUnit.SECONDS); 1328 } 1329 1330 /** 1331 * <p>Returns the number of minutes within the 1332 * fragment. All datefields greater than the fragment will be ignored.</p> 1333 * 1334 * <p>Asking the minutes of any date will only return the number of minutes 1335 * of the current hour (resulting in a number between 0 and 59). This 1336 * method will retrieve the number of minutes for any fragment. 1337 * For example, if you want to calculate the number of minutes past this month, 1338 * your fragment is Calendar.MONTH. The result will be all minutes of the 1339 * past day(s) and hour(s).</p> 1340 * 1341 * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 1342 * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 1343 * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND 1344 * A fragment less than or equal to a MINUTE field will return 0.</p> 1345 * 1346 * <ul> 1347 * <li>January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 1348 * (equivalent to deprecated date.getMinutes())</li> 1349 * <li>January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 1350 * (equivalent to deprecated date.getMinutes())</li> 1351 * <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15</li> 1352 * <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)</li> 1353 * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 1354 * (a millisecond cannot be split in minutes)</li> 1355 * </ul> 1356 * 1357 * @param date the date to work with, not null 1358 * @param fragment the {@code Calendar} field part of date to calculate 1359 * @return number of minutes within the fragment of date 1360 * @throws IllegalArgumentException if the date is <code>null</code> or 1361 * fragment is not supported 1362 * @since 2.4 1363 */ 1364 public static long getFragmentInMinutes(final Date date, final int fragment) { 1365 return getFragment(date, fragment, TimeUnit.MINUTES); 1366 } 1367 1368 /** 1369 * <p>Returns the number of hours within the 1370 * fragment. All datefields greater than the fragment will be ignored.</p> 1371 * 1372 * <p>Asking the hours of any date will only return the number of hours 1373 * of the current day (resulting in a number between 0 and 23). This 1374 * method will retrieve the number of hours for any fragment. 1375 * For example, if you want to calculate the number of hours past this month, 1376 * your fragment is Calendar.MONTH. The result will be all hours of the 1377 * past day(s).</p> 1378 * 1379 * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 1380 * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 1381 * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND 1382 * A fragment less than or equal to a HOUR field will return 0.</p> 1383 * 1384 * <ul> 1385 * <li>January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 1386 * (equivalent to deprecated date.getHours())</li> 1387 * <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 1388 * (equivalent to deprecated date.getHours())</li> 1389 * <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7</li> 1390 * <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)</li> 1391 * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 1392 * (a millisecond cannot be split in hours)</li> 1393 * </ul> 1394 * 1395 * @param date the date to work with, not null 1396 * @param fragment the {@code Calendar} field part of date to calculate 1397 * @return number of hours within the fragment of date 1398 * @throws IllegalArgumentException if the date is <code>null</code> or 1399 * fragment is not supported 1400 * @since 2.4 1401 */ 1402 public static long getFragmentInHours(final Date date, final int fragment) { 1403 return getFragment(date, fragment, TimeUnit.HOURS); 1404 } 1405 1406 /** 1407 * <p>Returns the number of days within the 1408 * fragment. All datefields greater than the fragment will be ignored.</p> 1409 * 1410 * <p>Asking the days of any date will only return the number of days 1411 * of the current month (resulting in a number between 1 and 31). This 1412 * method will retrieve the number of days for any fragment. 1413 * For example, if you want to calculate the number of days past this year, 1414 * your fragment is Calendar.YEAR. The result will be all days of the 1415 * past month(s).</p> 1416 * 1417 * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 1418 * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 1419 * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND 1420 * A fragment less than or equal to a DAY field will return 0.</p> 1421 * 1422 * <ul> 1423 * <li>January 28, 2008 with Calendar.MONTH as fragment will return 28 1424 * (equivalent to deprecated date.getDay())</li> 1425 * <li>February 28, 2008 with Calendar.MONTH as fragment will return 28 1426 * (equivalent to deprecated date.getDay())</li> 1427 * <li>January 28, 2008 with Calendar.YEAR as fragment will return 28</li> 1428 * <li>February 28, 2008 with Calendar.YEAR as fragment will return 59</li> 1429 * <li>January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 1430 * (a millisecond cannot be split in days)</li> 1431 * </ul> 1432 * 1433 * @param date the date to work with, not null 1434 * @param fragment the {@code Calendar} field part of date to calculate 1435 * @return number of days within the fragment of date 1436 * @throws IllegalArgumentException if the date is <code>null</code> or 1437 * fragment is not supported 1438 * @since 2.4 1439 */ 1440 public static long getFragmentInDays(final Date date, final int fragment) { 1441 return getFragment(date, fragment, TimeUnit.DAYS); 1442 } 1443 1444 /** 1445 * <p>Returns the number of milliseconds within the 1446 * fragment. All datefields greater than the fragment will be ignored.</p> 1447 * 1448 * <p>Asking the milliseconds of any date will only return the number of milliseconds 1449 * of the current second (resulting in a number between 0 and 999). This 1450 * method will retrieve the number of milliseconds for any fragment. 1451 * For example, if you want to calculate the number of seconds past today, 1452 * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will 1453 * be all seconds of the past hour(s), minutes(s) and second(s).</p> 1454 * 1455 * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 1456 * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 1457 * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND 1458 * A fragment less than or equal to a MILLISECOND field will return 0.</p> 1459 * 1460 * <ul> 1461 * <li>January 1, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 1462 * (equivalent to calendar.get(Calendar.MILLISECOND))</li> 1463 * <li>January 6, 2008 7:15:10.538 with Calendar.SECOND as fragment will return 538 1464 * (equivalent to calendar.get(Calendar.MILLISECOND))</li> 1465 * <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10538 1466 * (10*1000 + 538)</li> 1467 * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 1468 * (a millisecond cannot be split in milliseconds)</li> 1469 * </ul> 1470 * 1471 * @param calendar the calendar to work with, not null 1472 * @param fragment the {@code Calendar} field part of calendar to calculate 1473 * @return number of milliseconds within the fragment of date 1474 * @throws IllegalArgumentException if the date is <code>null</code> or 1475 * fragment is not supported 1476 * @since 2.4 1477 */ 1478 public static long getFragmentInMilliseconds(final Calendar calendar, final int fragment) { 1479 return getFragment(calendar, fragment, TimeUnit.MILLISECONDS); 1480 } 1481 /** 1482 * <p>Returns the number of seconds within the 1483 * fragment. All datefields greater than the fragment will be ignored.</p> 1484 * 1485 * <p>Asking the seconds of any date will only return the number of seconds 1486 * of the current minute (resulting in a number between 0 and 59). This 1487 * method will retrieve the number of seconds for any fragment. 1488 * For example, if you want to calculate the number of seconds past today, 1489 * your fragment is Calendar.DATE or Calendar.DAY_OF_YEAR. The result will 1490 * be all seconds of the past hour(s) and minutes(s).</p> 1491 * 1492 * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 1493 * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 1494 * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND 1495 * A fragment less than or equal to a SECOND field will return 0.</p> 1496 * 1497 * <ul> 1498 * <li>January 1, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 1499 * (equivalent to calendar.get(Calendar.SECOND))</li> 1500 * <li>January 6, 2008 7:15:10.538 with Calendar.MINUTE as fragment will return 10 1501 * (equivalent to calendar.get(Calendar.SECOND))</li> 1502 * <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 26110 1503 * (7*3600 + 15*60 + 10)</li> 1504 * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 1505 * (a millisecond cannot be split in seconds)</li> 1506 * </ul> 1507 * 1508 * @param calendar the calendar to work with, not null 1509 * @param fragment the {@code Calendar} field part of calendar to calculate 1510 * @return number of seconds within the fragment of date 1511 * @throws IllegalArgumentException if the date is <code>null</code> or 1512 * fragment is not supported 1513 * @since 2.4 1514 */ 1515 public static long getFragmentInSeconds(final Calendar calendar, final int fragment) { 1516 return getFragment(calendar, fragment, TimeUnit.SECONDS); 1517 } 1518 1519 /** 1520 * <p>Returns the number of minutes within the 1521 * fragment. All datefields greater than the fragment will be ignored.</p> 1522 * 1523 * <p>Asking the minutes of any date will only return the number of minutes 1524 * of the current hour (resulting in a number between 0 and 59). This 1525 * method will retrieve the number of minutes for any fragment. 1526 * For example, if you want to calculate the number of minutes past this month, 1527 * your fragment is Calendar.MONTH. The result will be all minutes of the 1528 * past day(s) and hour(s).</p> 1529 * 1530 * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 1531 * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 1532 * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND 1533 * A fragment less than or equal to a MINUTE field will return 0.</p> 1534 * 1535 * <ul> 1536 * <li>January 1, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 1537 * (equivalent to calendar.get(Calendar.MINUTES))</li> 1538 * <li>January 6, 2008 7:15:10.538 with Calendar.HOUR_OF_DAY as fragment will return 15 1539 * (equivalent to calendar.get(Calendar.MINUTES))</li> 1540 * <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 15</li> 1541 * <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 435 (7*60 + 15)</li> 1542 * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 1543 * (a millisecond cannot be split in minutes)</li> 1544 * </ul> 1545 * 1546 * @param calendar the calendar to work with, not null 1547 * @param fragment the {@code Calendar} field part of calendar to calculate 1548 * @return number of minutes within the fragment of date 1549 * @throws IllegalArgumentException if the date is <code>null</code> or 1550 * fragment is not supported 1551 * @since 2.4 1552 */ 1553 public static long getFragmentInMinutes(final Calendar calendar, final int fragment) { 1554 return getFragment(calendar, fragment, TimeUnit.MINUTES); 1555 } 1556 1557 /** 1558 * <p>Returns the number of hours within the 1559 * fragment. All datefields greater than the fragment will be ignored.</p> 1560 * 1561 * <p>Asking the hours of any date will only return the number of hours 1562 * of the current day (resulting in a number between 0 and 23). This 1563 * method will retrieve the number of hours for any fragment. 1564 * For example, if you want to calculate the number of hours past this month, 1565 * your fragment is Calendar.MONTH. The result will be all hours of the 1566 * past day(s).</p> 1567 * 1568 * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 1569 * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 1570 * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND 1571 * A fragment less than or equal to a HOUR field will return 0.</p> 1572 * 1573 * <ul> 1574 * <li>January 1, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 1575 * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))</li> 1576 * <li>January 6, 2008 7:15:10.538 with Calendar.DAY_OF_YEAR as fragment will return 7 1577 * (equivalent to calendar.get(Calendar.HOUR_OF_DAY))</li> 1578 * <li>January 1, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 7</li> 1579 * <li>January 6, 2008 7:15:10.538 with Calendar.MONTH as fragment will return 127 (5*24 + 7)</li> 1580 * <li>January 16, 2008 7:15:10.538 with Calendar.MILLISECOND as fragment will return 0 1581 * (a millisecond cannot be split in hours)</li> 1582 * </ul> 1583 * 1584 * @param calendar the calendar to work with, not null 1585 * @param fragment the {@code Calendar} field part of calendar to calculate 1586 * @return number of hours within the fragment of date 1587 * @throws IllegalArgumentException if the date is <code>null</code> or 1588 * fragment is not supported 1589 * @since 2.4 1590 */ 1591 public static long getFragmentInHours(final Calendar calendar, final int fragment) { 1592 return getFragment(calendar, fragment, TimeUnit.HOURS); 1593 } 1594 1595 /** 1596 * <p>Returns the number of days within the 1597 * fragment. All datefields greater than the fragment will be ignored.</p> 1598 * 1599 * <p>Asking the days of any date will only return the number of days 1600 * of the current month (resulting in a number between 1 and 31). This 1601 * method will retrieve the number of days for any fragment. 1602 * For example, if you want to calculate the number of days past this year, 1603 * your fragment is Calendar.YEAR. The result will be all days of the 1604 * past month(s).</p> 1605 * 1606 * <p>Valid fragments are: Calendar.YEAR, Calendar.MONTH, both 1607 * Calendar.DAY_OF_YEAR and Calendar.DATE, Calendar.HOUR_OF_DAY, 1608 * Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND 1609 * A fragment less than or equal to a DAY field will return 0.</p> 1610 * 1611 * <ul> 1612 * <li>January 28, 2008 with Calendar.MONTH as fragment will return 28 1613 * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))</li> 1614 * <li>February 28, 2008 with Calendar.MONTH as fragment will return 28 1615 * (equivalent to calendar.get(Calendar.DAY_OF_MONTH))</li> 1616 * <li>January 28, 2008 with Calendar.YEAR as fragment will return 28 1617 * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))</li> 1618 * <li>February 28, 2008 with Calendar.YEAR as fragment will return 59 1619 * (equivalent to calendar.get(Calendar.DAY_OF_YEAR))</li> 1620 * <li>January 28, 2008 with Calendar.MILLISECOND as fragment will return 0 1621 * (a millisecond cannot be split in days)</li> 1622 * </ul> 1623 * 1624 * @param calendar the calendar to work with, not null 1625 * @param fragment the {@code Calendar} field part of calendar to calculate 1626 * @return number of days within the fragment of date 1627 * @throws IllegalArgumentException if the date is <code>null</code> or 1628 * fragment is not supported 1629 * @since 2.4 1630 */ 1631 public static long getFragmentInDays(final Calendar calendar, final int fragment) { 1632 return getFragment(calendar, fragment, TimeUnit.DAYS); 1633 } 1634 1635 /** 1636 * Gets a Date fragment for any unit. 1637 * 1638 * @param date the date to work with, not null 1639 * @param fragment the Calendar field part of date to calculate 1640 * @param unit the time unit 1641 * @return number of units within the fragment of the date 1642 * @throws IllegalArgumentException if the date is <code>null</code> or 1643 * fragment is not supported 1644 * @since 2.4 1645 */ 1646 private static long getFragment(final Date date, final int fragment, final TimeUnit unit) { 1647 validateDateNotNull(date); 1648 final Calendar calendar = Calendar.getInstance(); 1649 calendar.setTime(date); 1650 return getFragment(calendar, fragment, unit); 1651 } 1652 1653 /** 1654 * Gets a Calendar fragment for any unit. 1655 * 1656 * @param calendar the calendar to work with, not null 1657 * @param fragment the Calendar field part of calendar to calculate 1658 * @param unit the time unit 1659 * @return number of units within the fragment of the calendar 1660 * @throws IllegalArgumentException if the date is <code>null</code> or 1661 * fragment is not supported 1662 * @since 2.4 1663 */ 1664 private static long getFragment(final Calendar calendar, final int fragment, final TimeUnit unit) { 1665 if(calendar == null) { 1666 throw new IllegalArgumentException("The date must not be null"); 1667 } 1668 1669 long result = 0; 1670 1671 final int offset = (unit == TimeUnit.DAYS) ? 0 : 1; 1672 1673 // Fragments bigger than a day require a breakdown to days 1674 switch (fragment) { 1675 case Calendar.YEAR: 1676 result += unit.convert(calendar.get(Calendar.DAY_OF_YEAR) - offset, TimeUnit.DAYS); 1677 break; 1678 case Calendar.MONTH: 1679 result += unit.convert(calendar.get(Calendar.DAY_OF_MONTH) - offset, TimeUnit.DAYS); 1680 break; 1681 default: 1682 break; 1683 } 1684 1685 switch (fragment) { 1686 // Number of days already calculated for these cases 1687 case Calendar.YEAR: 1688 case Calendar.MONTH: 1689 1690 // The rest of the valid cases 1691 case Calendar.DAY_OF_YEAR: 1692 case Calendar.DATE: 1693 result += unit.convert(calendar.get(Calendar.HOUR_OF_DAY), TimeUnit.HOURS); 1694 //$FALL-THROUGH$ 1695 case Calendar.HOUR_OF_DAY: 1696 result += unit.convert(calendar.get(Calendar.MINUTE), TimeUnit.MINUTES); 1697 //$FALL-THROUGH$ 1698 case Calendar.MINUTE: 1699 result += unit.convert(calendar.get(Calendar.SECOND), TimeUnit.SECONDS); 1700 //$FALL-THROUGH$ 1701 case Calendar.SECOND: 1702 result += unit.convert(calendar.get(Calendar.MILLISECOND), TimeUnit.MILLISECONDS); 1703 break; 1704 case Calendar.MILLISECOND: break;//never useful 1705 default: throw new IllegalArgumentException("The fragment " + fragment + " is not supported"); 1706 } 1707 return result; 1708 } 1709 1710 /** 1711 * Determines if two calendars are equal up to no more than the specified 1712 * most significant field. 1713 * 1714 * @param cal1 the first calendar, not <code>null</code> 1715 * @param cal2 the second calendar, not <code>null</code> 1716 * @param field the field from {@code Calendar} 1717 * @return <code>true</code> if equal; otherwise <code>false</code> 1718 * @throws IllegalArgumentException if any argument is <code>null</code> 1719 * @see #truncate(Calendar, int) 1720 * @see #truncatedEquals(Date, Date, int) 1721 * @since 3.0 1722 */ 1723 public static boolean truncatedEquals(final Calendar cal1, final Calendar cal2, final int field) { 1724 return truncatedCompareTo(cal1, cal2, field) == 0; 1725 } 1726 1727 /** 1728 * Determines if two dates are equal up to no more than the specified 1729 * most significant field. 1730 * 1731 * @param date1 the first date, not <code>null</code> 1732 * @param date2 the second date, not <code>null</code> 1733 * @param field the field from {@code Calendar} 1734 * @return <code>true</code> if equal; otherwise <code>false</code> 1735 * @throws IllegalArgumentException if any argument is <code>null</code> 1736 * @see #truncate(Date, int) 1737 * @see #truncatedEquals(Calendar, Calendar, int) 1738 * @since 3.0 1739 */ 1740 public static boolean truncatedEquals(final Date date1, final Date date2, final int field) { 1741 return truncatedCompareTo(date1, date2, field) == 0; 1742 } 1743 1744 /** 1745 * Determines how two calendars compare up to no more than the specified 1746 * most significant field. 1747 * 1748 * @param cal1 the first calendar, not <code>null</code> 1749 * @param cal2 the second calendar, not <code>null</code> 1750 * @param field the field from {@code Calendar} 1751 * @return a negative integer, zero, or a positive integer as the first 1752 * calendar is less than, equal to, or greater than the second. 1753 * @throws IllegalArgumentException if any argument is <code>null</code> 1754 * @see #truncate(Calendar, int) 1755 * @see #truncatedCompareTo(Date, Date, int) 1756 * @since 3.0 1757 */ 1758 public static int truncatedCompareTo(final Calendar cal1, final Calendar cal2, final int field) { 1759 final Calendar truncatedCal1 = truncate(cal1, field); 1760 final Calendar truncatedCal2 = truncate(cal2, field); 1761 return truncatedCal1.compareTo(truncatedCal2); 1762 } 1763 1764 /** 1765 * Determines how two dates compare up to no more than the specified 1766 * most significant field. 1767 * 1768 * @param date1 the first date, not <code>null</code> 1769 * @param date2 the second date, not <code>null</code> 1770 * @param field the field from <code>Calendar</code> 1771 * @return a negative integer, zero, or a positive integer as the first 1772 * date is less than, equal to, or greater than the second. 1773 * @throws IllegalArgumentException if any argument is <code>null</code> 1774 * @see #truncate(Calendar, int) 1775 * @see #truncatedCompareTo(Date, Date, int) 1776 * @since 3.0 1777 */ 1778 public static int truncatedCompareTo(final Date date1, final Date date2, final int field) { 1779 final Date truncatedDate1 = truncate(date1, field); 1780 final Date truncatedDate2 = truncate(date2, field); 1781 return truncatedDate1.compareTo(truncatedDate2); 1782 } 1783 1784 private static void validateDateNotNull(final Date date) { 1785 Validate.isTrue(date != null, "The date must not be null"); 1786 } 1787 1788 //----------------------------------------------------------------------- 1789 /** 1790 * <p>Date iterator.</p> 1791 */ 1792 static class DateIterator implements Iterator<Calendar> { 1793 private final Calendar endFinal; 1794 private final Calendar spot; 1795 1796 /** 1797 * Constructs a DateIterator that ranges from one date to another. 1798 * 1799 * @param startFinal start date (inclusive) 1800 * @param endFinal end date (inclusive) 1801 */ 1802 DateIterator(final Calendar startFinal, final Calendar endFinal) { 1803 super(); 1804 this.endFinal = endFinal; 1805 spot = startFinal; 1806 spot.add(Calendar.DATE, -1); 1807 } 1808 1809 /** 1810 * Has the iterator not reached the end date yet? 1811 * 1812 * @return <code>true</code> if the iterator has yet to reach the end date 1813 */ 1814 @Override 1815 public boolean hasNext() { 1816 return spot.before(endFinal); 1817 } 1818 1819 /** 1820 * Return the next calendar in the iteration 1821 * 1822 * @return Object calendar for the next date 1823 */ 1824 @Override 1825 public Calendar next() { 1826 if (spot.equals(endFinal)) { 1827 throw new NoSuchElementException(); 1828 } 1829 spot.add(Calendar.DATE, 1); 1830 return (Calendar) spot.clone(); 1831 } 1832 1833 /** 1834 * Always throws UnsupportedOperationException. 1835 * 1836 * @throws UnsupportedOperationException 1837 * @see java.util.Iterator#remove() 1838 */ 1839 @Override 1840 public void remove() { 1841 throw new UnsupportedOperationException(); 1842 } 1843 } 1844 1845}