A utility class for representing a span of time. : Time « Development Class « Java






A utility class for representing a span of time.

     
/*
 * Copyright 2009 David Jurgens
 *
 * This file is part of the S-Space package and is covered under the terms and
 * conditions therein.
 *
 * The S-Space package is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation and distributed hereunder to you.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND NO REPRESENTATIONS OR WARRANTIES,
 * EXPRESS OR IMPLIED ARE MADE.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, WE MAKE
 * NO REPRESENTATIONS OR WARRANTIES OF MERCHANT- ABILITY OR FITNESS FOR ANY
 * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE OR DOCUMENTATION
 * WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER
 * RIGHTS.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

//package edu.ucla.sspace.util;

import java.util.Calendar;
import java.util.Date;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * A utility class for representing a span of time.  Instance of this class can
 * be used to determine whether two moments fall within the time span.<p>
 *
 * Time spans are expressed as a combination of time units and their associated
 * amounts.  Each amount may add up to be more than one of a larger time unit.
 * For example a time unit may be expressed as 1 month and 6 weeks.  Time
 * amounts that are not used should be set to 0, e.g. a time span representing a
 * single day would have only {@code days} set to 1 and all other time units as
 * 0.<p>
 *
 * Note that when the overloaded {@code Date} and {@code long} methods are used,
 * the instances are converted to the JVM's current calendar system.  This may
 * have a slight, noticeable affect due to daylight savings or any other
 * calendar-specific differences to how a date is calculated.
 *
 * @see Calendar
 * @see Date
 */
public class TimeSpan {

    /**
     * The pattern for recognizing an amount of time as a number followed by a
     * single character denoting the unit
     */
    private static final Pattern TIME_SPAN_PATTERN = 
  Pattern.compile("\\d+[a-zA-Z]");

    /**
     * The number of years in this time span
     */
    private final int years;

    /**
     * The number of months in this time span
     */
    private final int months;

    /**
     * The number of weeks in this time span
     */
    private final int weeks;

    /**
     * The number of days in this time span
     */
    private final int days;

    /**
     * The number of hours in this time span
     */
    private final int hours;

    /**
     * Creates a time span using the date string to specify the time units and
     * amounts.  Time units are specified using a single lower case letter of
     * the word for the time unit, e.g. {@code y} for year, {@code m} for month.
     *
     * @param timespan a string containing numbers each followed by a single
     *        character to denote the time unit
     *
     * @throws IllegalArgumentException if <ul> <li> any of the parameters are
     *         negative <li> if any of the time units are specified more than
     *         once</ul>
     */
    public TimeSpan(String timespan) {
  
  Matcher matcher = TIME_SPAN_PATTERN.matcher(timespan);

  // Keep track of which time units have been set so far using a bit flag
  // pattern.  Each of the 5 units is stored as a single bit in order of
  // length, with longest first.
  int unitBitFlags = 0;

  // Assign default values of 0 to all of the time ranges before hand,
  // then overwite those with what data the timespan string contains
  int y = 0;
  int m = 0;
  int w = 0;
  int d = 0;
  int h = 0;

  int prevEnd = 0;
  while (matcher.find()) {
      // check that the next time unit has a proper format by ensuring
      // that all the patterns occur with no character gaps, e.g. 30d is
      // valid but 30dd will cause an error from the extra 'd'.
      if (matcher.start() != prevEnd) {
    throw new IllegalArgumentException(
        "invalid time unit format: " + timespan);
      }
      prevEnd = matcher.end();
      String lengthStr = 
    timespan.substring(matcher.start(), matcher.end() -1);
      int length = Integer.parseInt(lengthStr);
      checkDuration(length);
      char timeUnit = timespan.charAt(matcher.end() - 1);

      // Update the appropriate time based on the unit
      switch (timeUnit) {
      case 'y':
    checkSetTwice(unitBitFlags, 0, "years");
    unitBitFlags |= (1 << 0);
    y = length;
    break;
      case 'm':
    checkSetTwice(unitBitFlags, 1, "months");
    unitBitFlags |= (1 << 1);
    m = length;
    break;
      case 'w':
    checkSetTwice(unitBitFlags, 2, "weeks");
    unitBitFlags |= (1 << 2);
    w = length;
    break;
      case 'd':
    checkSetTwice(unitBitFlags, 3, "days");
    unitBitFlags |= (1 << 3);
    d = length;
    break;
      case 'h':
    checkSetTwice(unitBitFlags, 4, "hours");
    unitBitFlags |= (1 << 4);
    h = length;
    break;
      default:
    throw new IllegalArgumentException(
        "Unknown time unit: " + timeUnit);
      }
  }

  // update the final variables;
  years = y;
  months = m;
  weeks = w;
  days = d;
  hours = h;
    }
    
