• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *******************************************************************************
3  * Copyright (C) 2007-2010, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  */
7 package com.ibm.icu.util;
8 import java.util.Date;
9 
10 import com.ibm.icu.impl.Grego;
11 
12 
13 /**
14  * <code>AnnualTimeZoneRule</code> is a class used for representing a time zone
15  * rule which takes effect annually.  Years used in this class are
16  * all Gregorian calendar years.
17  *
18  * @stable ICU 3.8
19  */
20 public class AnnualTimeZoneRule extends TimeZoneRule {
21 
22     private static final long serialVersionUID = -8870666707791230688L;
23 
24     /**
25      * The constant representing the maximum year used for designating a rule is permanent.
26      * @stable ICU 3.8
27      */
28     public static final int MAX_YEAR = Integer.MAX_VALUE;
29 
30     private final DateTimeRule dateTimeRule;
31     private final int startYear;
32     private final int endYear;
33 
34     /**
35      * Constructs a <code>AnnualTimeZoneRule</code> with the name, the GMT offset of its
36      * standard time, the amount of daylight saving offset adjustment,
37      * the annual start time rule and the start/until years.
38      *
39      * @param name          The time zone name.
40      * @param rawOffset     The GMT offset of its standard time in milliseconds.
41      * @param dstSavings    The amount of daylight saving offset adjustment in
42      *                      milliseconds.  If this ia a rule for standard time,
43      *                      the value of this argument is 0.
44      * @param dateTimeRule  The start date/time rule repeated annually.
45      * @param startYear     The first year when this rule takes effect.
46      * @param endYear       The last year when this rule takes effect.  If this
47      *                      rule is effective forever in future, specify MAX_YEAR.
48      *
49      * @stable ICU 3.8
50      */
AnnualTimeZoneRule(String name, int rawOffset, int dstSavings, DateTimeRule dateTimeRule, int startYear, int endYear)51     public AnnualTimeZoneRule(String name, int rawOffset, int dstSavings,
52             DateTimeRule dateTimeRule, int startYear, int endYear) {
53         super(name, rawOffset, dstSavings);
54         this.dateTimeRule = dateTimeRule;
55         this.startYear = startYear;
56         this.endYear = endYear > MAX_YEAR ? MAX_YEAR : endYear;
57     }
58 
59     /**
60      * Gets the start date/time rule associated used by this rule.
61      *
62      * @return  An <code>AnnualDateTimeRule</code> which represents the start date/time
63      *          rule used by this time zone rule.
64      *
65      * @stable ICU 3.8
66      */
getRule()67     public DateTimeRule getRule() {
68         return dateTimeRule;
69     }
70 
71     /**
72      * Gets the first year when this rule takes effect.
73      *
74      * @return  The start year of this rule.  The year is in Gregorian calendar
75      *          with 0 == 1 BCE, -1 == 2 BCE, etc.
76      *
77      * @stable ICU 3.8
78      */
getStartYear()79     public int getStartYear() {
80         return startYear;
81     }
82 
83     /**
84      * Gets the end year when this rule takes effect.
85      *
86      * @return  The end year of this rule (inclusive). The year is in Gregorian calendar
87      *          with 0 == 1 BCE, -1 == 2 BCE, etc.
88      *
89      * @stable ICU 3.8
90      */
getEndYear()91     public int getEndYear() {
92         return endYear;
93     }
94 
95     /**
96      * Gets the time when this rule takes effect in the given year.
97      *
98      * @param year              The Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc.
99      * @param prevRawOffset     The standard time offset from UTC before this rule
100      *                          takes effect in milliseconds.
101      * @param prevDSTSavings    The amount of daylight saving offset from the
102      *                          standard time.
103      *
104      * @return  The time when this rule takes effect in the year, or
105      *          null if this rule is not applicable in the year.
106      *
107      * @stable ICU 3.8
108      */
getStartInYear(int year, int prevRawOffset, int prevDSTSavings)109     public Date getStartInYear(int year, int prevRawOffset, int prevDSTSavings) {
110         if (year < startYear || year > endYear) {
111             return null;
112         }
113 
114         long ruleDay;
115         int type = dateTimeRule.getDateRuleType();
116 
117         if (type == DateTimeRule.DOM) {
118             ruleDay = Grego.fieldsToDay(year, dateTimeRule.getRuleMonth(), dateTimeRule.getRuleDayOfMonth());
119         } else {
120             boolean after = true;
121             if (type == DateTimeRule.DOW) {
122                 int weeks = dateTimeRule.getRuleWeekInMonth();
123                 if (weeks > 0) {
124                     ruleDay = Grego.fieldsToDay(year, dateTimeRule.getRuleMonth(), 1);
125                     ruleDay += 7 * (weeks - 1);
126                 } else {
127                     after = false;
128                     ruleDay = Grego.fieldsToDay(year, dateTimeRule.getRuleMonth(),
129                             Grego.monthLength(year, dateTimeRule.getRuleMonth()));
130                     ruleDay += 7 * (weeks + 1);
131                 }
132             } else {
133                 int month = dateTimeRule.getRuleMonth();
134                 int dom = dateTimeRule.getRuleDayOfMonth();
135                 if (type == DateTimeRule.DOW_LEQ_DOM) {
136                     after = false;
137                     // Handle Feb <=29
138                     if (month == Calendar.FEBRUARY && dom == 29 && !Grego.isLeapYear(year)) {
139                         dom--;
140                     }
141                 }
142                 ruleDay = Grego.fieldsToDay(year, month, dom);
143             }
144 
145             int dow = Grego.dayOfWeek(ruleDay);
146             int delta = dateTimeRule.getRuleDayOfWeek() - dow;
147             if (after) {
148                 delta = delta < 0 ? delta + 7 : delta;
149             } else {
150                 delta = delta > 0 ? delta - 7 : delta;
151             }
152             ruleDay += delta;
153         }
154 
155         long ruleTime = ruleDay * Grego.MILLIS_PER_DAY + dateTimeRule.getRuleMillisInDay();
156         if (dateTimeRule.getTimeRuleType() != DateTimeRule.UTC_TIME) {
157             ruleTime -= prevRawOffset;
158         }
159         if (dateTimeRule.getTimeRuleType() == DateTimeRule.WALL_TIME) {
160             ruleTime -= prevDSTSavings;
161         }
162         return new Date(ruleTime);
163     }
164 
165     /**
166      * {@inheritDoc}
167      * @stable ICU 3.8
168      */
getFirstStart(int prevRawOffset, int prevDSTSavings)169     public Date getFirstStart(int prevRawOffset, int prevDSTSavings) {
170         return getStartInYear(startYear, prevRawOffset, prevDSTSavings);
171     }
172 
173     /**
174      * {@inheritDoc}
175      * @stable ICU 3.8
176      */
getFinalStart(int prevRawOffset, int prevDSTSavings)177     public Date getFinalStart(int prevRawOffset, int prevDSTSavings) {
178         if (endYear == MAX_YEAR) {
179             return null;
180         }
181         return getStartInYear(endYear, prevRawOffset, prevDSTSavings);
182     }
183 
184     /**
185      * {@inheritDoc}
186      * @stable ICU 3.8
187      */
getNextStart(long base, int prevRawOffset, int prevDSTSavings, boolean inclusive)188     public Date getNextStart(long base, int prevRawOffset, int prevDSTSavings, boolean inclusive) {
189         int[] fields = Grego.timeToFields(base, null);
190         int year = fields[0];
191         if (year < startYear) {
192             return getFirstStart(prevRawOffset, prevDSTSavings);
193         }
194         Date d = getStartInYear(year, prevRawOffset, prevDSTSavings);
195         if (d != null && (d.getTime() < base || (!inclusive && (d.getTime() == base)))) {
196             d = getStartInYear(year + 1, prevRawOffset, prevDSTSavings);
197         }
198         return d;
199     }
200 
201     /**
202      * {@inheritDoc}
203      * @stable ICU 3.8
204      */
getPreviousStart(long base, int prevRawOffset, int prevDSTSavings, boolean inclusive)205     public Date getPreviousStart(long base, int prevRawOffset, int prevDSTSavings, boolean inclusive) {
206         int[] fields = Grego.timeToFields(base, null);
207         int year = fields[0];
208         if (year > endYear) {
209             return getFinalStart(prevRawOffset, prevDSTSavings);
210         }
211         Date d = getStartInYear(year, prevRawOffset, prevDSTSavings);
212         if (d != null && (d.getTime() > base || (!inclusive && (d.getTime() == base)))) {
213             d = getStartInYear(year - 1, prevRawOffset, prevDSTSavings);
214         }
215         return d;
216     }
217 
218     /**
219      * {@inheritDoc}
220      * @stable ICU 3.8
221      */
isEquivalentTo(TimeZoneRule other)222     public boolean isEquivalentTo(TimeZoneRule other) {
223         if (!(other instanceof AnnualTimeZoneRule)) {
224             return false;
225         }
226         AnnualTimeZoneRule otherRule = (AnnualTimeZoneRule)other;
227         if (startYear == otherRule.startYear
228                 && endYear == otherRule.endYear
229                 && dateTimeRule.equals(otherRule.dateTimeRule)) {
230             return super.isEquivalentTo(other);
231         }
232         return false;
233     }
234 
235     /**
236      * {@inheritDoc}<br><br>
237      * Note: This method in <code>AnnualTimeZoneRule</code> always returns true.
238      * @stable ICU 3.8
239      */
isTransitionRule()240     public boolean isTransitionRule() {
241         return true;
242     }
243 
244     /**
245      * Returns a <code>String</code> representation of this <code>AnnualTimeZoneRule</code> object.
246      * This method is used for debugging purpose only.  The string representation can be changed
247      * in future version of ICU without any notice.
248      *
249      * @stable ICU 3.8
250      */
toString()251     public String toString() {
252         StringBuilder buf = new StringBuilder();
253         buf.append(super.toString());
254         buf.append(", rule={" + dateTimeRule + "}");
255         buf.append(", startYear=" + startYear);
256         buf.append(", endYear=");
257         if (endYear == MAX_YEAR) {
258             buf.append("max");
259         } else {
260             buf.append(endYear);
261         }
262         return buf.toString();
263     }
264 }
265