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