001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.lang3.time; 018 019import java.text.ParseException; 020import java.text.ParsePosition; 021import java.util.Calendar; 022import java.util.Date; 023import java.util.Iterator; 024import java.util.Locale; 025import java.util.NoSuchElementException; 026import java.util.TimeZone; 027import java.util.concurrent.TimeUnit; 028 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 */ 052public class DateUtils { 053 054 /** 055 * Number of milliseconds in a standard second. 056 * @since 2.1 057 */ 058 public static final long MILLIS_PER_SECOND = 1000; 059 /** 060 * Number of milliseconds in a standard minute. 061 * @since 2.1 062 */ 063 public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; 064 /** 065 * Number of milliseconds in a standard hour. 066 * @since 2.1 067 */ 068 public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; 069 /** 070 * Number of milliseconds in a standard day. 071 * @since 2.1 072 */ 073 public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; 074 075 /** 076 * This is half a month, so this represents whether a date is in the top 077 * or bottom half of the month. 078 */ 079 public static final int SEMI_MONTH = 1001; 080 081 private static final int[][] fields = { 082 {Calendar.MILLISECOND}, 083 {Calendar.SECOND}, 084 {Calendar.MINUTE}, 085 {Calendar.HOUR_OF_DAY, Calendar.HOUR}, 086 {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM 087 /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */ 088 }, 089 {Calendar.MONTH, DateUtils.SEMI_MONTH}, 090 {Calendar.YEAR}, 091 {Calendar.ERA}}; 092 093 /** 094 * A week range, starting on Sunday. 095 */ 096 public static final int RANGE_WEEK_SUNDAY = 1; 097 /** 098 * A week range, starting on Monday. 099 */ 100 public static final int RANGE_WEEK_MONDAY = 2; 101 /** 102 * A week range, starting on the day focused. 103 */ 104 public static final int RANGE_WEEK_RELATIVE = 3; 105 /** 106 * A week range, centered around the day focused. 107 */ 108 public static final int RANGE_WEEK_CENTER = 4; 109 /** 110 * A month range, the week starting on Sunday. 111 */ 112 public static final int RANGE_MONTH_SUNDAY = 5; 113 /** 114 * A month range, the week starting on Monday. 115 */ 116 public static final int RANGE_MONTH_MONDAY = 6; 117 118 /** 119 * Calendar modification types. 120 */ 121 private enum ModifyType { 122 /** 123 * Truncation. 124 */ 125 TRUNCATE, 126 127 /** 128 * Rounding. 129 */ 130 ROUND, 131 132 /** 133 * Ceiling. 134 */ 135 CEILING 136 } 137 138 /** 139 * <p>{@code DateUtils} instances should NOT be constructed in 140 * standard programming. Instead, the static methods on the class should 141 * be used, such as {@code DateUtils.parseDate(str);}.</p> 142 * 143 * <p>This constructor is public to permit tools that require a JavaBean 144 * instance to operate.</p> 145 */ 146 public DateUtils() { 147 super(); 148 } 149 150 //----------------------------------------------------------------------- 151 /** 152 * <p>Checks if two date objects are on the same day ignoring time.</p> 153 * 154 * <p>28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. 155 * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. 156 * </p> 157 * 158 * @param date1 the first date, not altered, not null 159 * @param date2 the second date, not altered, not null 160 * @return true if they represent the same day 161 * @throws IllegalArgumentException if either date is <code>null</code> 162 * @since 2.1 163 */ 164 public static boolean isSameDay(final Date date1, final Date date2) { 165 if (date1 == null || date2 == null) { 166 throw new IllegalArgumentException("The date must not be null"); 167 } 168 final Calendar cal1 = Calendar.getInstance(); 169 cal1.setTime(date1); 170 final Calendar cal2 = Calendar.getInstance(); 171 cal2.setTime(date2); 172 return isSameDay(cal1, cal2); 173 } 174 175 /** 176 * <p>Checks if two calendar objects are on the same day ignoring time.</p> 177 * 178 * <p>28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true. 179 * 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false. 180 * </p> 181 * 182 * @param cal1 the first calendar, not altered, not null 183 * @param cal2 the second calendar, not altered, not null 184 * @return true if they represent the same day 185 * @throws IllegalArgumentException if either calendar is <code>null</code> 186 * @since 2.1 187 */ 188 public static boolean isSameDay(final Calendar cal1, final Calendar cal2) { 189 if (cal1 == null || cal2 == null) { 190 throw new IllegalArgumentException("The date must not be null"); 191 } 192 return cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && 193 cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && 194 cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR); 195 } 196 197 //----------------------------------------------------------------------- 198 /** 199 * <p>Checks if two date objects represent the same instant in time.</p> 200 * 201 * <p>This method compares the long millisecond time of the two objects.</p> 202 * 203 * @param date1 the first date, not altered, not null 204 * @param date2 the second date, not altered, not null 205 * @return true if they represent the same millisecond instant 206 * @throws IllegalArgumentException if either date is <code>null</code> 207 * @since 2.1 208 */ 209 public static boolean isSameInstant(final Date date1, final Date date2) { 210 if (date1 == null || date2 == null) { 211 throw new IllegalArgumentException("The date must not be null"); 212 } 213 return date1.getTime() == date2.getTime(); 214 } 215 216 /** 217 * <p>Checks if two calendar objects represent the same instant in time.</p> 218 * 219 * <p>This method compares the long millisecond time of the two objects.</p> 220 * 221 * @param cal1 the first calendar, not altered, not null 222 * @param cal2 the second calendar, not altered, not null 223 * @return true if they represent the same millisecond instant 224 * @throws IllegalArgumentException if either date is <code>null</code> 225 * @since 2.1 226 */ 227 public static boolean isSameInstant(final Calendar cal1, final Calendar cal2) { 228 if (cal1 == null || cal2 == null) { 229 throw new IllegalArgumentException("The date must not be null"); 230 } 231 return cal1.getTime().getTime() == cal2.getTime().getTime(); 232 } 233 234 //----------------------------------------------------------------------- 235 /** 236 * <p>Checks if two calendar objects represent the same local time.</p> 237 * 238 * <p>This method compares the values of the fields of the two objects. 239 * In addition, both calendars must be the same of the same type.</p> 240 * 241 * @param cal1 the first calendar, not altered, not null 242 * @param cal2 the second calendar, not altered, not null 243 * @return true if they represent the same millisecond instant 244 * @throws IllegalArgumentException if either date is <code>null</code> 245 * @since 2.1 246 */ 247 public static boolean isSameLocalTime(final Calendar cal1, final Calendar cal2) { 248 if (cal1 == null || cal2 == null) { 249 throw new IllegalArgumentException("The date must not be null"); 250 } 251 return cal1.get(Calendar.MILLISECOND) == cal2.get(Calendar.MILLISECOND) && 252 cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND) && 253 cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE) && 254 cal1.get(Calendar.HOUR_OF_DAY) == cal2.get(Calendar.HOUR_OF_DAY) && 255 cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) && 256 cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && 257 cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && 258 cal1.getClass() == cal2.getClass(); 259 } 260 261 //----------------------------------------------------------------------- 262 /** 263 * <p>Parses a string representing a date by trying a variety of different parsers.</p> 264 * 265 * <p>The parse will try each parse pattern in turn. 266 * A parse is only deemed successful if it parses the whole of the input string. 267 * If no parse patterns match, a ParseException is thrown.</p> 268 * The parser will be lenient toward the parsed date. 269 * 270 * @param str the date to parse, not null 271 * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null 272 * @return the parsed date 273 * @throws IllegalArgumentException if the date string or pattern array is null 274 * @throws ParseException if none of the date patterns were suitable (or there were none) 275 */ 276 public static Date parseDate(final String str, final String... parsePatterns) throws ParseException { 277 return parseDate(str, null, parsePatterns); 278 } 279 280 //----------------------------------------------------------------------- 281 /** 282 * <p>Parses a string representing a date by trying a variety of different parsers, 283 * using the default date format symbols for the given locale.</p> 284 * 285 * <p>The parse will try each parse pattern in turn. 286 * A parse is only deemed successful if it parses the whole of the input string. 287 * If no parse patterns match, a ParseException is thrown.</p> 288 * The parser will be lenient toward the parsed date. 289 * 290 * @param str the date to parse, not null 291 * @param locale the locale whose date format symbols should be used. If <code>null</code>, 292 * the system locale is used (as per {@link #parseDate(String, String...)}). 293 * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null 294 * @return the parsed date 295 * @throws IllegalArgumentException if the date string or pattern array is null 296 * @throws ParseException if none of the date patterns were suitable (or there were none) 297 * @since 3.2 298 */ 299 public static Date parseDate(final String str, final Locale locale, final String... parsePatterns) throws ParseException { 300 return parseDateWithLeniency(str, locale, parsePatterns, true); 301 } 302 303 //----------------------------------------------------------------------- 304 /** 305 * <p>Parses a string representing a date by trying a variety of different parsers.</p> 306 * 307 * <p>The parse will try each parse pattern in turn. 308 * A parse is only deemed successful if it parses the whole of the input string. 309 * If no parse patterns match, a ParseException is thrown.</p> 310 * The parser parses strictly - it does not allow for dates such as "February 942, 1996". 311 * 312 * @param str the date to parse, not null 313 * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null 314 * @return the parsed date 315 * @throws IllegalArgumentException if the date string or pattern array is null 316 * @throws ParseException if none of the date patterns were suitable 317 * @since 2.5 318 */ 319 public static Date parseDateStrictly(final String str, final String... parsePatterns) throws ParseException { 320 return parseDateStrictly(str, null, parsePatterns); 321 } 322 323 /** 324 * <p>Parses a string representing a date by trying a variety of different parsers, 325 * using the default date format symbols for the given locale..</p> 326 * 327 * <p>The parse will try each parse pattern in turn. 328 * A parse is only deemed successful if it parses the whole of the input string. 329 * If no parse patterns match, a ParseException is thrown.</p> 330 * The parser parses strictly - it does not allow for dates such as "February 942, 1996". 331 * 332 * @param str the date to parse, not null 333 * @param locale the locale whose date format symbols should be used. If <code>null</code>, 334 * the system locale is used (as per {@link #parseDateStrictly(String, String...)}). 335 * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null 336 * @return the parsed date 337 * @throws IllegalArgumentException if the date string or pattern array is null 338 * @throws ParseException if none of the date patterns were suitable 339 * @since 3.2 340 */ 341 public static Date parseDateStrictly(final String str, final Locale locale, final String... parsePatterns) throws ParseException { 342 return parseDateWithLeniency(str, locale, parsePatterns, false); 343 } 344 345 /** 346 * <p>Parses a string representing a date by trying a variety of different parsers.</p> 347 * 348 * <p>The parse will try each parse pattern in turn. 349 * A parse is only deemed successful if it parses the whole of the input string. 350 * If no parse patterns match, a ParseException is thrown.</p> 351 * 352 * @param str the date to parse, not null 353 * @param locale the locale to use when interpretting the pattern, can be null in which 354 * case the default system locale is used 355 * @param parsePatterns the date format patterns to use, see SimpleDateFormat, not null 356 * @param lenient Specify whether or not date/time parsing is to be lenient. 357 * @return the parsed date 358 * @throws IllegalArgumentException if the date string or pattern array is null 359 * @throws ParseException if none of the date patterns were suitable 360 * @see java.util.Calendar#isLenient() 361 */ 362 private static Date parseDateWithLeniency( 363 final String str, final Locale locale, final String[] parsePatterns, final boolean lenient) throws ParseException { 364 if (str == null || parsePatterns == null) { 365 throw new IllegalArgumentException("Date and Patterns must not be null"); 366 } 367 368 final TimeZone tz = TimeZone.getDefault(); 369 final Locale lcl = locale==null ?Locale.getDefault() : locale; 370 final ParsePosition pos = new ParsePosition(0); 371 final Calendar calendar = Calendar.getInstance(tz, lcl); 372 calendar.setLenient(lenient); 373 374 for (final String parsePattern : parsePatterns) { 375 FastDateParser fdp = new FastDateParser(parsePattern, tz, lcl); 376 calendar.clear(); 377 try { 378 if (fdp.parse(str, pos, calendar) && pos.getIndex()==str.length()) { 379 return calendar.getTime(); 380 } 381 } 382 catch(IllegalArgumentException ignore) { 383 // leniency is preventing calendar from being set 384 } 385 pos.setIndex(0); 386 } 387 throw new ParseException("Unable to parse the date: " + str, -1); 388 } 389 390 //----------------------------------------------------------------------- 391 /** 392 * Adds a number of years to a date returning a new object. 393 * The original {@code Date} is unchanged. 394 * 395 * @param date the date, not null 396 * @param amount the amount to add, may be negative 397 * @return the new {@code Date} with the amount added 398 * @throws IllegalArgumentException if the date is null 399 */ 400 public static Date addYears(final Date date, final int amount) { 401 return add(date, Calendar.YEAR, amount); 402 } 403 404 //----------------------------------------------------------------------- 405 /** 406 * Adds a number of months 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 addMonths(final Date date, final int amount) { 415 return add(date, Calendar.MONTH, amount); 416 } 417 418 //----------------------------------------------------------------------- 419 /** 420 * Adds a number of weeks 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 addWeeks(final Date date, final int amount) { 429 return add(date, Calendar.WEEK_OF_YEAR, amount); 430 } 431 432 //----------------------------------------------------------------------- 433 /** 434 * Adds a number of days 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 addDays(final Date date, final int amount) { 443 return add(date, Calendar.DAY_OF_MONTH, amount); 444 } 445 446 //----------------------------------------------------------------------- 447 /** 448 * Adds a number of hours 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 addHours(final Date date, final int amount) { 457 return add(date, Calendar.HOUR_OF_DAY, amount); 458 } 459 460 //----------------------------------------------------------------------- 461 /** 462 * Adds a number of minutes 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 addMinutes(final Date date, final int amount) { 471 return add(date, Calendar.MINUTE, amount); 472 } 473 474 //----------------------------------------------------------------------- 475 /** 476 * Adds a number of seconds 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 addSeconds(final Date date, final int amount) { 485 return add(date, Calendar.SECOND, amount); 486 } 487 488 //----------------------------------------------------------------------- 489 /** 490 * Adds a number of milliseconds 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 addMilliseconds(final Date date, final int amount) { 499 return add(date, Calendar.MILLISECOND, amount); 500 } 501 502 //----------------------------------------------------------------------- 503 /** 504 * Adds to a date returning a new object. 505 * The original {@code Date} is unchanged. 506 * 507 * @param date the date, not null 508 * @param calendarField the calendar field to add to 509 * @param amount the amount to add, may be negative 510 * @return the new {@code Date} with the amount added 511 * @throws IllegalArgumentException if the date is null 512 */ 513 private static Date add(final Date date, final int calendarField, final int amount) { 514 if (date == null) { 515 throw new IllegalArgumentException("The date must not be null"); 516 } 517 final Calendar c = Calendar.getInstance(); 518 c.setTime(date); 519 c.add(calendarField, amount); 520 return c.getTime(); 521 } 522 523 //----------------------------------------------------------------------- 524 /** 525 * Sets the years field to a date returning a new object. 526 * The original {@code Date} is unchanged. 527 * 528 * @param date the date, not null 529 * @param amount the amount to set 530 * @return a new {@code Date} set with the specified value 531 * @throws IllegalArgumentException if the date is null 532 * @since 2.4 533 */ 534 public static Date setYears(final Date date, final int amount) { 535 return set(date, Calendar.YEAR, amount); 536 } 537 538 //----------------------------------------------------------------------- 539 /** 540 * Sets the months field to a date returning a new object. 541 * The original {@code Date} is unchanged. 542 * 543 * @param date the date, not null 544 * @param amount the amount to set 545 * @return a new {@code Date} set with the specified value 546 * @throws IllegalArgumentException if the date is null 547 * @since 2.4 548 */ 549 public static Date setMonths(final Date date, final int amount) { 550 return set(date, Calendar.MONTH, amount); 551 } 552 553 //----------------------------------------------------------------------- 554 /** 555 * Sets the day of month field to a date returning a new object. 556 * The original {@code Date} is unchanged. 557 * 558 * @param date the date, not null 559 * @param amount the amount to set 560 * @return a new {@code Date} set with the specified value 561 * @throws IllegalArgumentException if the date is null 562 * @since 2.4 563 */ 564 public static Date setDays(final Date date, final int amount) { 565 return set(date, Calendar.DAY_OF_MONTH, amount); 566 } 567 568 //----------------------------------------------------------------------- 569 /** 570 * Sets the hours field to a date returning a new object. Hours range 571 * from 0-23. 572 * The original {@code Date} is unchanged. 573 * 574 * @param date the date, not null 575 * @param amount the amount to set 576 * @return a new {@code Date} set with the specified value 577 * @throws IllegalArgumentException if the date is null 578 * @since 2.4 579 */ 580 public static Date setHours(final Date date, final int amount) { 581 return set(date, Calendar.HOUR_OF_DAY, amount); 582 } 583 584 //----------------------------------------------------------------------- 585 /** 586 * Sets the minute field to a date returning a new object. 587 * The original {@code Date} is unchanged. 588 * 589 * @param date the date, not null 590 * @param amount the amount to set 591 * @return a new {@code Date} set with the specified value 592 * @throws IllegalArgumentException if the date is null 593 * @since 2.4 594 */ 595 public static Date setMinutes(final Date date, final int amount) { 596 return set(date, Calendar.MINUTE, amount); 597 } 598 599 //----------------------------------------------------------------------- 600 /** 601 * Sets the seconds field to a date returning a new object. 602 * The original {@code Date} is unchanged. 603 * 604 * @param date the date, not null 605 * @param amount the amount to set 606 * @return a new {@code Date} set with the specified value 607 * @throws IllegalArgumentException if the date is null 608 * @since 2.4 609 */ 610 public static Date setSeconds(final Date date, final int amount) { 611 return set(date, Calendar.SECOND, amount); 612 } 613 614 //----------------------------------------------------------------------- 615 /** 616 * Sets the milliseconds field to a date returning a new object. 617 * The original {@code Date} is unchanged. 618 * 619 * @param date the date, not null 620 * @param amount the amount to set 621 * @return a new {@code Date} set with the specified value 622 * @throws IllegalArgumentException if the date is null 623 * @since 2.4 624 */ 625 public static Date setMilliseconds(final Date date, final int amount) { 626 return set(date, Calendar.MILLISECOND, amount); 627 } 628 629 //----------------------------------------------------------------------- 630 /** 631 * Sets the specified field to a date returning a new object. 632 * This does not use a lenient calendar. 633 * The original {@code Date} is unchanged. 634 * 635 * @param date the date, not null 636 * @param calendarField the {@code Calendar} field to set the amount to 637 * @param amount the amount to set 638 * @return a new {@code Date} set with the specified value 639 * @throws IllegalArgumentException if the date is null 640 * @since 2.4 641 */ 642 private static Date set(final Date date, final int calendarField, final int amount) { 643 if (date == null) { 644 throw new IllegalArgumentException("The date must not be null"); 645 } 646 // getInstance() returns a new object, so this method is thread safe. 647 final Calendar c = Calendar.getInstance(); 648 c.setLenient(false); 649 c.setTime(date); 650 c.set(calendarField, amount); 651 return c.getTime(); 652 } 653 654 //----------------------------------------------------------------------- 655 /** 656 * Converts a {@code Date} into a {@code Calendar}. 657 * 658 * @param date the date to convert to a Calendar 659 * @return the created Calendar 660 * @throws NullPointerException if null is passed in 661 * @since 3.0 662 */ 663 public static Calendar toCalendar(final Date date) { 664 final Calendar c = Calendar.getInstance(); 665 c.setTime(date); 666 return c; 667 } 668 669 //----------------------------------------------------------------------- 670 /** 671 * Converts a {@code Date} of a given {@code TimeZone} into a {@code Calendar} 672 * @param date the date to convert to a Calendar 673 * @param tz the time zone of the @{code date} 674 * @return the created Calendar 675 * @throws NullPointerException if {@code date} or {@code tz} is null 676 */ 677 public static Calendar toCalendar(final Date date, final TimeZone tz) { 678 final Calendar c = Calendar.getInstance(tz); 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}