TimeArrayTimeZoneRule.java

// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
 *******************************************************************************
 * Copyright (C) 2007-2010, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 */
package com.ibm.icu.util;

import java.util.Arrays;
import java.util.Date;

/**
 * <code>TimeArrayTimeZoneRule</code> represents a time zone rule whose start times are defined by
 * an array of milliseconds since the standard base time.
 *
 * @stable ICU 3.8
 */
public class TimeArrayTimeZoneRule extends TimeZoneRule {

    private static final long serialVersionUID = -1117109130077415245L;

    private final long[] startTimes;
    private final int timeType;

    /**
     * Constructs a <code>TimeArrayTimeZoneRule</code> with the name, the GMT offset of its standard
     * time, the amount of daylight saving offset adjustment and the array of times when this rule
     * takes effect.
     *
     * @param name The time zone name.
     * @param rawOffset The UTC offset of its standard time in milliseconds.
     * @param dstSavings The amount of daylight saving offset adjustment in milliseconds. If this ia
     *     a rule for standard time, the value of this argument is 0.
     * @param startTimes The start times in milliseconds since the base time (January 1, 1970,
     *     00:00:00).
     * @param timeType The time type of the start times, which is one of <code>
     *     DataTimeRule.WALL_TIME</code>, <code>STANDARD_TIME</code> and <code>UTC_TIME</code>.
     * @stable ICU 3.8
     */
    public TimeArrayTimeZoneRule(
            String name, int rawOffset, int dstSavings, long[] startTimes, int timeType) {
        super(name, rawOffset, dstSavings);
        if (startTimes == null || startTimes.length == 0) {
            throw new IllegalArgumentException("No start times are specified.");
        } else {
            this.startTimes = startTimes.clone();
            Arrays.sort(this.startTimes);
        }
        this.timeType = timeType;
    }

    /**
     * Gets the array of start times used by this rule.
     *
     * @return An array of the start times in milliseconds since the base time (January 1, 1970,
     *     00:00:00 GMT).
     * @stable ICU 3.8
     */
    public long[] getStartTimes() {
        return startTimes.clone();
    }

    /**
     * Gets the time type of the start times used by this rule. The return value is either <code>
     * DateTimeRule.WALL_TIME</code> or <code>DateTimeRule.STANDARD_TIME</code> or <code>
     * DateTimeRule.UTC_TIME</code>.
     *
     * @return The time type used of the start times used by this rule.
     * @stable ICU 3.8
     */
    public int getTimeType() {
        return timeType;
    }

    /**
     * {@inheritDoc}
     *
     * @stable ICU 3.8
     */
    @Override
    public Date getFirstStart(int prevRawOffset, int prevDSTSavings) {
        return new Date(getUTC(startTimes[0], prevRawOffset, prevDSTSavings));
    }

    /**
     * {@inheritDoc}
     *
     * @stable ICU 3.8
     */
    @Override
    public Date getFinalStart(int prevRawOffset, int prevDSTSavings) {
        return new Date(getUTC(startTimes[startTimes.length - 1], prevRawOffset, prevDSTSavings));
    }

    /**
     * {@inheritDoc}
     *
     * @stable ICU 3.8
     */
    @Override
    public Date getNextStart(long base, int prevOffset, int prevDSTSavings, boolean inclusive) {
        int i = startTimes.length - 1;
        for (; i >= 0; i--) {
            long time = getUTC(startTimes[i], prevOffset, prevDSTSavings);
            if (time < base || (!inclusive && time == base)) {
                break;
            }
        }
        if (i == startTimes.length - 1) {
            return null;
        }
        return new Date(getUTC(startTimes[i + 1], prevOffset, prevDSTSavings));
    }

    /**
     * {@inheritDoc}
     *
     * @stable ICU 3.8
     */
    @Override
    public Date getPreviousStart(long base, int prevOffset, int prevDSTSavings, boolean inclusive) {
        int i = startTimes.length - 1;
        for (; i >= 0; i--) {
            long time = getUTC(startTimes[i], prevOffset, prevDSTSavings);
            if (time < base || (inclusive && time == base)) {
                return new Date(time);
            }
        }
        return null;
    }

    /**
     * {@inheritDoc}
     *
     * @stable ICU 3.8
     */
    @Override
    public boolean isEquivalentTo(TimeZoneRule other) {
        if (!(other instanceof TimeArrayTimeZoneRule)) {
            return false;
        }
        if (timeType == ((TimeArrayTimeZoneRule) other).timeType
                && Arrays.equals(startTimes, ((TimeArrayTimeZoneRule) other).startTimes)) {
            return super.isEquivalentTo(other);
        }
        return false;
    }

    /**
     * {@inheritDoc}<br>
     * <br>
     * Note: This method in <code>TimeArrayTimeZoneRule</code> always returns true.
     *
     * @stable ICU 3.8
     */
    @Override
    public boolean isTransitionRule() {
        return true;
    }

    /* Get UTC of the time with the raw/dst offset */
    private long getUTC(long time, int raw, int dst) {
        if (timeType != DateTimeRule.UTC_TIME) {
            time -= raw;
        }
        if (timeType == DateTimeRule.WALL_TIME) {
            time -= dst;
        }
        return time;
    }

    /**
     * Returns a <code>String</code> representation of this <code>TimeArrayTimeZoneRule</code>
     * object. This method is used for debugging purpose only. The string representation can be
     * changed in future version of ICU without any notice.
     *
     * @stable ICU 3.8
     */
    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder();
        buf.append(super.toString());
        buf.append(", timeType=");
        buf.append(timeType);
        buf.append(", startTimes=[");
        for (int i = 0; i < startTimes.length; i++) {
            if (i != 0) {
                buf.append(", ");
            }
            buf.append(Long.toString(startTimes[i]));
        }
        buf.append("]");
        return buf.toString();
    }
}