Package ch.agent.t2.time.engine

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

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

import ch.agent.t2.T2Exception;
import ch.agent.t2.T2Msg;
import ch.agent.t2.T2Msg.K;
import ch.agent.t2.time.Adjustment;
import ch.agent.t2.time.Day;
import ch.agent.t2.time.DayOfWeek;
import ch.agent.t2.time.DefaultExternalFormat;
import ch.agent.t2.time.ExternalTimeFormat;
import ch.agent.t2.time.Resolution;
import ch.agent.t2.time.ThirdFriday;
import ch.agent.t2.time.TimeDomain;
import ch.agent.t2.time.TimeDomainDefinition;
import ch.agent.t2.time.TimeDomainManager;
import ch.agent.t2.time.TimeIndex;
import ch.agent.t2.time.TimeParts;
import ch.agent.t2.time.Workday;
import ch.agent.t2.time.Year;


/**
* Time2 implements the behavior of {@link TimeIndex} as an immutable object.
* The design goals of <em>Time2</em> are flexibility and performance.
* It is not a replacement for {@link java.util.Date java.util.Date} and is not
* a competitor to <a href="http://joda-time.sourceforge.net/">Joda Time</a>.
* <em>Time2</em> has no time zones, no daylight savings, no
* locales, no Gregorian cutover. Dates before October 15 1582 do
* not correspond to historical dates. The base of <em>Time2</em> time is
* zero microseconds into January 1st of year zero:
* <p>
* <blockquote>
* <code>0000-01-01 00:00:00.000000</code>
* </blockquote>
* <p>
* It corresponds to the numerical time index 0L.
* <p>
* <b>Warning note about time comparisons</b>
* <p>
* This class implements {@link Comparable} with simple
* semantics. Basically, it considers "earlier" as "smaller".
* When time domain differs, times are converted before performing the
* comparison. The conversion is done in the following fashion:
* <ul>
* <li>If resolutions differ, the time with the lowest resolution is
* converted to the one with the highest resolution. For example comparing a
* {@link Day} to a {@link Year} will compare the day specified to the
* implicit default day of the year specified (which is January 1).
* <li>If resolutions do not differ, the times are converted to the unrestricted
* time domain for their resolution. For example the
* comparison of a {@link Workday} to a {@link ThirdFriday} will convert
* both to a {@link Day} before comparing.
* </ul>
* These semantics are not meaningful for all possible time domains.
* Applications using "exotic" time domains should consider writing a subclass
* of Time2 and override the {@link Comparable#compareTo(Object) compareTo} method.
* <p>
* @author Jean-Paul Vetterli
*/
public class Time2 implements TimeIndex {

  private int hash; // Time2 is immutable, so hash needs to be computed only once
 
  private TimeFactory domain;

  private long internalTime;

  private boolean internalTimeModified;

  private TimeParts tp;
 
  /**
   * The parameterless constructor is not used.
   */
  @SuppressWarnings("unused")
  private Time2() {
  }
 
  /**
   * Construct a time index with the given time domain.
   * An <b>unchecked</b> exception is thrown if the concrete time domain
   * is not a {@link TimeFactory}.
   *
   * @param domain a non-null {@link TimeFactory}
   */
  private Time2(TimeDomain domain) {
    if (domain == null)
      throw new IllegalArgumentException("domain null");
    try {
      this.domain = (TimeFactory) domain;
    } catch (ClassCastException e) {
      throw new IllegalArgumentException("domain " + domain.getLabel() + " is not a " +  TimeFactory.class.getSimpleName());
    }
  }
 
  /**
   * Construct a time index with the given time domain and numerical time index.
   * An <b>unchecked</b> exception is thrown if the concrete time domain
   * is not a {@link TimeFactory} or if the numerical time index is
   * out of range. The range depends on the domain but is very large.
   *
   * @param domain a non-null {@link TimeFactory}
   * @param time a valid numerical time index
   */
  protected Time2(TimeDomain domain, long time) {
    this(domain);
    try {
      this.domain.valid(time, false);
      setInternalTime(time);
    } catch (T2Exception e) {
      throw new IllegalArgumentException("time", e);
    }
  }
 
