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






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.
 */




/**
 * A suite of utilities surrounding the use of the
 * {@link java.util.Calendar} and {@link java.util.Date} object.
 * 
 * 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.
 * 
 *   
 *   
 *
 * @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;

  //-----------------------------------------------------------------------
  /**
   * This constructs an <code>Iterator</code> over each day in a date
   * range defined by a focus date and range style.
   *
   * 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.
   *
   * This method provides an iterator that returns Calendar objects.
   * The days are progressed using {@link Calendar#add(int, int)}.
   *
   * @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);
  }

  /**
   * This constructs an <code>Iterator</code> over each day in a date
   * range defined by a focus date and range style.
   *
   * 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.
   *
   * This method provides an iterator that returns Calendar objects.
   * The days are progressed using {@link Calendar#add(int, int)}.
   *
   * @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);
  }

  /**
   * This constructs an <code>Iterator</code> over each day in a date
   * range defined by a focus date and range style.
   *
   * 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.
   *
   * @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);
      }
  }
  /**
   * Truncate this date, leaving the field specified as the most
   * significant field.
   *
   * 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.
   * 
   * @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);
      }
  }

  /**
   * Truncate this date, leaving the field specified as the most
   * significant field.
   *
   * 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.
   * 
   * @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;
  }
  //-----------------------------------------------------------------------
  /**
   * Internal calculation method.
   * 
   * @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");

  }
  /**
   * Date iterator.
   */
  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();
      }
  }
}








9.37.Iterator
9.37.1.Use Iterator to loop through ArrayList
9.37.2.Use Iterator to loop through the HashMap class
9.37.3.Iterate through a Collection using Java Iterator
9.37.4.Traverse through ArrayList in forward direction using Java ListIterator
9.37.5.Traverse through ArrayList in reverse direction using Java ListIterator
9.37.6.Iterate a Collection and remove an item (Exception, wrong version)
9.37.7.Remove an element from Collection using Java Iterator
9.37.8.Iterator from LinkedList
9.37.9.Iterate through elements of Java LinkedList using Iterator
9.37.10.An Iterator that wraps a number of Iterators
9.37.11.Array Iterator
9.37.12.Create singleton Iterator
9.37.13.Cyclic Iteration
9.37.14.Iterator Union of Iterators
9.37.15.Iterator Utils
9.37.16.Iterator class for sparse values in an array.
9.37.17.Iterator class for values contained in an array range.
9.37.18.Sorted Iterator
9.37.19.Treat an Iterator as an Iterable
9.37.20.An Iterator that returns the elements of a specified array, or other iterators etc.
9.37.21.An Iterator wrapper for an Enumeration.
9.37.22.An Iterator wrapper for an Object[], allow us to deal with all array like structures in a consistent manner.
9.37.23.An iterator that breaks text into lines. The result is equal to BufferedReader.readLine().
9.37.24.Gets the child of the specified element having the specified unique name
9.37.25.Implements an java.util.Iterator over any array
9.37.26.Protects an given iterator by preventing calls to remove().
9.37.27.Returns an iterator over the children of the given element with the given tag name
9.37.28.Use an Iterator to cycle through a collection in the forward direction.
9.37.29.This constructs an Iterator over each day in a date range defined by a focus date and range style.