This constructs an Iterator over each day in a date range defined by a focus date and range style. : Range « Collections Data Structure « Java






This constructs an Iterator over each day in a date range defined by a focus date and range style.

       
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.TimeZone;


/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */




/**
 * <p>A suite of utilities surrounding the use of the
 * {@link java.util.Calendar} and {@link java.util.Date} object.</p>
 * 
 * <p>DateUtils contains a lot of common methods considering manipulations
 * of Dates or Calendars. Some methods require some extra explanation.
 * The truncate and round methods could be considered the Math.floor(),
 * Math.ceil() or Math.round versions for dates
 * This way date-fields will be ignored in bottom-up order.
 * As a complement to these methods we've introduced some fragment-methods.
 * With these methods the Date-fields will be ignored in top-down order.
 * Since a date without a year is not a valid date, you have to decide in what
 * kind of date-field you want your result, for instance milliseconds or days.
 * </p>
 *   
 *   
 *
 * @author <a href="mailto:sergek@lokitech.com">Serge Knystautas</a>
 * @author Stephen Colebourne
 * @author Janek Bogucki
 * @author <a href="mailto:ggregory@seagullsw.com">Gary Gregory</a>
 * @author Phil Steitz
 * @author Robert Scholte
 * @since 2.0
 * @version $Id: DateUtils.java 634096 2008-03-06 00:58:11Z niallp $
 */
public class Main {
  
  /**
   * The UTC time zone  (often referred to as GMT).
   */
  public static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("GMT");
  /**
   * Number of milliseconds in a standard second.
   * @since 2.1
   */
  public static final long MILLIS_PER_SECOND = 1000;
  /**
   * Number of milliseconds in a standard minute.
   * @since 2.1
   */
  public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
  /**
   * Number of milliseconds in a standard hour.
   * @since 2.1
   */
  public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
  /**
   * Number of milliseconds in a standard day.
   * @since 2.1
   */
  public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;

  /**
   * This is half a month, so this represents whether a date is in the top
   * or bottom half of the month.
   */
  public final static int SEMI_MONTH = 1001;

  private static final int[][] fields = {
          {Calendar.MILLISECOND},
          {Calendar.SECOND},
          {Calendar.MINUTE},
          {Calendar.HOUR_OF_DAY, Calendar.HOUR},
          {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM 
              /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */
          },
          {Calendar.MONTH, DateUtils.SEMI_MONTH},
          {Calendar.YEAR},
          {Calendar.ERA}};

  /**
   * A week range, starting on Sunday.
   */
  public final static int RANGE_WEEK_SUNDAY = 1;

  /**
   * A week range, starting on Monday.
   */
  public final static int RANGE_WEEK_MONDAY = 2;

  /**
   * A week range, starting on the day focused.
   */
  public final static int RANGE_WEEK_RELATIVE = 3;

  /**
   * A week range, centered around the day focused.
   */
  public final static int RANGE_WEEK_CENTER = 4;

  /**
   * A month range, the week starting on Sunday.
   */
  public final static int RANGE_MONTH_SUNDAY = 5;

  /**
   * A month range, the week starting on Monday.
   */
  public final static int RANGE_MONTH_MONDAY = 6;

  //-----------------------------------------------------------------------
  /**
   * <p>This constructs an <code>Iterator</code> over each day in a date
   * range defined by a focus date and range style.</p>
   *
   * <p>For instance, passing Thursday, July 4, 2002 and a
   * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code>
   * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
   * 2002, returning a Calendar instance for each intermediate day.</p>
   *
   * <p>This method provides an iterator that returns Calendar objects.
   * The days are progressed using {@link Calendar#add(int, int)}.</p>
   *
   * @param focus  the date to work with, not null
   * @param rangeStyle  the style constant to use. Must be one of
   * {@link DateUtils#RANGE_MONTH_SUNDAY}, 
   * {@link DateUtils#RANGE_MONTH_MONDAY},
   * {@link DateUtils#RANGE_WEEK_SUNDAY},
   * {@link DateUtils#RANGE_WEEK_MONDAY},
   * {@link DateUtils#RANGE_WEEK_RELATIVE},
   * {@link DateUtils#RANGE_WEEK_CENTER}
   * @return the date iterator, which always returns Calendar instances
   * @throws IllegalArgumentException if the date is <code>null</code>
   * @throws IllegalArgumentException if the rangeStyle is invalid
   */
  public static Iterator iterator(Date focus, int rangeStyle) {
      if (focus == null) {
          throw new IllegalArgumentException("The date must not be null");
      }
      Calendar gval = Calendar.getInstance();
      gval.setTime(focus);
      return iterator(gval, rangeStyle);
  }