    /**
     * Creates a time span for the specified duration.
     *
     * @param years the number of years for this time span
     * @param months the number of years for this time span
     * @param weeks the number of years for this time span
     * @param days the number of years for this time span
     * @param hours the number of years for this time span
     *
     * @throws IllegalArgumentException if any of the parameters are negative
     */
    public TimeSpan(int years, int months, int weeks, int days, int hours) {
  checkDuration(years);
  checkDuration(months);
  checkDuration(weeks);
  checkDuration(days);
  checkDuration(hours);
  
  this.years = years;
  this.months = months;
  this.weeks = weeks;
  this.days = days;
  this.hours = hours;
    }

    /**
     * Adds the duration of this time span to the provided {@code Calendar}
     * instance, moving it forward in time.
     *
     * @param c the calendar whose date will be moved forward by the duration of
     *        this time span
     */
    public void addTo(Calendar c) {
        c.add(Calendar.YEAR, years);
        c.add(Calendar.MONTH, months);
        c.add(Calendar.WEEK_OF_YEAR, weeks);
        c.add(Calendar.DAY_OF_YEAR, days);
        c.add(Calendar.HOUR_OF_DAY, hours);
    }

    /**
     * Adds the duration of this time span to the provided {@code Date}
     * instance, moving it forward in time.
     *
     * @param d the date whose value will be moved forward by the duration of
     *        this time span
     */
    public void addTo(Date d) {
  Calendar c = Calendar.getInstance();
  c.setTime(d);
        addTo(c);
        d.setTime(c.getTime().getTime());
    }

    /**
     * Checks whether the index is already set in the bit flags and throws an
     * exception if so.
     *
     * @param bigFlag an {code int} bit sequence, where each bit represents a a
     *        time span value whose value is {@code 1} if that value has been
     *        set
     * @param index the index of the field whose value is to be checked whether
     *        it has already been set
     * @param field the name of the index, which is used in the exception
     *        message
     *
     * @throws IllegalArgumentException if the value for the field has already
     *         been set
     */
    private static void checkSetTwice(int bitFlag, int index, String field) {
  // check that the field's index has not already been set
  if ((bitFlag & (1 << index)) != 0) {
      throw new IllegalArgumentException(field +  " is set twice");
  }  
    }

    /**
     * Throws an exception if the duration is negative
     */
    private static void checkDuration(int duration) {
  if (duration < 0) 
      throw new IllegalArgumentException(
    "Duration must be non-negative");
    }    

    /**
     * Returns the day component of this time span.  This value does not reflect
     * the total number of days that make up this time span, but rather how many
     * days were specified in addition to the other time components to comprise
     * the total duration.
     *
     * @return the day component of this time span
     */
    public int getDays() {
        return days;
    }
     
    /**
     * Returns the hour component of this time span.  This value does not
     * reflect the total number of hours that make up this time span, but rather
     * how many hours were specified in addition to the other time components to
     * comprise the total duration.
     *
     * @return the hour component of this time span
     */
    public int getHours() {
        return hours;
    }

    /**
     * Returns the month component of this time span.  This value does not
     * reflect the total number of months that make up this time span, but
     * rather how many months were specified in addition to the other time
     * components to comprise the total duration.
     *
     * @return the month component of this time span
     */
    public int getMonths() {
        return months;
    }

    /**
     * Returns the week component of this time span.  This value does not
     * reflect the total number of weeks that make up this time span, but rather
     * how many weeks were specified in addition to the other time components to
     * comprise the total duration.
     *
     * @return the week component of this time span
     */
    public int getWeeks() {
        return weeks;
    }

    /**
     * Returns the year component of this time span.  This value does not
     * reflect the total number of years that make up this time span, but rather
     * how many years were specified in addition to the other time components to
     * comprise the total duration.
     *
     * @return the year component of this time span
     */
    public int getYears() {
        return years;
    }

    /**
     * Returns {@code true} if the end date occurs after the start date during
     * the period of time represented by this time span.
     */
    public boolean insideRange(Calendar startDate,
             Calendar endDate) {
  // make a copy of the start time so that it is safe to modify it without
  // affecting the input parameter
  Calendar mutableStartDate = (Calendar)(startDate.clone());
  return isInRange(mutableStartDate, endDate);
    }
    
