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