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