    /**
     * Returns {@code true} if the end date occurs after the start date during
     * the period of time represented by this time span.
     * 
     * @param mutableStartDate a <i>mutable<i> {@code Calendar} object that will
     *        be changed to the ending time of this time range as a side effect
     *        of this method
     */
    private boolean isInRange(Calendar mutableStartDate,
            Calendar endDate) {
  
  // ensure that the ending date does not occur before the time span would
  // have started
  if (endDate.before(mutableStartDate))
      return false;

  // update the start date to be the date at the end of the time span
  Calendar tsEnd = mutableStartDate;
  tsEnd.add(Calendar.YEAR, years);
  tsEnd.add(Calendar.MONTH, months);
  tsEnd.add(Calendar.WEEK_OF_YEAR, weeks);
  tsEnd.add(Calendar.DAY_OF_YEAR, days);
  tsEnd.add(Calendar.HOUR, hours);
    
  return endDate.before(tsEnd);
    }

    /**
     * Returns {@code true} if the end date occurs after the start date during
     * the period of time represented by this time span.
     */
    public boolean insideRange(Date startDate, Date endDate) {
  Calendar c1 = Calendar.getInstance();
  Calendar c2 = Calendar.getInstance();
  c1.setTime(startDate);
  c2.setTime(endDate);
  return isInRange(c1, c2);
    }

    /**
     * Returns {@code true} if the end date occurs after the start date during
     * the period of time represented by this time span.
     */
    public boolean insideRange(long startDate, long endDate) {
  Calendar c1 = Calendar.getInstance();
  Calendar c2 = Calendar.getInstance();
  c1.setTimeInMillis(startDate);
  c2.setTimeInMillis(endDate);
  return isInRange(c1, c2);
    }