  /**
   * <p>This constructs an <code>Iterator</code> over each day in a date
   * range defined by a focus date and range style.</p>
   *
   * <p>For instance, passing Thursday, July 4, 2002 and a
   * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code>
   * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
   * 2002, returning a Calendar instance for each intermediate day.</p>
   *
   * <p>This method provides an iterator that returns Calendar objects.
   * The days are progressed using {@link Calendar#add(int, int)}.</p>
   *
   * @param focus  the date to work with
   * @param rangeStyle  the style constant to use. Must be one of
   * {@link DateUtils#RANGE_MONTH_SUNDAY}, 
   * {@link DateUtils#RANGE_MONTH_MONDAY},
   * {@link DateUtils#RANGE_WEEK_SUNDAY},
   * {@link DateUtils#RANGE_WEEK_MONDAY},
   * {@link DateUtils#RANGE_WEEK_RELATIVE},
   * {@link DateUtils#RANGE_WEEK_CENTER}
   * @return the date iterator
   * @throws IllegalArgumentException if the date is <code>null</code>
   * @throws IllegalArgumentException if the rangeStyle is invalid
   */
  public static Iterator iterator(Calendar focus, int rangeStyle) {
      if (focus == null) {
          throw new IllegalArgumentException("The date must not be null");
      }
      Calendar start = null;
      Calendar end = null;
      int startCutoff = Calendar.SUNDAY;
      int endCutoff = Calendar.SATURDAY;
      switch (rangeStyle) {
          case RANGE_MONTH_SUNDAY:
          case RANGE_MONTH_MONDAY:
              //Set start to the first of the month
              start = truncate(focus, Calendar.MONTH);
              //Set end to the last of the month
              end = (Calendar) start.clone();
              end.add(Calendar.MONTH, 1);
              end.add(Calendar.DATE, -1);
              //Loop start back to the previous sunday or monday
              if (rangeStyle == RANGE_MONTH_MONDAY) {
                  startCutoff = Calendar.MONDAY;
                  endCutoff = Calendar.SUNDAY;
              }
              break;
          case RANGE_WEEK_SUNDAY:
          case RANGE_WEEK_MONDAY:
          case RANGE_WEEK_RELATIVE:
          case RANGE_WEEK_CENTER:
              //Set start and end to the current date
              start = truncate(focus, Calendar.DATE);
              end = truncate(focus, Calendar.DATE);
              switch (rangeStyle) {
                  case RANGE_WEEK_SUNDAY:
                      //already set by default
                      break;
                  case RANGE_WEEK_MONDAY:
                      startCutoff = Calendar.MONDAY;
                      endCutoff = Calendar.SUNDAY;
                      break;
                  case RANGE_WEEK_RELATIVE:
                      startCutoff = focus.get(Calendar.DAY_OF_WEEK);
                      endCutoff = startCutoff - 1;
                      break;
                  case RANGE_WEEK_CENTER:
                      startCutoff = focus.get(Calendar.DAY_OF_WEEK) - 3;
                      endCutoff = focus.get(Calendar.DAY_OF_WEEK) + 3;
                      break;
              }
              break;
          default:
              throw new IllegalArgumentException("The range style " + rangeStyle + " is not valid.");
      }
      if (startCutoff < Calendar.SUNDAY) {
          startCutoff += 7;
      }
      if (startCutoff > Calendar.SATURDAY) {
          startCutoff -= 7;
      }
      if (endCutoff < Calendar.SUNDAY) {
          endCutoff += 7;
      }
      if (endCutoff > Calendar.SATURDAY) {
          endCutoff -= 7;
      }
      while (start.get(Calendar.DAY_OF_WEEK) != startCutoff) {
          start.add(Calendar.DATE, -1);
      }
      while (end.get(Calendar.DAY_OF_WEEK) != endCutoff) {
          end.add(Calendar.DATE, 1);
      }
      return new DateIterator(start, end);
  }

