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

Save This Page
Home » commons-lang-2.4-src » org.apache.commons » lang » time » [javadoc | source]