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