  /**
   * <p>This constructs an <code>Iterator</code> over each day in a date
   * range defined by a focus date and range style.</p>
   *
   * <p>For instance, passing Thursday, July 4, 2002 and a
   * <code>RANGE_MONTH_SUNDAY</code> will return an <code>Iterator</code>
   * that starts with Sunday, June 30, 2002 and ends with Saturday, August 3,
   * 2002, returning a Calendar instance for each intermediate day.</p>
   *
   * @param focus  the date to work with, either
   *  <code>Date</code> or <code>Calendar</code>
   * @param rangeStyle  the style constant to use. Must be one of the range
   * styles listed for the {@link #iterator(Calendar, int)} method.
   * @return the date iterator
   * @throws IllegalArgumentException if the date
   *  is <code>null</code>
   * @throws ClassCastException if the object type is
   *  not a <code>Date</code> or <code>Calendar</code>
   */
  public static Iterator iterator(Object focus, int rangeStyle) {
      if (focus == null) {
          throw new IllegalArgumentException("The date must not be null");
      }
      if (focus instanceof Date) {
          return iterator((Date) focus, rangeStyle);
      } else if (focus instanceof Calendar) {
          return iterator((Calendar) focus, rangeStyle);
      } else {
          throw new ClassCastException("Could not iterate based on " + focus);
      }
  }
  /**
   * <p>Truncate this date, leaving the field specified as the most
   * significant field.</p>
   *
   * <p>For example, if you had the datetime of 28 Mar 2002
   * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
   * 2002 13:00:00.000.  If this was passed with MONTH, it would
   * return 1 Mar 2002 0:00:00.000.</p>
   * 
   * @param date  the date to work with, either <code>Date</code>
   *  or <code>Calendar</code>
   * @param field  the field from <code>Calendar</code>
   *  or <code>SEMI_MONTH</code>
   * @return the rounded date
   * @throws IllegalArgumentException if the date
   *  is <code>null</code>
   * @throws ClassCastException if the object type is not a
   *  <code>Date</code> or <code>Calendar</code>
   * @throws ArithmeticException if the year is over 280 million
   */
  public static Date truncate(Object date, int field) {
      if (date == null) {
          throw new IllegalArgumentException("The date must not be null");
      }
      if (date instanceof Date) {
          return truncate((Date) date, field);
      } else if (date instanceof Calendar) {
          return truncate((Calendar) date, field).getTime();
      } else {
          throw new ClassCastException("Could not truncate " + date);
      }
  }