  /**
   * Construct a time index with the given time domain and parameters.
   * An <b>unchecked</b> exception is thrown if the concrete time domain
   * is not a {@link TimeFactory}.
   * If necessary the time is adjusted as allowed by the last argument.
   *
   * @param domain a non-null {@link TimeFactory}
   * @param year a year, which can be unusually large, depending on the domain
   * @param month a number between 1 and 12
   * @param day the day in the month, starting with 1
   * @param hour an hour in the range 0-23
   * @param min a minute in the range 0-59
   * @param sec a second in the range 0-59
   * @param usec the number of microseconds in the current second in the range 0-999999
   * @param adjust a non-null allowed adjustment mode
   * @throws T2Exception
   */
  protected Time2(TimeDomain domain, long year, int month, int day, int hour, int min, int sec,
      int usec, Adjustment adjust) throws T2Exception {
    this(domain);
    TimeParts tp = new TimeParts();
    tp.setYear(year);
    tp.setMonth(month);
    tp.setDay(day);
    tp.setHour(hour);
    tp.setMin(min);
    tp.setSec(sec);
    tp.setUsec(usec);
    set(tp, adjust);
  }
 
  /**
   * Construct a time index with the given time domain and string, with possible adjustment.
   * An <b>unchecked</b> exception is thrown if the concrete time domain
   * is not a {@link TimeFactory}.
   * The string is interpreted by an instance of {@link ExternalTimeFormat}
   * configured. By default it is {@link DefaultExternalFormat}.
   * <p>
   * If necessary the time is adjusted as allowed by the last argument.
   *
   * @param domain a non-null {@link TimeFactory}
   * @param time a string containing a representation of a date and time
   * @param adjustment a non-null allowed adjustment mode
   * @throws T2Exception
   */
  protected Time2(TimeDomain domain, String time, Adjustment adjustment) throws T2Exception {
    this(domain);
    set(time, adjustment);
  }
 
  /**
   * Construct a time index with the given time domain and string.
   * An <b>unchecked</b> exception is thrown if the concrete time domain
   * is not a {@link TimeFactory}.
   * The string is interpreted by an instance of {@link ExternalTimeFormat}
   * configured. By default it is {@link DefaultExternalFormat}.
   * <p>
   * No adjustment is allowed.
   *
   * @param domain a non-null {@link TimeFactory}
   * @param time a string containing a representation of a date and time
   * @throws T2Exception
   */
  protected Time2(TimeDomain domain, String time) throws T2Exception {
    this(domain, time, Adjustment.NONE);
  }
 
  private Time2(TimeIndex timeIndex, long increment) throws T2Exception {
    this(timeIndex.getTimeDomain());
    long t = timeIndex.asLong();
    long incrT = t + increment;
    // overflow?
    if (t > 0 && incrT < 0 || t < 0 && incrT > 0)
      throw T2Msg.exception(K.T1075, timeIndex.toString(), increment);
    try {
      domain.valid(incrT, false);
    } catch (T2Exception e) {
      throw T2Msg.exception(e, K.T1075, timeIndex.toString(), increment);
    }
    setInternalTime(incrT);
  }
 
  @Override
  public TimeIndex convert(TimeDomain domain) throws T2Exception {
    return convert(domain, Adjustment.NONE);
  }
 
  @Override
  public TimeIndex convert(TimeDomain domain, Adjustment adjustment) throws T2Exception {
    if (getTimeDomain().equals(domain))
      return this;
    resolve();
    switch(getTimeDomain().getResolution()) {
    case YEAR:
      tp.setMonth(1);
    case MONTH:
      tp.setDay(1);
      break;
    default:
    }
    return new Time2(domain, tp.getYear(), tp.getMonth(), tp.getDay(), tp.getHour(),
        tp.getMin(), tp.getSec(), tp.getUsec(), adjustment);
  }