    public String toString() {
  return String.format("TimeSpan: %dy%dm%dw%dd%dh", years, months, 
           weeks, days, hours);
    }

}
/*
 * Copyright 2009 David Jurgens
 *
 * This file is part of the S-Space package and is covered under the terms and
 * conditions therein.
 *
 * The S-Space package is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation and distributed hereunder to you.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND NO REPRESENTATIONS OR WARRANTIES,
 * EXPRESS OR IMPLIED ARE MADE.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, WE MAKE
 * NO REPRESENTATIONS OR WARRANTIES OF MERCHANT- ABILITY OR FITNESS FOR ANY
 * PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE OR DOCUMENTATION
 * WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER
 * RIGHTS.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/*

package edu.ucla.sspace.util;

import java.util.Calendar;
import java.util.Date;

import org.junit.Ignore;
import org.junit.Test;

import static org.junit.Assert.*;

// * Tests for the {@link TimeSpan} class.

@SuppressWarnings("deprecation")
public class TimeSpanTests {

    @Test public void testAddCalendar() {
        TimeSpan ts = new TimeSpan("1y");
        Calendar c = Calendar.getInstance();
        int year = c.get(Calendar.YEAR);
        ts.addTo(c);
        assertEquals(year + 1, c.get(Calendar.YEAR));

        ts = new TimeSpan("1m");
        for (int i = 0; i < 12; ++i) {
            int month = c.get(Calendar.MONTH);
            ts.addTo(c);
            assertEquals((month + 1) % 12, c.get(Calendar.MONTH));
        }
    }

    @SuppressWarnings("deprecated")
    @Test public void testAddDate() {
        TimeSpan ts = new TimeSpan("1y");
        Date d = new Date();
        int year = d.getYear();
        ts.addTo(d);
        assertEquals(year + 1, d.getYear());

        ts = new TimeSpan("1m");
        for (int i = 0; i < 12; ++i) {
            int month = d.getMonth();
            ts.addTo(d);
            assertEquals((month + 1) % 12, d.getMonth());
        }
    }
   
    @Test public void testStringConstructor() {
  TimeSpan ts = new TimeSpan("1y");
  ts = new TimeSpan("1m");
  ts = new TimeSpan("1w");
  ts = new TimeSpan("1d");
  ts = new TimeSpan("1h");
    }

    @Test public void testStringConstructorCombined() {
  TimeSpan ts = new TimeSpan("1y1m1w1d1h");
  assertEquals("TimeSpan: 1y1m1w1d1h", ts.toString());
    }

    @Test public void testStringConstructorCombinedRandomOrder() {
  TimeSpan ts = new TimeSpan("1h1d1m1y1w");
  assertEquals("TimeSpan: 1y1m1w1d1h", ts.toString());
    }

    @Test(expected=IllegalArgumentException.class)
    public void testStringConstructorRepeat() {
  TimeSpan ts = new TimeSpan("1y1y");
    }

    @Test(expected=IllegalArgumentException.class)
    public void testStringConstructorUnknownType() {
  TimeSpan ts = new TimeSpan("1z");
    }    

    @Test public void testYear() {
  TimeSpan ts = new TimeSpan("1y");
  Calendar now = Calendar.getInstance();
  Calendar lessThanYearFromNow = Calendar.getInstance();
  //lessThanYearFromNow.add(Calendar.YEAR, 1);
  lessThanYearFromNow.add(Calendar.MONTH, 1);
  assertTrue(ts.insideRange(now, lessThanYearFromNow));
    }

    @Test public void testYearOutside() {
  TimeSpan ts = new TimeSpan("1y");
  Calendar now = Calendar.getInstance();
  Calendar yearFromNow = Calendar.getInstance();
  yearFromNow.add(Calendar.YEAR, 1);
  assertFalse(ts.insideRange(now, yearFromNow));
    }

    @Test public void testMonth() {
  TimeSpan ts = new TimeSpan("1m");
  Calendar now = Calendar.getInstance();
  Calendar lessThanMonthFromNow = Calendar.getInstance();
  lessThanMonthFromNow.add(Calendar.WEEK_OF_YEAR, 1);
  assertTrue(ts.insideRange(now, lessThanMonthFromNow));
    }

    @Test public void testMonthOutside() {
  TimeSpan ts = new TimeSpan("1m");
  Calendar now = Calendar.getInstance();
  Calendar monthFromNow = Calendar.getInstance();
  monthFromNow.add(Calendar.MONTH, 1);
  assertFalse(ts.insideRange(now, monthFromNow));
    }

    @Test public void testWeek() {
  TimeSpan ts = new TimeSpan("1w");
  Calendar now = Calendar.getInstance();
  Calendar lessThanWeekFromNow = Calendar.getInstance();
  lessThanWeekFromNow.add(Calendar.DAY_OF_YEAR, 1);
  assertTrue(ts.insideRange(now, lessThanWeekFromNow));
    }

    @Test public void testWeekOutside() {
  TimeSpan ts = new TimeSpan("1w");
  Calendar now = Calendar.getInstance();
  Calendar weekFromNow = Calendar.getInstance();
  weekFromNow.add(Calendar.WEEK_OF_YEAR, 1);
  assertFalse(ts.insideRange(now, weekFromNow));
    }

    @Test public void testDay() {
  TimeSpan ts = new TimeSpan("1d");
  Calendar now = Calendar.getInstance();
  Calendar lessThanDayFromNow = Calendar.getInstance();
  lessThanDayFromNow.add(Calendar.HOUR, 1);
  assertTrue(ts.insideRange(now, lessThanDayFromNow));
    }

    @Test public void testDayOutside() {
  TimeSpan ts = new TimeSpan("1d");
  Calendar now = Calendar.getInstance();
  Calendar dayFromNow = Calendar.getInstance();
  dayFromNow.add(Calendar.DAY_OF_YEAR, 1);
  assertFalse(ts.insideRange(now, dayFromNow));
    }

    @Test public void testHour() {
  TimeSpan ts = new TimeSpan("1h");
  Calendar now = Calendar.getInstance();
  Calendar lessThanHourFromNow = Calendar.getInstance();
  lessThanHourFromNow.add(Calendar.MINUTE, 1);
  assertTrue(ts.insideRange(now, lessThanHourFromNow));
    }

    @Test public void testHourOutside() {
  TimeSpan ts = new TimeSpan("1h");
  Calendar now = Calendar.getInstance();
  Calendar hourFromNow = Calendar.getInstance();
  hourFromNow.add(Calendar.HOUR, 1);
  assertFalse(ts.insideRange(now, hourFromNow));
    }

    @Test public void testEndBeforeStart() {
  TimeSpan ts = new TimeSpan("1h");
  Calendar now = Calendar.getInstance();
  Calendar before = Calendar.getInstance();
  before.add(Calendar.HOUR, -1);
  assertFalse(ts.insideRange(now, before));
    }
}*/

   
    
    
    
    
  








Related examples in the same category

1.Get Time From Date
2.ISO8601 Date Time Format
3.Time Format
4.Time Formatter
5.Returns time string
6.Returns the given date with the time values cleared
7.Convert the time to the midnight of the currently set date
8.Compare both times and dates
9.Tells you if the date part of a datetime is in a certain time range
10.Returns the given date with time set to the end of the day
11.Convert milliseconds to readable string
12.Determines whether or not a date has any time values (hour, minute, seconds or millisecondsReturns the given date with the time values cleared
13.Returns a formatted String from time
14.Time library
15.Elapsed time in hours/minutes/seconds
16.Sets the time on the same day to 00:00:00.000
17.Determines if given times span at least an entire day
18.Converts a given time in milliseconds into a XMLGregorianCalendar object.
19.Time Distance
20.Time Formatter
21.Format time
22.GmtCalendar is a useful class for working with times that are based using GMT time.
23.Time Period
24.Represents the TSTInfo strcture within a time-stamp token (RFC 3161).
25.SimpleTimer enables bounded and unbounded waits.
26.Takes a time in milliseconds and returns an hours, minutes and seconds representation.
27.Format time in milliseconds