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