  @Override
  public TimeDomain getTimeDomain() {
    return domain;
  }

  @Override
  public long asLong() {
    return getInternalTime();
  }
 
  @Override
  public int asOffset() throws T2Exception {
    long time = getInternalTime() - domain.getOrigin();
    if (time < Integer.MIN_VALUE || time > Integer.MAX_VALUE)
      throw T2Msg.exception(K.T1076, domain.getResolution(), time);
    return (int) time;
  }
 
  @Override
  public TimeIndex add(long increment) throws T2Exception {
    return new Time2(this, increment);
  }
 
  @Override
  public long sub(TimeIndex time) throws T2Exception {
    if (getTimeDomain().equals(time.getTimeDomain())) {
      return asLong() - time.asLong();
    } else
      throw T2Msg.exception(K.T1077, time.toString(), toString(),
          time.getTimeDomain().getLabel(), getTimeDomain().getLabel());
  }
 
  /**
   * Return the time as a time parts object.
   *
   * @return the time as a time parts object
   */
  TimeParts getTimeParts() {
    // package private
    resolve();
    return tp;
  }

  @Override
  public long getYear() {
    resolve();
    return tp.getYear();
  }

  @Override
  public int getMonth() {
    resolve();
    return tp.getMonth();
  }

  @Override
  public int getDay() {
    resolve();
    return tp.getDay();
  }

  @Override
  public int getHour() {
    resolve();
    return tp.getHour();
  }

  @Override
  public int getMinute() {
    resolve();
    return tp.getMin();
  }

  @Override
  public int getSecond() {
    resolve();
    return tp.getSec();
  }

  @Override
  public int getMicrosecond() {
    resolve();
    return tp.getUsec();
  }

  @Override
  public DayOfWeek getDayOfWeek() throws T2Exception {
    return domain.getDayOfWeek(this);
  }

  @Override
  public TimeIndex getDayByRank(Resolution basePeriod, DayOfWeek day, int rank) throws T2Exception {
    switch(basePeriod) {
    case MONTH:
      return TimeTools.getDayOfMonthByRank(this, day, rank);
    case YEAR:
      return TimeTools.getDayOfYearByRank(this, day, rank);
    default:
      throw T2Msg.exception(K.T1052, basePeriod.name());
    }
  }
 
  /**
   * Return a string representation of the time. In a <i>full</i>
   * representation, all components are always included, from years to
   * microseconds. In a standard representation, the formatting is delegated
   * to {@link ExternalTimeFormat#format(Resolution, TimeParts)}.
   *
   * @param full
   *            if true, returns a full representation
   * @return a string representation of the time
   */
  public String toString(boolean full) {
    resolve();
    if (full) {
      StringBuilder sb = new StringBuilder();
      Formatter fmt = new Formatter(sb);
      String plus = tp.getYear() > 9999 ? "+" : "";
      fmt.format("%s%04d-%02d-%02d %02d:%02d:%02d.%06d", plus, tp.getYear(), tp.getMonth(),
          tp.getDay(), tp.getHour(), tp.getMin(), tp.getSec(), tp.getUsec());
      fmt.close();
      return sb.toString();
    } else
      return domain.format(domain.getResolution(), tp);
  }
 
  @Override
  public String toString(String format) {
    if (format == null)
      return toString();
    resolve();
    if (format.length() == 0) {
      // TODO: should be localized (e.g. m/d yy)
      String yy = tp.getYear() + "";
      if (yy.length() >= 3)
        yy = yy.substring(2);
      return String.format("%d.%d.%s", tp.getDay(), tp.getMonth(), yy);
    }
    StringBuilder sb = new StringBuilder();
    Formatter fmt = new Formatter(sb);
    fmt.format(format, tp.getYear(), tp.getMonth(), tp.getDay(), tp.getHour(), tp.getMin(), tp.getSec(), tp.getUsec());
    fmt.close();
    return sb.toString();
  }

  @Override
  public String toString() {
    return toString(false);
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (!(obj instanceof Time2))
      return false;
    return (asLong() == ((Time2)obj).asLong()) && domain.equals(((Time2)obj).getTimeDomain());
  }

