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