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