Package ch.agent.t2.time.engine

Source Code of ch.agent.t2.time.engine.TimeTools

/*
*   Copyright 2011-2013 Hauser Olsson GmbH
*
* Licensed 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.
*
*/
package ch.agent.t2.time.engine;

import java.util.Arrays;

import ch.agent.t2.T2Exception;
import ch.agent.t2.T2Msg;
import ch.agent.t2.T2Msg.K;
import ch.agent.t2.time.Day;
import ch.agent.t2.time.DayOfWeek;
import ch.agent.t2.time.Resolution;
import ch.agent.t2.time.TimeIndex;
import ch.agent.t2.time.TimeParts;

/**
* TimeTools provides a selection of methods useful in time computations.
* These methods do not need any state.
*
* @author Jean-Paul Vetterli
*/
public class TimeTools {
 
  /**
   * Number of days in 400 years, the smallest repeating sequence of days.
   */
  private static final long DAYS_IN_400_YEARS = 365 * 303 + 366 * 97;
 
  /**
   * Number of days in the year before this month, January = 0.
   */
  private static final int[] daysToMonthCommonYear = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
  /**
   * Number of days in a leap year before this month, January = 0.
   */
  private static final int[] daysToMonthLeapYear = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };

  /**
   * Return the number of days for the indicated year and month.
   *
   * @param year
   *            a non-negative number
   * @param month
   *            a number in the range [1, 12]
   * @return the number of days
   */
  public static int daysInMonth(long year, int month) {
    int[] daysToMonth = isLeap(year) ? daysToMonthLeapYear : daysToMonthCommonYear;
    if (month == 12)
      return 31;
    else
      return daysToMonth[month] - daysToMonth[month - 1];
  }

  /**
   * Return the number of days in the year before the indicated month. The
   * caller is assumed to pass meaningful inputs.
   *
   * @param year
   *            a non-negative number
   * @param month
   *            a number between 1 and 12
   * @return the number of days in this year before this month
   */
  public static int daysToMonth(long year, int month) {
    int[] daysToMonth = isLeap(year) ? daysToMonthLeapYear : daysToMonthCommonYear;
    return daysToMonth[month - 1];
  }
 
  /**
   * Return true if the given year is a leap year.
   *
   * @param year
   *            a non-negative number
   *
   * @return true if the year is a leap year
   */
  public static boolean isLeap(long year) {
    if (year < 0)
      throw new IllegalArgumentException();
    return (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0);
  }
 
  /**
   * Return the number of leap years between year 0 and the year preceding the given
   * year. Assumes that year zero is a leap year.
   *
   * @param year
   *            a non-negative number
   * @return the number of leap year before the given year
   */
  public static long leapYears(long year) {
    if (year < 0)
      throw new IllegalArgumentException();
    if (year == 0)
      return 0;
    year--;
    return 1 + year / 4 - year / 100 + year / 400;
  }

  /**
   * Return an array with the month and the day for the given year and day in year.
   *
   * @param year a non-negative number
   * @param day the day number of the year, with the first day = 1
   * @return a two element array with the month and the day
   */
  public static int[] computeMonthAndDay(long year, int day) {
    int[] md = new int[2];
    int[] daysToMonth = isLeap(year) ? daysToMonthLeapYear : daysToMonthCommonYear;
    int i = Arrays.binarySearch(daysToMonth, day - 1);
    if (i >= 0) {
      md[0] = i + 1;
      md[1] = 1; // first day of month
    } else {
      i = -i - 2;
      md[0] = i + 1;
      md[1] = day - daysToMonth[i];
    }
    return md;
  }

  /**
   * Extract hours, minutes and seconds from totalSeconds.
   *
   * @param totalSeconds a non-negative number
   * @param tp a non-null time parts object
   */
  public static void computeHMS(long totalSeconds, TimeParts tp) {
    if (totalSeconds < 0)
      throw new IllegalArgumentException("totalSeconds negative");
    // avoid modulo arithmetic
    long minutes = totalSeconds / 60;
    tp.setSec((int) (totalSeconds - minutes * 60));
    tp.setHour((int) (minutes / 60));
    tp.setMin((int)(minutes - tp.getHour() * 60));
  }
 
  /**
   * Extract years, month of year and day of month from total days.
   * This method must deal
   * with large inputs, corresponding to ridiculously large years. TimeIndex
   * inputs are not validated at object creation time. This is for performance
   * reasons: many TimeIndex objects to create, few of them to format. So we
   * must deal with the issue here. To avoid the consequence of losing
   * precision when converting large longs to double, we avoid using doubles
   * altogether.
   *
   * @param days a non-negative number
   * @param tp
   *            time parts to fill in
   */
  public static void computeYMD(long days, TimeParts tp) {
    if (days < 0)
      throw new IllegalArgumentException("" + days);

    /*
     * By definition of leap years, the number of leap years during a 400
     * year interval is constant (97), and the number of days is also a
     * constant. The algorithm to determine the year, month and day, is
     * based on this fact.
     *
     * The first step computes the number of full 400 years intervals in the
     * total number of days and the number of days remaining. These can be
     * computed exactly. The second step computes the number of years and
     * days in the remainder. This second step is implemented using pseudo
     * years of 365 days and an error correction scheme, taking in account
     * that at most 97 days have been ignored, less than a full year. The
     * error correction scheme is complicated by the fact that the relevant
     * year can itself be a leap year.
     */
   
    long y400Intervals = days / DAYS_IN_400_YEARS;
    int y400Remainder = (int) (days - y400Intervals * DAYS_IN_400_YEARS);
   
    int remainingYears = y400Remainder / 365;
    int dayOffset = y400Remainder - remainingYears * 365 - (int) TimeTools.leapYears(remainingYears);
   
    if (dayOffset < 0) {
      remainingYears--;
      dayOffset += (TimeTools.isLeap(remainingYears) ? 366 : 365);
    }
   
    tp.setYear(y400Intervals * 400 + remainingYears);
    int[]monthDay = TimeTools.computeMonthAndDay(tp.getYear(), dayOffset + 1);
    tp.setMonth(monthDay[0]);
    tp.setDay(monthDay[1]);
  }

  /**
   * Return the numeric representation of the time specified.
   * <p>
   * The method has been deprecrated in version 1.0.1 of the class.
   * Use {@link TimeParts#asRawIndex(Resolution)} instead.
   *
   * @param unit a non-null resolution
   * @param tp a non-null time parts object
   * @return a numeric time index
   * @throws T2Exception
   */
  @Deprecated
  public static long makeRawIndex(Resolution unit, TimeParts tp) throws T2Exception {
    return tp.asRawIndex(unit);
  }
 
  /**
   * Return the day index computed from the uncompressed numerical time.
   *
   * @param unit a non- null resolution
   * @param time a non-negative numerical time index
   * @return a number of days
   * @throws T2Exception
   */
  public static long dayIndex(Resolution unit, long time) throws T2Exception {
    long dayIndex;
    switch (unit) {
    case DAY:
      dayIndex = time;
      break;
    case HOUR:
      dayIndex = time / 24;
      break;
    case MIN:
      dayIndex = time / (24 * 60);
      break;
    case SEC:
      dayIndex = time / (24 * 60 * 60);
      break;
    case MSEC:
      dayIndex = time / (24L * 60L * 60L * 1000L);
      break;
    case USEC:
      dayIndex = time / (24L * 60L * 60L * 1000000L);
      break;
    default:
      throw T2Msg.exception(K.T1060, unit);
    }
    return dayIndex;
  }
 
  /**
   * Return the day of week for the given unit and uncompressed time index.
   * The computation assumes that January 1 of year zero falls on a Saturday.
   *
   * @param time an uncompressed numerical time
   * @return a {@link DayOfWeek}
   * @throws T2Exception
   */
  public static DayOfWeek getDayOfWeek(Resolution unit, long time) throws T2Exception {
    int day = (int)(TimeTools.dayIndex(unit, time) % 7); // 0 -> Saturday
    if (day == 0)
      day = 6;
    else
      day--;
    return DayOfWeek.values()[day];
  }
 
  /**
   * Return the day number of the month or of the year given the day's name and rank.
   * When the month parameter has a value of zero, the day of the year will be computed.
   * A zero result indicates that a maximal rank of 5 or -5 (for a month) or 53 or -53 (for a year)
   * was requested but there is no such day in the given month or year.
   *
   * @param year a non-negative number
   * @param month a number in [1, 12] for the day of the month, 0 for the day of the year
   * @param name the day of week
   * @param rank a non-zero number in the range [-5, 5] or [-53, 53]
   * @return the day of the month or the year, starting with 1
   * @throws T2Exception
   */
  public static int getDayByRank(long year, int month, DayOfWeek name, int rank) throws T2Exception {
    int max = 5;
    if (month == 0)
      max = 53;
    if (rank == 0 || rank < -max || rank > max)
      throw T2Msg.exception(K.T1051, rank, -max, max);
    int daysInPeriod;
    if (month == 0) {
      month = 1;
      daysInPeriod = isLeap(year) ? 366 : 365;
    } else
      daysInPeriod = daysInMonth(year, month);
    TimeIndex t = new Day(year, month, 1);
    DayOfWeek firstOfPeriod = t.getDayOfWeek();
    int workRank = rank;
    if (workRank < 0)
      workRank = max; // try the max
    int week1Offset = name.ordinal() - firstOfPeriod.ordinal();
    if (week1Offset < 0)
      week1Offset += 7;
    int dayOfPeriod = 1 + week1Offset + (workRank - 1) * 7;
    if (rank < 0) {
      if (dayOfPeriod > daysInPeriod)
        dayOfPeriod -= 7; // tried the max, was too much
      dayOfPeriod += (rank + 1) * 7;
      if (dayOfPeriod < 0)
        dayOfPeriod = 0;
    } else {
      if (dayOfPeriod > daysInPeriod)
        dayOfPeriod = 0;
    }
    return dayOfPeriod;
  }

  /**
   * Return the TimeIndex for the day defined by name and rank within the
   * month of the indicated date. If the rank is negative, days are counted
   * from the end, with -1 meaning the last day. The result is a TimeIndex
   * with DAY resolution. For example
   * <blockquote>
   * <code>getDayOfMonthByRank(DayOfWeek.Mon, -1)</code>
   * </blockquote>
   * returns the last Monday of the month. If the day does not
   * exist, like for a 5th Monday of a month with only four Mondays, the
   * method returns null.
   *
   * @param time
   *            the reference time
   * @param dayName
   *            the day of week
   * @param rank
   *            a non-zero number in the range [-5, 5]
   * @return the day
   * @throws T2Exception
   */
  public static Day getDayOfMonthByRank(TimeIndex time, DayOfWeek dayName, int rank) throws T2Exception {
    if (time.getTimeDomain().compareResolutionTo(Resolution.MONTH) > 0)
      throw T2Msg.exception(K.T1059, time.toString(), Resolution.MONTH.name());
    long y = time.getYear();
    int m = time.getMonth();
    int day = getDayByRank(y, m, dayName, rank);
    return day == 0 ? null : new Day(y, m, day);
  }

  /**
   * Return the TimeIndex for the day defined by name and rank within the year
   * of the indicated date. If the rank is negative, days are counted from the
   * end, with -1 meaning the last day. The result is a TimeIndex with DAY
   * resolution. For example <xmp> getDayOfYearByRank(DayOfWeek.Mon, -1)</xmp>
   * returns the last Monday of the year. If the day does not exist, like for
   * a 53d Monday of a year with only 52 Mondays, the method returns null.
   *
   * @param time
   *            the reference time
   * @param dayName
   *            the day of week
   * @param rank
   *            a non-zero number in the range [-53, 53]
   * @return the day
   * @throws T2Exception
   */
  public static Day getDayOfYearByRank(TimeIndex time, DayOfWeek dayName, int rank) throws T2Exception {
    long y = time.getYear();
    int yearDay = getDayByRank(y, 0, dayName, rank);
    if (yearDay == 0)
      return null;
    else {
      int[] md = computeMonthAndDay(y, yearDay);
      return new Day(y, md[0], md[1]);
    }
  }

}
TOP

Related Classes of ch.agent.t2.time.engine.TimeTools

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.