1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3/*
4 *******************************************************************************
5 * Copyright (C) 2007-2010, International Business Machines Corporation and    *
6 * others. All Rights Reserved.                                                *
7 *******************************************************************************
8 */
9package com.ibm.icu.util;
10import java.util.Arrays;
11import java.util.Date;
12
13/**
14 * <code>TimeArrayTimeZoneRule</code> represents a time zone rule whose start times are
15 * defined by an array of milliseconds since the standard base time.
16 *
17 * @stable ICU 3.8
18 */
19public class TimeArrayTimeZoneRule extends TimeZoneRule {
20
21    private static final long serialVersionUID = -1117109130077415245L;
22
23    private final long[] startTimes;
24    private final int timeType;
25
26    /**
27     * Constructs a <code>TimeArrayTimeZoneRule</code> with the name, the GMT offset of its
28     * standard time, the amount of daylight saving offset adjustment and
29     * the array of times when this rule takes effect.
30     *
31     * @param name          The time zone name.
32     * @param rawOffset     The UTC offset of its standard time in milliseconds.
33     * @param dstSavings    The amount of daylight saving offset adjustment in
34     *                      milliseconds.  If this ia a rule for standard time,
35     *                      the value of this argument is 0.
36     * @param startTimes    The start times in milliseconds since the base time
37     *                      (January 1, 1970, 00:00:00).
38     * @param timeType      The time type of the start times, which is one of
39     *                      <code>DataTimeRule.WALL_TIME</code>, <code>STANDARD_TIME</code>
40     *                      and <code>UTC_TIME</code>.
41     *
42     * @stable ICU 3.8
43     */
44    public TimeArrayTimeZoneRule(String name, int rawOffset, int dstSavings, long[] startTimes, int timeType) {
45        super(name, rawOffset, dstSavings);
46        if (startTimes == null || startTimes.length == 0) {
47            throw new IllegalArgumentException("No start times are specified.");
48        } else {
49            this.startTimes = startTimes.clone();
50            Arrays.sort(this.startTimes);
51        }
52        this.timeType = timeType;
53    }
54
55    /**
56     * Gets the array of start times used by this rule.
57     *
58     * @return  An array of the start times in milliseconds since the base time
59     *          (January 1, 1970, 00:00:00 GMT).
60     * @stable ICU 3.8
61     */
62    public long[] getStartTimes() {
63        return startTimes.clone();
64    }
65
66    /**
67     * Gets the time type of the start times used by this rule.  The return value
68     * is either <code>DateTimeRule.WALL_TIME</code> or <code>DateTimeRule.STANDARD_TIME</code>
69     * or <code>DateTimeRule.UTC_TIME</code>.
70     *
71     * @return The time type used of the start times used by this rule.
72     * @stable ICU 3.8
73     */
74    public int getTimeType() {
75        return timeType;
76    }
77
78    /**
79     * {@inheritDoc}
80     * @stable ICU 3.8
81     */
82    @Override
83    public Date getFirstStart(int prevRawOffset, int prevDSTSavings) {
84        return new Date(getUTC(startTimes[0], prevRawOffset, prevDSTSavings));
85    }
86
87    /**
88     * {@inheritDoc}
89     * @stable ICU 3.8
90     */
91    @Override
92    public Date getFinalStart(int prevRawOffset, int prevDSTSavings) {
93        return new Date(getUTC(startTimes[startTimes.length - 1], prevRawOffset, prevDSTSavings));
94    }
95
96    /**
97     * {@inheritDoc}
98     * @stable ICU 3.8
99     */
100    @Override
101    public Date getNextStart(long base, int prevOffset, int prevDSTSavings, boolean inclusive) {
102        int i = startTimes.length - 1;
103        for (; i >= 0; i--) {
104            long time = getUTC(startTimes[i], prevOffset, prevDSTSavings);
105            if (time < base || (!inclusive && time == base)) {
106                break;
107            }
108        }
109        if (i == startTimes.length - 1) {
110            return null;
111        }
112        return new Date(getUTC(startTimes[i + 1], prevOffset, prevDSTSavings));
113    }
114
115    /**
116     * {@inheritDoc}
117     * @stable ICU 3.8
118     */
119    @Override
120    public Date getPreviousStart(long base, int prevOffset, int prevDSTSavings, boolean inclusive) {
121        int i = startTimes.length - 1;
122        for (; i >= 0; i--) {
123            long time = getUTC(startTimes[i], prevOffset, prevDSTSavings);
124            if (time < base || (inclusive && time == base)) {
125                return new Date(time);
126            }
127        }
128        return null;
129    }
130
131    /**
132     * {@inheritDoc}
133     * @stable ICU 3.8
134     */
135    @Override
136    public boolean isEquivalentTo(TimeZoneRule other) {
137        if (!(other instanceof TimeArrayTimeZoneRule)) {
138            return false;
139        }
140        if (timeType == ((TimeArrayTimeZoneRule)other).timeType
141                && Arrays.equals(startTimes, ((TimeArrayTimeZoneRule)other).startTimes)) {
142            return super.isEquivalentTo(other);
143        }
144        return false;
145    }
146
147    /**
148     * {@inheritDoc}<br><br>
149     * Note: This method in <code>TimeArrayTimeZoneRule</code> always returns true.
150     * @stable ICU 3.8
151     */
152    @Override
153    public boolean isTransitionRule() {
154        return true;
155    }
156
157    /* Get UTC of the time with the raw/dst offset */
158    private long getUTC(long time, int raw, int dst) {
159        if (timeType != DateTimeRule.UTC_TIME) {
160            time -= raw;
161        }
162        if (timeType == DateTimeRule.WALL_TIME) {
163            time -= dst;
164        }
165        return time;
166    }
167
168    /**
169     * Returns a <code>String</code> representation of this <code>TimeArrayTimeZoneRule</code> object.
170     * This method is used for debugging purpose only.  The string representation can be changed
171     * in future version of ICU without any notice.
172     *
173     * @stable ICU 3.8
174     */
175    @Override
176    public String toString() {
177        StringBuilder buf = new StringBuilder();
178        buf.append(super.toString());
179        buf.append(", timeType=");
180        buf.append(timeType);
181        buf.append(", startTimes=[");
182        for (int i = 0; i < startTimes.length; i++) {
183            if (i != 0) {
184                buf.append(", ");
185            }
186            buf.append(Long.toString(startTimes[i]));
187        }
188        buf.append("]");
189        return buf.toString();
190    }
191}
192