  /**
   * <p>Truncate this date, leaving the field specified as the most
   * significant field.</p>
   *
   * <p>For example, if you had the datetime of 28 Mar 2002
   * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
   * 2002 13:00:00.000.  If this was passed with MONTH, it would
   * return 1 Mar 2002 0:00:00.000.</p>
   * 
   * @param date  the date to work with
   * @param field  the field from <code>Calendar</code>
   *  or <code>SEMI_MONTH</code>
   * @return the rounded date (a different object)
   * @throws IllegalArgumentException if the date is <code>null</code>
   * @throws ArithmeticException if the year is over 280 million
   */
  public static Calendar truncate(Calendar date, int field) {
      if (date == null) {
          throw new IllegalArgumentException("The date must not be null");
      }
      Calendar truncated = (Calendar) date.clone();
      modify(truncated, field, false);
      return truncated;
  }
  //-----------------------------------------------------------------------
  /**
   * <p>Internal calculation method.</p>
   * 
   * @param val  the calendar
   * @param field  the field constant
   * @param round  true to round, false to truncate
   * @throws ArithmeticException if the year is over 280 million
   */
  private static void modify(Calendar val, int field, boolean round) {
      if (val.get(Calendar.YEAR) > 280000000) {
          throw new ArithmeticException("Calendar value too large for accurate calculations");
      }
      
      if (field == Calendar.MILLISECOND) {
          return;
      }

      // ----------------- Fix for LANG-59 ---------------------- START ---------------
      // see http://issues.apache.org/jira/browse/LANG-59
      //
      // Manually truncate milliseconds, seconds and minutes, rather than using
      // Calendar methods.

      Date date = val.getTime();
      long time = date.getTime();
      boolean done = false;

      // truncate milliseconds
      int millisecs = val.get(Calendar.MILLISECOND);
      if (!round || millisecs < 500) {
          time = time - millisecs;
      }
      if (field == Calendar.SECOND) {
          done = true;
      }

      // truncate seconds
      int seconds = val.get(Calendar.SECOND);
      if (!done && (!round || seconds < 30)) {
          time = time - (seconds * 1000L);
      }
      if (field == Calendar.MINUTE) {
          done = true;
      }

      // truncate minutes
      int minutes = val.get(Calendar.MINUTE);
      if (!done && (!round || minutes < 30)) {
          time = time - (minutes * 60000L);
      }

      // reset time
      if (date.getTime() != time) {
          date.setTime(time);
          val.setTime(date);
      }
      // ----------------- Fix for LANG-59 ----------------------- END ----------------

      boolean roundUp = false;
      for (int i = 0; i < fields.length; i++) {
          for (int j = 0; j < fields[i].length; j++) {
              if (fields[i][j] == field) {
                  //This is our field... we stop looping
                  if (round && roundUp) {
                      if (field == DateUtils.SEMI_MONTH) {
                          //This is a special case that's hard to generalize
                          //If the date is 1, we round up to 16, otherwise
                          //  we subtract 15 days and add 1 month
                          if (val.get(Calendar.DATE) == 1) {
                              val.add(Calendar.DATE, 15);
                          } else {
                              val.add(Calendar.DATE, -15);
                              val.add(Calendar.MONTH, 1);
                          }
                      } else {
                          //We need at add one to this field since the
                          //  last number causes us to round up
                          val.add(fields[i][0], 1);
                      }
                  }
                  return;
              }
          }
          //We have various fields that are not easy roundings
          int offset = 0;
          boolean offsetSet = false;
          //These are special types of fields that require different rounding rules
          switch (field) {
              case DateUtils.SEMI_MONTH:
                  if (fields[i][0] == Calendar.DATE) {
                      //If we're going to drop the DATE field's value,
                      //  we want to do this our own way.
                      //We need to subtrace 1 since the date has a minimum of 1
                      offset = val.get(Calendar.DATE) - 1;
                      //If we're above 15 days adjustment, that means we're in the
                      //  bottom half of the month and should stay accordingly.
                      if (offset >= 15) {
                          offset -= 15;
                      }
                      //Record whether we're in the top or bottom half of that range
                      roundUp = offset > 7;
                      offsetSet = true;
                  }
                  break;
              case Calendar.AM_PM:
                  if (fields[i][0] == Calendar.HOUR_OF_DAY) {
                      //If we're going to drop the HOUR field's value,
                      //  we want to do this our own way.
                      offset = val.get(Calendar.HOUR_OF_DAY);
                      if (offset >= 12) {
                          offset -= 12;
                      }
                      roundUp = offset > 6;
                      offsetSet = true;
                  }
                  break;
          }
          if (!offsetSet) {
              int min = val.getActualMinimum(fields[i][0]);
              int max = val.getActualMaximum(fields[i][0]);
              //Calculate the offset from the minimum allowed value
              offset = val.get(fields[i][0]) - min;
              //Set roundUp if this is more than half way between the minimum and maximum
              roundUp = offset > ((max - min) / 2);
          }
          //We need to remove this field
          if (offset != 0) {
              val.set(fields[i][0], val.get(fields[i][0]) - offset);
          }
      }
      throw new IllegalArgumentException("The field " + field + " is not supported");

  }
  /**
   * <p>Date iterator.</p>
   */
  static class DateIterator implements Iterator {
      private final Calendar endFinal;
      private final Calendar spot;
      
      /**
       * Constructs a DateIterator that ranges from one date to another. 
       *
       * @param startFinal start date (inclusive)
       * @param endFinal end date (not inclusive)
       */
      DateIterator(Calendar startFinal, Calendar endFinal) {
          super();
          this.endFinal = endFinal;
          spot = startFinal;
          spot.add(Calendar.DATE, -1);
      }

      /**
       * Has the iterator not reached the end date yet?
       *
       * @return <code>true</code> if the iterator has yet to reach the end date
       */
      public boolean hasNext() {
          return spot.before(endFinal);
      }

      /**
       * Return the next calendar in the iteration
       *
       * @return Object calendar for the next date
       */
      public Object next() {
          if (spot.equals(endFinal)) {
              throw new RuntimeException();
          }
          spot.add(Calendar.DATE, 1);
          return spot.clone();
      }

      /**
       * Always throws UnsupportedOperationException.
       * 
       * @throws UnsupportedOperationException
       * @see java.util.Iterator#remove()
       */
      public void remove() {
          throw new UnsupportedOperationException();
      }
  }
}

   
    
    
    
    
    
    
  








Related examples in the same category

1.Represents a sequence of integer values, either ascending or descending.
2.Finds the value in the range (start,limit) of the largest element (rank) where the count of all smaller elements in that range is less than or equals target.
3.IntRange represents an inclusive range of ints.
4.LongRange represents an inclusive range of longs.
5.Byte Range
6.NumberRange represents an inclusive range of java.lang.Number objects of the same type.
7.Represents a range of Number objects.
8.A numeric range has a high, low, mean, root-mean-square, standard deviation, and the count of how many samples it contains.
9.A range of integers.
10.Value Range generic structure
11.Long Range
12.Class for storing start and end integer offsets.
13.Integer Sequence Generator
14.A numerical interval