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