  @Override
  public int hashCode() {
    if (hash == 0)
      hash = 31 * domain.hashCode() + (new Long(asLong())).hashCode();
    return hash;
  }

  @Override
  public int compareTo(TimeIndex otherTime) {
    if (otherTime == null)
      throw new IllegalArgumentException("t null");
    if (otherTime.getTimeDomain().equals(getTimeDomain())) {
      long l1 = asLong();
      long l2 = otherTime.asLong();
      if (l1 < l2)
        return -1;
      else if (l1 > l2)
        return 1;
      else
        return 0;
    } else {
      int compareResols = getTimeDomain().getResolution().compareTo(otherTime.getTimeDomain().getResolution());
      if (compareResols == 0) {
        // convert  both to unrestricted domain
        TimeDomainDefinition def = new TimeDomainDefinition(null, getTimeDomain().getResolution(), 0L);
        TimeDomain unrestrictedDomain = TimeDomainManager.getFactory().get(def, true);
        TimeIndex converted = convertOrThrowRTE(unrestrictedDomain, this);
        TimeIndex otherConverted = convertOrThrowRTE(unrestrictedDomain, otherTime);
        return converted.compareTo(otherConverted);
      } else if (compareResols < 0) {
        // convert  both to highest resolution
        TimeDomainDefinition def = new TimeDomainDefinition(null, otherTime.getTimeDomain().getResolution(), 0L);
        TimeDomain unrestrictedDomain = TimeDomainManager.getFactory().get(def, true);
        TimeIndex converted = convertOrThrowRTE(unrestrictedDomain, this);
        return converted.compareTo(otherTime);
      } else {
        // convert  both to highest resolution
        TimeDomainDefinition def = new TimeDomainDefinition(null, getTimeDomain().getResolution(), 0L);
        TimeDomain unrestrictedDomain = TimeDomainManager.getFactory().get(def, true);
        TimeIndex otherConverted = convertOrThrowRTE(unrestrictedDomain, otherTime);
        return compareTo(otherConverted);
      }
    }
  }
 
  /**
   * Convert the time to the given time domain and turn any checked exception
   * to an unchecked one.
   *
   * @param domain
   *            non-null time domain
   * @param time
   *            non-null time index
   * @return the time converted to the time domain
   */
  private TimeIndex convertOrThrowRTE(TimeDomain domain, TimeIndex time) {
    try {
      return time.convert(domain);
    } catch (T2Exception e) {
      Exception cause = T2Msg.exception(e, K.T0005, time.toString(), domain);
      throw T2Msg.runtimeException(K.T0001, cause);
    }
  }
 
  /**
   * Set the time from a time parts object.
   * Silently ignore elements finer than the resolution.
   *
   * @param tp a non-null time parts object
   * @param adjust a non-null allowed adjustment mode
   * @throws T2Exception
   */
  private void set(TimeParts tp, Adjustment adjust) throws T2Exception {
    setInternalTime(domain.pack(tp, adjust));
  }

  /**
   * Set the time from a string.
   * Silently ignore elements finer than the resolution.
   * @param date a non-null string
   * @param adjust a non-null allowed adjustment mode
   * @throws T2Exception
   */
  private void set(String date, Adjustment adjust) throws T2Exception {
    TimeParts tp = domain.scan(date);
    set(tp, adjust);
  }
 
  /**
   * If not yet done, resolve a numerical time index into its constituent elements.
   */
  private void resolve() {
    if (internalTimeModified) {
      tp = domain.unpack(getInternalTime());
      internalTimeModified = false;
    }
  }
 
  /**
   * Return the numerical time index.
   *
   * @return the numerical time index
   */
  private long getInternalTime() {
    return internalTime;
  }
 
  /**
   * Set the numerical time index.
   *
   * @param time the numerical time index
   */
  private void setInternalTime(long time) {
    internalTimeModified = true;
    internalTime = time;
  }

}
TOP

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

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.