Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | /* |
michael@0 | 2 | ******************************************************************************* |
michael@0 | 3 | * Copyright (C) 1997-2013, International Business Machines Corporation and |
michael@0 | 4 | * others. All Rights Reserved. |
michael@0 | 5 | ******************************************************************************* |
michael@0 | 6 | * |
michael@0 | 7 | * File SIMPLETZ.H |
michael@0 | 8 | * |
michael@0 | 9 | * Modification History: |
michael@0 | 10 | * |
michael@0 | 11 | * Date Name Description |
michael@0 | 12 | * 12/05/96 clhuang Creation. |
michael@0 | 13 | * 04/21/97 aliu Fixed miscellaneous bugs found by inspection and |
michael@0 | 14 | * testing. |
michael@0 | 15 | * 07/29/97 aliu Ported source bodies back from Java version with |
michael@0 | 16 | * numerous feature enhancements and bug fixes. |
michael@0 | 17 | * 08/10/98 stephen JDK 1.2 sync. |
michael@0 | 18 | * 09/17/98 stephen Fixed getOffset() for last hour of year and DST |
michael@0 | 19 | * 12/02/99 aliu Added TimeMode and constructor and setStart/EndRule |
michael@0 | 20 | * methods that take TimeMode. Whitespace cleanup. |
michael@0 | 21 | ******************************************************************************** |
michael@0 | 22 | */ |
michael@0 | 23 | |
michael@0 | 24 | #include "utypeinfo.h" // for 'typeid' to work |
michael@0 | 25 | |
michael@0 | 26 | #include "unicode/utypes.h" |
michael@0 | 27 | |
michael@0 | 28 | #if !UCONFIG_NO_FORMATTING |
michael@0 | 29 | |
michael@0 | 30 | #include "unicode/simpletz.h" |
michael@0 | 31 | #include "unicode/gregocal.h" |
michael@0 | 32 | #include "unicode/smpdtfmt.h" |
michael@0 | 33 | |
michael@0 | 34 | #include "gregoimp.h" |
michael@0 | 35 | #include "umutex.h" |
michael@0 | 36 | |
michael@0 | 37 | U_NAMESPACE_BEGIN |
michael@0 | 38 | |
michael@0 | 39 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleTimeZone) |
michael@0 | 40 | |
michael@0 | 41 | // Use only for decodeStartRule() and decodeEndRule() where the year is not |
michael@0 | 42 | // available. Set February to 29 days to accomodate rules with that date |
michael@0 | 43 | // and day-of-week-on-or-before-that-date mode (DOW_LE_DOM_MODE). |
michael@0 | 44 | // The compareToRule() method adjusts to February 28 in non-leap years. |
michael@0 | 45 | // |
michael@0 | 46 | // For actual getOffset() calculations, use Grego::monthLength() and |
michael@0 | 47 | // Grego::previousMonthLength() which take leap years into account. |
michael@0 | 48 | // We handle leap years assuming always |
michael@0 | 49 | // Gregorian, since we know they didn't have daylight time when |
michael@0 | 50 | // Gregorian calendar started. |
michael@0 | 51 | const int8_t SimpleTimeZone::STATICMONTHLENGTH[] = {31,29,31,30,31,30,31,31,30,31,30,31}; |
michael@0 | 52 | |
michael@0 | 53 | static const UChar DST_STR[] = {0x0028,0x0044,0x0053,0x0054,0x0029,0}; // "(DST)" |
michael@0 | 54 | static const UChar STD_STR[] = {0x0028,0x0053,0x0054,0x0044,0x0029,0}; // "(STD)" |
michael@0 | 55 | |
michael@0 | 56 | |
michael@0 | 57 | // ***************************************************************************** |
michael@0 | 58 | // class SimpleTimeZone |
michael@0 | 59 | // ***************************************************************************** |
michael@0 | 60 | |
michael@0 | 61 | |
michael@0 | 62 | SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID) |
michael@0 | 63 | : BasicTimeZone(ID), |
michael@0 | 64 | startMonth(0), |
michael@0 | 65 | startDay(0), |
michael@0 | 66 | startDayOfWeek(0), |
michael@0 | 67 | startTime(0), |
michael@0 | 68 | startTimeMode(WALL_TIME), |
michael@0 | 69 | endTimeMode(WALL_TIME), |
michael@0 | 70 | endMonth(0), |
michael@0 | 71 | endDay(0), |
michael@0 | 72 | endDayOfWeek(0), |
michael@0 | 73 | endTime(0), |
michael@0 | 74 | startYear(0), |
michael@0 | 75 | rawOffset(rawOffsetGMT), |
michael@0 | 76 | useDaylight(FALSE), |
michael@0 | 77 | startMode(DOM_MODE), |
michael@0 | 78 | endMode(DOM_MODE), |
michael@0 | 79 | dstSavings(U_MILLIS_PER_HOUR) |
michael@0 | 80 | { |
michael@0 | 81 | clearTransitionRules(); |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | // ------------------------------------- |
michael@0 | 85 | |
michael@0 | 86 | SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID, |
michael@0 | 87 | int8_t savingsStartMonth, int8_t savingsStartDay, |
michael@0 | 88 | int8_t savingsStartDayOfWeek, int32_t savingsStartTime, |
michael@0 | 89 | int8_t savingsEndMonth, int8_t savingsEndDay, |
michael@0 | 90 | int8_t savingsEndDayOfWeek, int32_t savingsEndTime, |
michael@0 | 91 | UErrorCode& status) |
michael@0 | 92 | : BasicTimeZone(ID) |
michael@0 | 93 | { |
michael@0 | 94 | clearTransitionRules(); |
michael@0 | 95 | construct(rawOffsetGMT, |
michael@0 | 96 | savingsStartMonth, savingsStartDay, savingsStartDayOfWeek, |
michael@0 | 97 | savingsStartTime, WALL_TIME, |
michael@0 | 98 | savingsEndMonth, savingsEndDay, savingsEndDayOfWeek, |
michael@0 | 99 | savingsEndTime, WALL_TIME, |
michael@0 | 100 | U_MILLIS_PER_HOUR, status); |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | // ------------------------------------- |
michael@0 | 104 | |
michael@0 | 105 | SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID, |
michael@0 | 106 | int8_t savingsStartMonth, int8_t savingsStartDay, |
michael@0 | 107 | int8_t savingsStartDayOfWeek, int32_t savingsStartTime, |
michael@0 | 108 | int8_t savingsEndMonth, int8_t savingsEndDay, |
michael@0 | 109 | int8_t savingsEndDayOfWeek, int32_t savingsEndTime, |
michael@0 | 110 | int32_t savingsDST, UErrorCode& status) |
michael@0 | 111 | : BasicTimeZone(ID) |
michael@0 | 112 | { |
michael@0 | 113 | clearTransitionRules(); |
michael@0 | 114 | construct(rawOffsetGMT, |
michael@0 | 115 | savingsStartMonth, savingsStartDay, savingsStartDayOfWeek, |
michael@0 | 116 | savingsStartTime, WALL_TIME, |
michael@0 | 117 | savingsEndMonth, savingsEndDay, savingsEndDayOfWeek, |
michael@0 | 118 | savingsEndTime, WALL_TIME, |
michael@0 | 119 | savingsDST, status); |
michael@0 | 120 | } |
michael@0 | 121 | |
michael@0 | 122 | // ------------------------------------- |
michael@0 | 123 | |
michael@0 | 124 | SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID, |
michael@0 | 125 | int8_t savingsStartMonth, int8_t savingsStartDay, |
michael@0 | 126 | int8_t savingsStartDayOfWeek, int32_t savingsStartTime, |
michael@0 | 127 | TimeMode savingsStartTimeMode, |
michael@0 | 128 | int8_t savingsEndMonth, int8_t savingsEndDay, |
michael@0 | 129 | int8_t savingsEndDayOfWeek, int32_t savingsEndTime, |
michael@0 | 130 | TimeMode savingsEndTimeMode, |
michael@0 | 131 | int32_t savingsDST, UErrorCode& status) |
michael@0 | 132 | : BasicTimeZone(ID) |
michael@0 | 133 | { |
michael@0 | 134 | clearTransitionRules(); |
michael@0 | 135 | construct(rawOffsetGMT, |
michael@0 | 136 | savingsStartMonth, savingsStartDay, savingsStartDayOfWeek, |
michael@0 | 137 | savingsStartTime, savingsStartTimeMode, |
michael@0 | 138 | savingsEndMonth, savingsEndDay, savingsEndDayOfWeek, |
michael@0 | 139 | savingsEndTime, savingsEndTimeMode, |
michael@0 | 140 | savingsDST, status); |
michael@0 | 141 | } |
michael@0 | 142 | |
michael@0 | 143 | /** |
michael@0 | 144 | * Internal construction method. |
michael@0 | 145 | */ |
michael@0 | 146 | void SimpleTimeZone::construct(int32_t rawOffsetGMT, |
michael@0 | 147 | int8_t savingsStartMonth, |
michael@0 | 148 | int8_t savingsStartDay, |
michael@0 | 149 | int8_t savingsStartDayOfWeek, |
michael@0 | 150 | int32_t savingsStartTime, |
michael@0 | 151 | TimeMode savingsStartTimeMode, |
michael@0 | 152 | int8_t savingsEndMonth, |
michael@0 | 153 | int8_t savingsEndDay, |
michael@0 | 154 | int8_t savingsEndDayOfWeek, |
michael@0 | 155 | int32_t savingsEndTime, |
michael@0 | 156 | TimeMode savingsEndTimeMode, |
michael@0 | 157 | int32_t savingsDST, |
michael@0 | 158 | UErrorCode& status) |
michael@0 | 159 | { |
michael@0 | 160 | this->rawOffset = rawOffsetGMT; |
michael@0 | 161 | this->startMonth = savingsStartMonth; |
michael@0 | 162 | this->startDay = savingsStartDay; |
michael@0 | 163 | this->startDayOfWeek = savingsStartDayOfWeek; |
michael@0 | 164 | this->startTime = savingsStartTime; |
michael@0 | 165 | this->startTimeMode = savingsStartTimeMode; |
michael@0 | 166 | this->endMonth = savingsEndMonth; |
michael@0 | 167 | this->endDay = savingsEndDay; |
michael@0 | 168 | this->endDayOfWeek = savingsEndDayOfWeek; |
michael@0 | 169 | this->endTime = savingsEndTime; |
michael@0 | 170 | this->endTimeMode = savingsEndTimeMode; |
michael@0 | 171 | this->dstSavings = savingsDST; |
michael@0 | 172 | this->startYear = 0; |
michael@0 | 173 | this->startMode = DOM_MODE; |
michael@0 | 174 | this->endMode = DOM_MODE; |
michael@0 | 175 | |
michael@0 | 176 | decodeRules(status); |
michael@0 | 177 | |
michael@0 | 178 | if (savingsDST <= 0) { |
michael@0 | 179 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 180 | } |
michael@0 | 181 | } |
michael@0 | 182 | |
michael@0 | 183 | // ------------------------------------- |
michael@0 | 184 | |
michael@0 | 185 | SimpleTimeZone::~SimpleTimeZone() |
michael@0 | 186 | { |
michael@0 | 187 | deleteTransitionRules(); |
michael@0 | 188 | } |
michael@0 | 189 | |
michael@0 | 190 | // ------------------------------------- |
michael@0 | 191 | |
michael@0 | 192 | // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful. |
michael@0 | 193 | SimpleTimeZone::SimpleTimeZone(const SimpleTimeZone &source) |
michael@0 | 194 | : BasicTimeZone(source) |
michael@0 | 195 | { |
michael@0 | 196 | *this = source; |
michael@0 | 197 | } |
michael@0 | 198 | |
michael@0 | 199 | // ------------------------------------- |
michael@0 | 200 | |
michael@0 | 201 | // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful. |
michael@0 | 202 | SimpleTimeZone & |
michael@0 | 203 | SimpleTimeZone::operator=(const SimpleTimeZone &right) |
michael@0 | 204 | { |
michael@0 | 205 | if (this != &right) |
michael@0 | 206 | { |
michael@0 | 207 | TimeZone::operator=(right); |
michael@0 | 208 | rawOffset = right.rawOffset; |
michael@0 | 209 | startMonth = right.startMonth; |
michael@0 | 210 | startDay = right.startDay; |
michael@0 | 211 | startDayOfWeek = right.startDayOfWeek; |
michael@0 | 212 | startTime = right.startTime; |
michael@0 | 213 | startTimeMode = right.startTimeMode; |
michael@0 | 214 | startMode = right.startMode; |
michael@0 | 215 | endMonth = right.endMonth; |
michael@0 | 216 | endDay = right.endDay; |
michael@0 | 217 | endDayOfWeek = right.endDayOfWeek; |
michael@0 | 218 | endTime = right.endTime; |
michael@0 | 219 | endTimeMode = right.endTimeMode; |
michael@0 | 220 | endMode = right.endMode; |
michael@0 | 221 | startYear = right.startYear; |
michael@0 | 222 | dstSavings = right.dstSavings; |
michael@0 | 223 | useDaylight = right.useDaylight; |
michael@0 | 224 | clearTransitionRules(); |
michael@0 | 225 | } |
michael@0 | 226 | return *this; |
michael@0 | 227 | } |
michael@0 | 228 | |
michael@0 | 229 | // ------------------------------------- |
michael@0 | 230 | |
michael@0 | 231 | UBool |
michael@0 | 232 | SimpleTimeZone::operator==(const TimeZone& that) const |
michael@0 | 233 | { |
michael@0 | 234 | return ((this == &that) || |
michael@0 | 235 | (typeid(*this) == typeid(that) && |
michael@0 | 236 | TimeZone::operator==(that) && |
michael@0 | 237 | hasSameRules(that))); |
michael@0 | 238 | } |
michael@0 | 239 | |
michael@0 | 240 | // ------------------------------------- |
michael@0 | 241 | |
michael@0 | 242 | // Called by TimeZone::createDefault() inside a Mutex - be careful. |
michael@0 | 243 | TimeZone* |
michael@0 | 244 | SimpleTimeZone::clone() const |
michael@0 | 245 | { |
michael@0 | 246 | return new SimpleTimeZone(*this); |
michael@0 | 247 | } |
michael@0 | 248 | |
michael@0 | 249 | // ------------------------------------- |
michael@0 | 250 | |
michael@0 | 251 | /** |
michael@0 | 252 | * Sets the daylight savings starting year, that is, the year this time zone began |
michael@0 | 253 | * observing its specified daylight savings time rules. The time zone is considered |
michael@0 | 254 | * not to observe daylight savings time prior to that year; SimpleTimeZone doesn't |
michael@0 | 255 | * support historical daylight-savings-time rules. |
michael@0 | 256 | * @param year the daylight savings starting year. |
michael@0 | 257 | */ |
michael@0 | 258 | void |
michael@0 | 259 | SimpleTimeZone::setStartYear(int32_t year) |
michael@0 | 260 | { |
michael@0 | 261 | startYear = year; |
michael@0 | 262 | transitionRulesInitialized = FALSE; |
michael@0 | 263 | } |
michael@0 | 264 | |
michael@0 | 265 | // ------------------------------------- |
michael@0 | 266 | |
michael@0 | 267 | /** |
michael@0 | 268 | * Sets the daylight savings starting rule. For example, in the U.S., Daylight Savings |
michael@0 | 269 | * Time starts at the first Sunday in April, at 2 AM in standard time. |
michael@0 | 270 | * Therefore, you can set the start rule by calling: |
michael@0 | 271 | * setStartRule(TimeFields.APRIL, 1, TimeFields.SUNDAY, 2*60*60*1000); |
michael@0 | 272 | * The dayOfWeekInMonth and dayOfWeek parameters together specify how to calculate |
michael@0 | 273 | * the exact starting date. Their exact meaning depend on their respective signs, |
michael@0 | 274 | * allowing various types of rules to be constructed, as follows:<ul> |
michael@0 | 275 | * <li>If both dayOfWeekInMonth and dayOfWeek are positive, they specify the |
michael@0 | 276 | * day of week in the month (e.g., (2, WEDNESDAY) is the second Wednesday |
michael@0 | 277 | * of the month). |
michael@0 | 278 | * <li>If dayOfWeek is positive and dayOfWeekInMonth is negative, they specify |
michael@0 | 279 | * the day of week in the month counting backward from the end of the month. |
michael@0 | 280 | * (e.g., (-1, MONDAY) is the last Monday in the month) |
michael@0 | 281 | * <li>If dayOfWeek is zero and dayOfWeekInMonth is positive, dayOfWeekInMonth |
michael@0 | 282 | * specifies the day of the month, regardless of what day of the week it is. |
michael@0 | 283 | * (e.g., (10, 0) is the tenth day of the month) |
michael@0 | 284 | * <li>If dayOfWeek is zero and dayOfWeekInMonth is negative, dayOfWeekInMonth |
michael@0 | 285 | * specifies the day of the month counting backward from the end of the |
michael@0 | 286 | * month, regardless of what day of the week it is (e.g., (-2, 0) is the |
michael@0 | 287 | * next-to-last day of the month). |
michael@0 | 288 | * <li>If dayOfWeek is negative and dayOfWeekInMonth is positive, they specify the |
michael@0 | 289 | * first specified day of the week on or after the specfied day of the month. |
michael@0 | 290 | * (e.g., (15, -SUNDAY) is the first Sunday after the 15th of the month |
michael@0 | 291 | * [or the 15th itself if the 15th is a Sunday].) |
michael@0 | 292 | * <li>If dayOfWeek and DayOfWeekInMonth are both negative, they specify the |
michael@0 | 293 | * last specified day of the week on or before the specified day of the month. |
michael@0 | 294 | * (e.g., (-20, -TUESDAY) is the last Tuesday before the 20th of the month |
michael@0 | 295 | * [or the 20th itself if the 20th is a Tuesday].)</ul> |
michael@0 | 296 | * @param month the daylight savings starting month. Month is 0-based. |
michael@0 | 297 | * eg, 0 for January. |
michael@0 | 298 | * @param dayOfWeekInMonth the daylight savings starting |
michael@0 | 299 | * day-of-week-in-month. Please see the member description for an example. |
michael@0 | 300 | * @param dayOfWeek the daylight savings starting day-of-week. Please see |
michael@0 | 301 | * the member description for an example. |
michael@0 | 302 | * @param time the daylight savings starting time. Please see the member |
michael@0 | 303 | * description for an example. |
michael@0 | 304 | */ |
michael@0 | 305 | |
michael@0 | 306 | void |
michael@0 | 307 | SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek, |
michael@0 | 308 | int32_t time, TimeMode mode, UErrorCode& status) |
michael@0 | 309 | { |
michael@0 | 310 | startMonth = (int8_t)month; |
michael@0 | 311 | startDay = (int8_t)dayOfWeekInMonth; |
michael@0 | 312 | startDayOfWeek = (int8_t)dayOfWeek; |
michael@0 | 313 | startTime = time; |
michael@0 | 314 | startTimeMode = mode; |
michael@0 | 315 | decodeStartRule(status); |
michael@0 | 316 | transitionRulesInitialized = FALSE; |
michael@0 | 317 | } |
michael@0 | 318 | |
michael@0 | 319 | // ------------------------------------- |
michael@0 | 320 | |
michael@0 | 321 | void |
michael@0 | 322 | SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth, |
michael@0 | 323 | int32_t time, TimeMode mode, UErrorCode& status) |
michael@0 | 324 | { |
michael@0 | 325 | setStartRule(month, dayOfMonth, 0, time, mode, status); |
michael@0 | 326 | } |
michael@0 | 327 | |
michael@0 | 328 | // ------------------------------------- |
michael@0 | 329 | |
michael@0 | 330 | void |
michael@0 | 331 | SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek, |
michael@0 | 332 | int32_t time, TimeMode mode, UBool after, UErrorCode& status) |
michael@0 | 333 | { |
michael@0 | 334 | setStartRule(month, after ? dayOfMonth : -dayOfMonth, |
michael@0 | 335 | -dayOfWeek, time, mode, status); |
michael@0 | 336 | } |
michael@0 | 337 | |
michael@0 | 338 | // ------------------------------------- |
michael@0 | 339 | |
michael@0 | 340 | /** |
michael@0 | 341 | * Sets the daylight savings ending rule. For example, in the U.S., Daylight |
michael@0 | 342 | * Savings Time ends at the last (-1) Sunday in October, at 2 AM in standard time. |
michael@0 | 343 | * Therefore, you can set the end rule by calling: |
michael@0 | 344 | * setEndRule(TimeFields.OCTOBER, -1, TimeFields.SUNDAY, 2*60*60*1000); |
michael@0 | 345 | * Various other types of rules can be specified by manipulating the dayOfWeek |
michael@0 | 346 | * and dayOfWeekInMonth parameters. For complete details, see the documentation |
michael@0 | 347 | * for setStartRule(). |
michael@0 | 348 | * @param month the daylight savings ending month. Month is 0-based. |
michael@0 | 349 | * eg, 0 for January. |
michael@0 | 350 | * @param dayOfWeekInMonth the daylight savings ending |
michael@0 | 351 | * day-of-week-in-month. See setStartRule() for a complete explanation. |
michael@0 | 352 | * @param dayOfWeek the daylight savings ending day-of-week. See setStartRule() |
michael@0 | 353 | * for a complete explanation. |
michael@0 | 354 | * @param time the daylight savings ending time. Please see the member |
michael@0 | 355 | * description for an example. |
michael@0 | 356 | */ |
michael@0 | 357 | |
michael@0 | 358 | void |
michael@0 | 359 | SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek, |
michael@0 | 360 | int32_t time, TimeMode mode, UErrorCode& status) |
michael@0 | 361 | { |
michael@0 | 362 | endMonth = (int8_t)month; |
michael@0 | 363 | endDay = (int8_t)dayOfWeekInMonth; |
michael@0 | 364 | endDayOfWeek = (int8_t)dayOfWeek; |
michael@0 | 365 | endTime = time; |
michael@0 | 366 | endTimeMode = mode; |
michael@0 | 367 | decodeEndRule(status); |
michael@0 | 368 | transitionRulesInitialized = FALSE; |
michael@0 | 369 | } |
michael@0 | 370 | |
michael@0 | 371 | // ------------------------------------- |
michael@0 | 372 | |
michael@0 | 373 | void |
michael@0 | 374 | SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth, |
michael@0 | 375 | int32_t time, TimeMode mode, UErrorCode& status) |
michael@0 | 376 | { |
michael@0 | 377 | setEndRule(month, dayOfMonth, 0, time, mode, status); |
michael@0 | 378 | } |
michael@0 | 379 | |
michael@0 | 380 | // ------------------------------------- |
michael@0 | 381 | |
michael@0 | 382 | void |
michael@0 | 383 | SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek, |
michael@0 | 384 | int32_t time, TimeMode mode, UBool after, UErrorCode& status) |
michael@0 | 385 | { |
michael@0 | 386 | setEndRule(month, after ? dayOfMonth : -dayOfMonth, |
michael@0 | 387 | -dayOfWeek, time, mode, status); |
michael@0 | 388 | } |
michael@0 | 389 | |
michael@0 | 390 | // ------------------------------------- |
michael@0 | 391 | |
michael@0 | 392 | int32_t |
michael@0 | 393 | SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, |
michael@0 | 394 | uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const |
michael@0 | 395 | { |
michael@0 | 396 | // Check the month before calling Grego::monthLength(). This |
michael@0 | 397 | // duplicates the test that occurs in the 7-argument getOffset(), |
michael@0 | 398 | // however, this is unavoidable. We don't mind because this method, in |
michael@0 | 399 | // fact, should not be called; internal code should always call the |
michael@0 | 400 | // 7-argument getOffset(), and outside code should use Calendar.get(int |
michael@0 | 401 | // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of |
michael@0 | 402 | // this method because it's public API. - liu 8/10/98 |
michael@0 | 403 | if(month < UCAL_JANUARY || month > UCAL_DECEMBER) { |
michael@0 | 404 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 405 | return 0; |
michael@0 | 406 | } |
michael@0 | 407 | |
michael@0 | 408 | return getOffset(era, year, month, day, dayOfWeek, millis, Grego::monthLength(year, month), status); |
michael@0 | 409 | } |
michael@0 | 410 | |
michael@0 | 411 | int32_t |
michael@0 | 412 | SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, |
michael@0 | 413 | uint8_t dayOfWeek, int32_t millis, |
michael@0 | 414 | int32_t /*monthLength*/, UErrorCode& status) const |
michael@0 | 415 | { |
michael@0 | 416 | // Check the month before calling Grego::monthLength(). This |
michael@0 | 417 | // duplicates a test that occurs in the 9-argument getOffset(), |
michael@0 | 418 | // however, this is unavoidable. We don't mind because this method, in |
michael@0 | 419 | // fact, should not be called; internal code should always call the |
michael@0 | 420 | // 9-argument getOffset(), and outside code should use Calendar.get(int |
michael@0 | 421 | // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of |
michael@0 | 422 | // this method because it's public API. - liu 8/10/98 |
michael@0 | 423 | if (month < UCAL_JANUARY |
michael@0 | 424 | || month > UCAL_DECEMBER) { |
michael@0 | 425 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 426 | return -1; |
michael@0 | 427 | } |
michael@0 | 428 | |
michael@0 | 429 | // We ignore monthLength because it can be derived from year and month. |
michael@0 | 430 | // This is so that February in leap years is calculated correctly. |
michael@0 | 431 | // We keep this argument in this function for backwards compatibility. |
michael@0 | 432 | return getOffset(era, year, month, day, dayOfWeek, millis, |
michael@0 | 433 | Grego::monthLength(year, month), |
michael@0 | 434 | Grego::previousMonthLength(year, month), |
michael@0 | 435 | status); |
michael@0 | 436 | } |
michael@0 | 437 | |
michael@0 | 438 | int32_t |
michael@0 | 439 | SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, |
michael@0 | 440 | uint8_t dayOfWeek, int32_t millis, |
michael@0 | 441 | int32_t monthLength, int32_t prevMonthLength, |
michael@0 | 442 | UErrorCode& status) const |
michael@0 | 443 | { |
michael@0 | 444 | if(U_FAILURE(status)) return 0; |
michael@0 | 445 | |
michael@0 | 446 | if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC) |
michael@0 | 447 | || month < UCAL_JANUARY |
michael@0 | 448 | || month > UCAL_DECEMBER |
michael@0 | 449 | || day < 1 |
michael@0 | 450 | || day > monthLength |
michael@0 | 451 | || dayOfWeek < UCAL_SUNDAY |
michael@0 | 452 | || dayOfWeek > UCAL_SATURDAY |
michael@0 | 453 | || millis < 0 |
michael@0 | 454 | || millis >= U_MILLIS_PER_DAY |
michael@0 | 455 | || monthLength < 28 |
michael@0 | 456 | || monthLength > 31 |
michael@0 | 457 | || prevMonthLength < 28 |
michael@0 | 458 | || prevMonthLength > 31) { |
michael@0 | 459 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 460 | return -1; |
michael@0 | 461 | } |
michael@0 | 462 | |
michael@0 | 463 | int32_t result = rawOffset; |
michael@0 | 464 | |
michael@0 | 465 | // Bail out if we are before the onset of daylight savings time |
michael@0 | 466 | if(!useDaylight || year < startYear || era != GregorianCalendar::AD) |
michael@0 | 467 | return result; |
michael@0 | 468 | |
michael@0 | 469 | // Check for southern hemisphere. We assume that the start and end |
michael@0 | 470 | // month are different. |
michael@0 | 471 | UBool southern = (startMonth > endMonth); |
michael@0 | 472 | |
michael@0 | 473 | // Compare the date to the starting and ending rules.+1 = date>rule, -1 |
michael@0 | 474 | // = date<rule, 0 = date==rule. |
michael@0 | 475 | int32_t startCompare = compareToRule((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength, |
michael@0 | 476 | (int8_t)day, (int8_t)dayOfWeek, millis, |
michael@0 | 477 | startTimeMode == UTC_TIME ? -rawOffset : 0, |
michael@0 | 478 | startMode, (int8_t)startMonth, (int8_t)startDayOfWeek, |
michael@0 | 479 | (int8_t)startDay, startTime); |
michael@0 | 480 | int32_t endCompare = 0; |
michael@0 | 481 | |
michael@0 | 482 | /* We don't always have to compute endCompare. For many instances, |
michael@0 | 483 | * startCompare is enough to determine if we are in DST or not. In the |
michael@0 | 484 | * northern hemisphere, if we are before the start rule, we can't have |
michael@0 | 485 | * DST. In the southern hemisphere, if we are after the start rule, we |
michael@0 | 486 | * must have DST. This is reflected in the way the next if statement |
michael@0 | 487 | * (not the one immediately following) short circuits. */ |
michael@0 | 488 | if(southern != (startCompare >= 0)) { |
michael@0 | 489 | endCompare = compareToRule((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength, |
michael@0 | 490 | (int8_t)day, (int8_t)dayOfWeek, millis, |
michael@0 | 491 | endTimeMode == WALL_TIME ? dstSavings : |
michael@0 | 492 | (endTimeMode == UTC_TIME ? -rawOffset : 0), |
michael@0 | 493 | endMode, (int8_t)endMonth, (int8_t)endDayOfWeek, |
michael@0 | 494 | (int8_t)endDay, endTime); |
michael@0 | 495 | } |
michael@0 | 496 | |
michael@0 | 497 | // Check for both the northern and southern hemisphere cases. We |
michael@0 | 498 | // assume that in the northern hemisphere, the start rule is before the |
michael@0 | 499 | // end rule within the calendar year, and vice versa for the southern |
michael@0 | 500 | // hemisphere. |
michael@0 | 501 | if ((!southern && (startCompare >= 0 && endCompare < 0)) || |
michael@0 | 502 | (southern && (startCompare >= 0 || endCompare < 0))) |
michael@0 | 503 | result += dstSavings; |
michael@0 | 504 | |
michael@0 | 505 | return result; |
michael@0 | 506 | } |
michael@0 | 507 | |
michael@0 | 508 | void |
michael@0 | 509 | SimpleTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt, |
michael@0 | 510 | int32_t& rawOffsetGMT, int32_t& savingsDST, UErrorCode& status) const { |
michael@0 | 511 | if (U_FAILURE(status)) { |
michael@0 | 512 | return; |
michael@0 | 513 | } |
michael@0 | 514 | |
michael@0 | 515 | rawOffsetGMT = getRawOffset(); |
michael@0 | 516 | int32_t year, month, dom, dow; |
michael@0 | 517 | double day = uprv_floor(date / U_MILLIS_PER_DAY); |
michael@0 | 518 | int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY); |
michael@0 | 519 | |
michael@0 | 520 | Grego::dayToFields(day, year, month, dom, dow); |
michael@0 | 521 | |
michael@0 | 522 | savingsDST = getOffset(GregorianCalendar::AD, year, month, dom, |
michael@0 | 523 | (uint8_t) dow, millis, |
michael@0 | 524 | Grego::monthLength(year, month), |
michael@0 | 525 | status) - rawOffsetGMT; |
michael@0 | 526 | if (U_FAILURE(status)) { |
michael@0 | 527 | return; |
michael@0 | 528 | } |
michael@0 | 529 | |
michael@0 | 530 | UBool recalc = FALSE; |
michael@0 | 531 | |
michael@0 | 532 | // Now we need some adjustment |
michael@0 | 533 | if (savingsDST > 0) { |
michael@0 | 534 | if ((nonExistingTimeOpt & kStdDstMask) == kStandard |
michael@0 | 535 | || ((nonExistingTimeOpt & kStdDstMask) != kDaylight && (nonExistingTimeOpt & kFormerLatterMask) != kLatter)) { |
michael@0 | 536 | date -= getDSTSavings(); |
michael@0 | 537 | recalc = TRUE; |
michael@0 | 538 | } |
michael@0 | 539 | } else { |
michael@0 | 540 | if ((duplicatedTimeOpt & kStdDstMask) == kDaylight |
michael@0 | 541 | || ((duplicatedTimeOpt & kStdDstMask) != kStandard && (duplicatedTimeOpt & kFormerLatterMask) == kFormer)) { |
michael@0 | 542 | date -= getDSTSavings(); |
michael@0 | 543 | recalc = TRUE; |
michael@0 | 544 | } |
michael@0 | 545 | } |
michael@0 | 546 | if (recalc) { |
michael@0 | 547 | day = uprv_floor(date / U_MILLIS_PER_DAY); |
michael@0 | 548 | millis = (int32_t) (date - day * U_MILLIS_PER_DAY); |
michael@0 | 549 | Grego::dayToFields(day, year, month, dom, dow); |
michael@0 | 550 | savingsDST = getOffset(GregorianCalendar::AD, year, month, dom, |
michael@0 | 551 | (uint8_t) dow, millis, |
michael@0 | 552 | Grego::monthLength(year, month), |
michael@0 | 553 | status) - rawOffsetGMT; |
michael@0 | 554 | } |
michael@0 | 555 | } |
michael@0 | 556 | |
michael@0 | 557 | // ------------------------------------- |
michael@0 | 558 | |
michael@0 | 559 | /** |
michael@0 | 560 | * Compare a given date in the year to a rule. Return 1, 0, or -1, depending |
michael@0 | 561 | * on whether the date is after, equal to, or before the rule date. The |
michael@0 | 562 | * millis are compared directly against the ruleMillis, so any |
michael@0 | 563 | * standard-daylight adjustments must be handled by the caller. |
michael@0 | 564 | * |
michael@0 | 565 | * @return 1 if the date is after the rule date, -1 if the date is before |
michael@0 | 566 | * the rule date, or 0 if the date is equal to the rule date. |
michael@0 | 567 | */ |
michael@0 | 568 | int32_t |
michael@0 | 569 | SimpleTimeZone::compareToRule(int8_t month, int8_t monthLen, int8_t prevMonthLen, |
michael@0 | 570 | int8_t dayOfMonth, |
michael@0 | 571 | int8_t dayOfWeek, int32_t millis, int32_t millisDelta, |
michael@0 | 572 | EMode ruleMode, int8_t ruleMonth, int8_t ruleDayOfWeek, |
michael@0 | 573 | int8_t ruleDay, int32_t ruleMillis) |
michael@0 | 574 | { |
michael@0 | 575 | // Make adjustments for startTimeMode and endTimeMode |
michael@0 | 576 | millis += millisDelta; |
michael@0 | 577 | while (millis >= U_MILLIS_PER_DAY) { |
michael@0 | 578 | millis -= U_MILLIS_PER_DAY; |
michael@0 | 579 | ++dayOfMonth; |
michael@0 | 580 | dayOfWeek = (int8_t)(1 + (dayOfWeek % 7)); // dayOfWeek is one-based |
michael@0 | 581 | if (dayOfMonth > monthLen) { |
michael@0 | 582 | dayOfMonth = 1; |
michael@0 | 583 | /* When incrementing the month, it is desirible to overflow |
michael@0 | 584 | * from DECEMBER to DECEMBER+1, since we use the result to |
michael@0 | 585 | * compare against a real month. Wraparound of the value |
michael@0 | 586 | * leads to bug 4173604. */ |
michael@0 | 587 | ++month; |
michael@0 | 588 | } |
michael@0 | 589 | } |
michael@0 | 590 | while (millis < 0) { |
michael@0 | 591 | millis += U_MILLIS_PER_DAY; |
michael@0 | 592 | --dayOfMonth; |
michael@0 | 593 | dayOfWeek = (int8_t)(1 + ((dayOfWeek+5) % 7)); // dayOfWeek is one-based |
michael@0 | 594 | if (dayOfMonth < 1) { |
michael@0 | 595 | dayOfMonth = prevMonthLen; |
michael@0 | 596 | --month; |
michael@0 | 597 | } |
michael@0 | 598 | } |
michael@0 | 599 | |
michael@0 | 600 | // first compare months. If they're different, we don't have to worry about days |
michael@0 | 601 | // and times |
michael@0 | 602 | if (month < ruleMonth) return -1; |
michael@0 | 603 | else if (month > ruleMonth) return 1; |
michael@0 | 604 | |
michael@0 | 605 | // calculate the actual day of month for the rule |
michael@0 | 606 | int32_t ruleDayOfMonth = 0; |
michael@0 | 607 | |
michael@0 | 608 | // Adjust the ruleDay to the monthLen, for non-leap year February 29 rule days. |
michael@0 | 609 | if (ruleDay > monthLen) { |
michael@0 | 610 | ruleDay = monthLen; |
michael@0 | 611 | } |
michael@0 | 612 | |
michael@0 | 613 | switch (ruleMode) |
michael@0 | 614 | { |
michael@0 | 615 | // if the mode is day-of-month, the day of month is given |
michael@0 | 616 | case DOM_MODE: |
michael@0 | 617 | ruleDayOfMonth = ruleDay; |
michael@0 | 618 | break; |
michael@0 | 619 | |
michael@0 | 620 | // if the mode is day-of-week-in-month, calculate the day-of-month from it |
michael@0 | 621 | case DOW_IN_MONTH_MODE: |
michael@0 | 622 | // In this case ruleDay is the day-of-week-in-month (this code is using |
michael@0 | 623 | // the dayOfWeek and dayOfMonth parameters to figure out the day-of-week |
michael@0 | 624 | // of the first day of the month, so it's trusting that they're really |
michael@0 | 625 | // consistent with each other) |
michael@0 | 626 | if (ruleDay > 0) |
michael@0 | 627 | ruleDayOfMonth = 1 + (ruleDay - 1) * 7 + |
michael@0 | 628 | (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7; |
michael@0 | 629 | |
michael@0 | 630 | // if ruleDay is negative (we assume it's not zero here), we have to do |
michael@0 | 631 | // the same calculation figuring backward from the last day of the month. |
michael@0 | 632 | else |
michael@0 | 633 | { |
michael@0 | 634 | // (again, this code is trusting that dayOfWeek and dayOfMonth are |
michael@0 | 635 | // consistent with each other here, since we're using them to figure |
michael@0 | 636 | // the day of week of the first of the month) |
michael@0 | 637 | ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 - |
michael@0 | 638 | (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7; |
michael@0 | 639 | } |
michael@0 | 640 | break; |
michael@0 | 641 | |
michael@0 | 642 | case DOW_GE_DOM_MODE: |
michael@0 | 643 | ruleDayOfMonth = ruleDay + |
michael@0 | 644 | (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7; |
michael@0 | 645 | break; |
michael@0 | 646 | |
michael@0 | 647 | case DOW_LE_DOM_MODE: |
michael@0 | 648 | ruleDayOfMonth = ruleDay - |
michael@0 | 649 | (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7; |
michael@0 | 650 | // Note at this point ruleDayOfMonth may be <1, although it will |
michael@0 | 651 | // be >=1 for well-formed rules. |
michael@0 | 652 | break; |
michael@0 | 653 | } |
michael@0 | 654 | |
michael@0 | 655 | // now that we have a real day-in-month for the rule, we can compare days... |
michael@0 | 656 | if (dayOfMonth < ruleDayOfMonth) return -1; |
michael@0 | 657 | else if (dayOfMonth > ruleDayOfMonth) return 1; |
michael@0 | 658 | |
michael@0 | 659 | // ...and if they're equal, we compare times |
michael@0 | 660 | if (millis < ruleMillis) return -1; |
michael@0 | 661 | else if (millis > ruleMillis) return 1; |
michael@0 | 662 | else return 0; |
michael@0 | 663 | } |
michael@0 | 664 | |
michael@0 | 665 | // ------------------------------------- |
michael@0 | 666 | |
michael@0 | 667 | int32_t |
michael@0 | 668 | SimpleTimeZone::getRawOffset() const |
michael@0 | 669 | { |
michael@0 | 670 | return rawOffset; |
michael@0 | 671 | } |
michael@0 | 672 | |
michael@0 | 673 | // ------------------------------------- |
michael@0 | 674 | |
michael@0 | 675 | void |
michael@0 | 676 | SimpleTimeZone::setRawOffset(int32_t offsetMillis) |
michael@0 | 677 | { |
michael@0 | 678 | rawOffset = offsetMillis; |
michael@0 | 679 | transitionRulesInitialized = FALSE; |
michael@0 | 680 | } |
michael@0 | 681 | |
michael@0 | 682 | // ------------------------------------- |
michael@0 | 683 | |
michael@0 | 684 | void |
michael@0 | 685 | SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST, UErrorCode& status) |
michael@0 | 686 | { |
michael@0 | 687 | if (millisSavedDuringDST <= 0) { |
michael@0 | 688 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 689 | } |
michael@0 | 690 | else { |
michael@0 | 691 | dstSavings = millisSavedDuringDST; |
michael@0 | 692 | } |
michael@0 | 693 | transitionRulesInitialized = FALSE; |
michael@0 | 694 | } |
michael@0 | 695 | |
michael@0 | 696 | // ------------------------------------- |
michael@0 | 697 | |
michael@0 | 698 | int32_t |
michael@0 | 699 | SimpleTimeZone::getDSTSavings() const |
michael@0 | 700 | { |
michael@0 | 701 | return dstSavings; |
michael@0 | 702 | } |
michael@0 | 703 | |
michael@0 | 704 | // ------------------------------------- |
michael@0 | 705 | |
michael@0 | 706 | UBool |
michael@0 | 707 | SimpleTimeZone::useDaylightTime() const |
michael@0 | 708 | { |
michael@0 | 709 | return useDaylight; |
michael@0 | 710 | } |
michael@0 | 711 | |
michael@0 | 712 | // ------------------------------------- |
michael@0 | 713 | |
michael@0 | 714 | /** |
michael@0 | 715 | * Overrides TimeZone |
michael@0 | 716 | * Queries if the given date is in Daylight Savings Time. |
michael@0 | 717 | */ |
michael@0 | 718 | UBool SimpleTimeZone::inDaylightTime(UDate date, UErrorCode& status) const |
michael@0 | 719 | { |
michael@0 | 720 | // This method is wasteful since it creates a new GregorianCalendar and |
michael@0 | 721 | // deletes it each time it is called. However, this is a deprecated method |
michael@0 | 722 | // and provided only for Java compatibility as of 8/6/97 [LIU]. |
michael@0 | 723 | if (U_FAILURE(status)) return FALSE; |
michael@0 | 724 | GregorianCalendar *gc = new GregorianCalendar(*this, status); |
michael@0 | 725 | /* test for NULL */ |
michael@0 | 726 | if (gc == 0) { |
michael@0 | 727 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 728 | return FALSE; |
michael@0 | 729 | } |
michael@0 | 730 | gc->setTime(date, status); |
michael@0 | 731 | UBool result = gc->inDaylightTime(status); |
michael@0 | 732 | delete gc; |
michael@0 | 733 | return result; |
michael@0 | 734 | } |
michael@0 | 735 | |
michael@0 | 736 | // ------------------------------------- |
michael@0 | 737 | |
michael@0 | 738 | /** |
michael@0 | 739 | * Return true if this zone has the same rules and offset as another zone. |
michael@0 | 740 | * @param other the TimeZone object to be compared with |
michael@0 | 741 | * @return true if the given zone has the same rules and offset as this one |
michael@0 | 742 | */ |
michael@0 | 743 | UBool |
michael@0 | 744 | SimpleTimeZone::hasSameRules(const TimeZone& other) const |
michael@0 | 745 | { |
michael@0 | 746 | if (this == &other) return TRUE; |
michael@0 | 747 | if (typeid(*this) != typeid(other)) return FALSE; |
michael@0 | 748 | SimpleTimeZone *that = (SimpleTimeZone*)&other; |
michael@0 | 749 | return rawOffset == that->rawOffset && |
michael@0 | 750 | useDaylight == that->useDaylight && |
michael@0 | 751 | (!useDaylight |
michael@0 | 752 | // Only check rules if using DST |
michael@0 | 753 | || (dstSavings == that->dstSavings && |
michael@0 | 754 | startMode == that->startMode && |
michael@0 | 755 | startMonth == that->startMonth && |
michael@0 | 756 | startDay == that->startDay && |
michael@0 | 757 | startDayOfWeek == that->startDayOfWeek && |
michael@0 | 758 | startTime == that->startTime && |
michael@0 | 759 | startTimeMode == that->startTimeMode && |
michael@0 | 760 | endMode == that->endMode && |
michael@0 | 761 | endMonth == that->endMonth && |
michael@0 | 762 | endDay == that->endDay && |
michael@0 | 763 | endDayOfWeek == that->endDayOfWeek && |
michael@0 | 764 | endTime == that->endTime && |
michael@0 | 765 | endTimeMode == that->endTimeMode && |
michael@0 | 766 | startYear == that->startYear)); |
michael@0 | 767 | } |
michael@0 | 768 | |
michael@0 | 769 | // ------------------------------------- |
michael@0 | 770 | |
michael@0 | 771 | //---------------------------------------------------------------------- |
michael@0 | 772 | // Rule representation |
michael@0 | 773 | // |
michael@0 | 774 | // We represent the following flavors of rules: |
michael@0 | 775 | // 5 the fifth of the month |
michael@0 | 776 | // lastSun the last Sunday in the month |
michael@0 | 777 | // lastMon the last Monday in the month |
michael@0 | 778 | // Sun>=8 first Sunday on or after the eighth |
michael@0 | 779 | // Sun<=25 last Sunday on or before the 25th |
michael@0 | 780 | // This is further complicated by the fact that we need to remain |
michael@0 | 781 | // backward compatible with the 1.1 FCS. Finally, we need to minimize |
michael@0 | 782 | // API changes. In order to satisfy these requirements, we support |
michael@0 | 783 | // three representation systems, and we translate between them. |
michael@0 | 784 | // |
michael@0 | 785 | // INTERNAL REPRESENTATION |
michael@0 | 786 | // This is the format SimpleTimeZone objects take after construction or |
michael@0 | 787 | // streaming in is complete. Rules are represented directly, using an |
michael@0 | 788 | // unencoded format. We will discuss the start rule only below; the end |
michael@0 | 789 | // rule is analogous. |
michael@0 | 790 | // startMode Takes on enumerated values DAY_OF_MONTH, |
michael@0 | 791 | // DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM. |
michael@0 | 792 | // startDay The day of the month, or for DOW_IN_MONTH mode, a |
michael@0 | 793 | // value indicating which DOW, such as +1 for first, |
michael@0 | 794 | // +2 for second, -1 for last, etc. |
michael@0 | 795 | // startDayOfWeek The day of the week. Ignored for DAY_OF_MONTH. |
michael@0 | 796 | // |
michael@0 | 797 | // ENCODED REPRESENTATION |
michael@0 | 798 | // This is the format accepted by the constructor and by setStartRule() |
michael@0 | 799 | // and setEndRule(). It uses various combinations of positive, negative, |
michael@0 | 800 | // and zero values to encode the different rules. This representation |
michael@0 | 801 | // allows us to specify all the different rule flavors without altering |
michael@0 | 802 | // the API. |
michael@0 | 803 | // MODE startMonth startDay startDayOfWeek |
michael@0 | 804 | // DOW_IN_MONTH_MODE >=0 !=0 >0 |
michael@0 | 805 | // DOM_MODE >=0 >0 ==0 |
michael@0 | 806 | // DOW_GE_DOM_MODE >=0 >0 <0 |
michael@0 | 807 | // DOW_LE_DOM_MODE >=0 <0 <0 |
michael@0 | 808 | // (no DST) don't care ==0 don't care |
michael@0 | 809 | // |
michael@0 | 810 | // STREAMED REPRESENTATION |
michael@0 | 811 | // We must retain binary compatibility with the 1.1 FCS. The 1.1 code only |
michael@0 | 812 | // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the |
michael@0 | 813 | // flag useDaylight. When we stream an object out, we translate into an |
michael@0 | 814 | // approximate DOW_IN_MONTH_MODE representation so the object can be parsed |
michael@0 | 815 | // and used by 1.1 code. Following that, we write out the full |
michael@0 | 816 | // representation separately so that contemporary code can recognize and |
michael@0 | 817 | // parse it. The full representation is written in a "packed" format, |
michael@0 | 818 | // consisting of a version number, a length, and an array of bytes. Future |
michael@0 | 819 | // versions of this class may specify different versions. If they wish to |
michael@0 | 820 | // include additional data, they should do so by storing them after the |
michael@0 | 821 | // packed representation below. |
michael@0 | 822 | //---------------------------------------------------------------------- |
michael@0 | 823 | |
michael@0 | 824 | /** |
michael@0 | 825 | * Given a set of encoded rules in startDay and startDayOfMonth, decode |
michael@0 | 826 | * them and set the startMode appropriately. Do the same for endDay and |
michael@0 | 827 | * endDayOfMonth. Upon entry, the day of week variables may be zero or |
michael@0 | 828 | * negative, in order to indicate special modes. The day of month |
michael@0 | 829 | * variables may also be negative. Upon exit, the mode variables will be |
michael@0 | 830 | * set, and the day of week and day of month variables will be positive. |
michael@0 | 831 | * This method also recognizes a startDay or endDay of zero as indicating |
michael@0 | 832 | * no DST. |
michael@0 | 833 | */ |
michael@0 | 834 | void |
michael@0 | 835 | SimpleTimeZone::decodeRules(UErrorCode& status) |
michael@0 | 836 | { |
michael@0 | 837 | decodeStartRule(status); |
michael@0 | 838 | decodeEndRule(status); |
michael@0 | 839 | } |
michael@0 | 840 | |
michael@0 | 841 | /** |
michael@0 | 842 | * Decode the start rule and validate the parameters. The parameters are |
michael@0 | 843 | * expected to be in encoded form, which represents the various rule modes |
michael@0 | 844 | * by negating or zeroing certain values. Representation formats are: |
michael@0 | 845 | * <p> |
michael@0 | 846 | * <pre> |
michael@0 | 847 | * DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST |
michael@0 | 848 | * ------------ ----- -------- -------- ---------- |
michael@0 | 849 | * month 0..11 same same same don't care |
michael@0 | 850 | * day -5..5 1..31 1..31 -1..-31 0 |
michael@0 | 851 | * dayOfWeek 1..7 0 -1..-7 -1..-7 don't care |
michael@0 | 852 | * time 0..ONEDAY same same same don't care |
michael@0 | 853 | * </pre> |
michael@0 | 854 | * The range for month does not include UNDECIMBER since this class is |
michael@0 | 855 | * really specific to GregorianCalendar, which does not use that month. |
michael@0 | 856 | * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the |
michael@0 | 857 | * end rule is an exclusive limit point. That is, the range of times that |
michael@0 | 858 | * are in DST include those >= the start and < the end. For this reason, |
michael@0 | 859 | * it should be possible to specify an end of ONEDAY in order to include the |
michael@0 | 860 | * entire day. Although this is equivalent to time 0 of the following day, |
michael@0 | 861 | * it's not always possible to specify that, for example, on December 31. |
michael@0 | 862 | * While arguably the start range should still be 0..ONEDAY-1, we keep |
michael@0 | 863 | * the start and end ranges the same for consistency. |
michael@0 | 864 | */ |
michael@0 | 865 | void |
michael@0 | 866 | SimpleTimeZone::decodeStartRule(UErrorCode& status) |
michael@0 | 867 | { |
michael@0 | 868 | if(U_FAILURE(status)) return; |
michael@0 | 869 | |
michael@0 | 870 | useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE); |
michael@0 | 871 | if (useDaylight && dstSavings == 0) { |
michael@0 | 872 | dstSavings = U_MILLIS_PER_HOUR; |
michael@0 | 873 | } |
michael@0 | 874 | if (startDay != 0) { |
michael@0 | 875 | if (startMonth < UCAL_JANUARY || startMonth > UCAL_DECEMBER) { |
michael@0 | 876 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 877 | return; |
michael@0 | 878 | } |
michael@0 | 879 | if (startTime < 0 || startTime > U_MILLIS_PER_DAY || |
michael@0 | 880 | startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) { |
michael@0 | 881 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 882 | return; |
michael@0 | 883 | } |
michael@0 | 884 | if (startDayOfWeek == 0) { |
michael@0 | 885 | startMode = DOM_MODE; |
michael@0 | 886 | } else { |
michael@0 | 887 | if (startDayOfWeek > 0) { |
michael@0 | 888 | startMode = DOW_IN_MONTH_MODE; |
michael@0 | 889 | } else { |
michael@0 | 890 | startDayOfWeek = (int8_t)-startDayOfWeek; |
michael@0 | 891 | if (startDay > 0) { |
michael@0 | 892 | startMode = DOW_GE_DOM_MODE; |
michael@0 | 893 | } else { |
michael@0 | 894 | startDay = (int8_t)-startDay; |
michael@0 | 895 | startMode = DOW_LE_DOM_MODE; |
michael@0 | 896 | } |
michael@0 | 897 | } |
michael@0 | 898 | if (startDayOfWeek > UCAL_SATURDAY) { |
michael@0 | 899 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 900 | return; |
michael@0 | 901 | } |
michael@0 | 902 | } |
michael@0 | 903 | if (startMode == DOW_IN_MONTH_MODE) { |
michael@0 | 904 | if (startDay < -5 || startDay > 5) { |
michael@0 | 905 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 906 | return; |
michael@0 | 907 | } |
michael@0 | 908 | } else if (startDay<1 || startDay > STATICMONTHLENGTH[startMonth]) { |
michael@0 | 909 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 910 | return; |
michael@0 | 911 | } |
michael@0 | 912 | } |
michael@0 | 913 | } |
michael@0 | 914 | |
michael@0 | 915 | /** |
michael@0 | 916 | * Decode the end rule and validate the parameters. This method is exactly |
michael@0 | 917 | * analogous to decodeStartRule(). |
michael@0 | 918 | * @see decodeStartRule |
michael@0 | 919 | */ |
michael@0 | 920 | void |
michael@0 | 921 | SimpleTimeZone::decodeEndRule(UErrorCode& status) |
michael@0 | 922 | { |
michael@0 | 923 | if(U_FAILURE(status)) return; |
michael@0 | 924 | |
michael@0 | 925 | useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE); |
michael@0 | 926 | if (useDaylight && dstSavings == 0) { |
michael@0 | 927 | dstSavings = U_MILLIS_PER_HOUR; |
michael@0 | 928 | } |
michael@0 | 929 | if (endDay != 0) { |
michael@0 | 930 | if (endMonth < UCAL_JANUARY || endMonth > UCAL_DECEMBER) { |
michael@0 | 931 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 932 | return; |
michael@0 | 933 | } |
michael@0 | 934 | if (endTime < 0 || endTime > U_MILLIS_PER_DAY || |
michael@0 | 935 | endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) { |
michael@0 | 936 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 937 | return; |
michael@0 | 938 | } |
michael@0 | 939 | if (endDayOfWeek == 0) { |
michael@0 | 940 | endMode = DOM_MODE; |
michael@0 | 941 | } else { |
michael@0 | 942 | if (endDayOfWeek > 0) { |
michael@0 | 943 | endMode = DOW_IN_MONTH_MODE; |
michael@0 | 944 | } else { |
michael@0 | 945 | endDayOfWeek = (int8_t)-endDayOfWeek; |
michael@0 | 946 | if (endDay > 0) { |
michael@0 | 947 | endMode = DOW_GE_DOM_MODE; |
michael@0 | 948 | } else { |
michael@0 | 949 | endDay = (int8_t)-endDay; |
michael@0 | 950 | endMode = DOW_LE_DOM_MODE; |
michael@0 | 951 | } |
michael@0 | 952 | } |
michael@0 | 953 | if (endDayOfWeek > UCAL_SATURDAY) { |
michael@0 | 954 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 955 | return; |
michael@0 | 956 | } |
michael@0 | 957 | } |
michael@0 | 958 | if (endMode == DOW_IN_MONTH_MODE) { |
michael@0 | 959 | if (endDay < -5 || endDay > 5) { |
michael@0 | 960 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 961 | return; |
michael@0 | 962 | } |
michael@0 | 963 | } else if (endDay<1 || endDay > STATICMONTHLENGTH[endMonth]) { |
michael@0 | 964 | status = U_ILLEGAL_ARGUMENT_ERROR; |
michael@0 | 965 | return; |
michael@0 | 966 | } |
michael@0 | 967 | } |
michael@0 | 968 | } |
michael@0 | 969 | |
michael@0 | 970 | UBool |
michael@0 | 971 | SimpleTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { |
michael@0 | 972 | if (!useDaylight) { |
michael@0 | 973 | return FALSE; |
michael@0 | 974 | } |
michael@0 | 975 | |
michael@0 | 976 | UErrorCode status = U_ZERO_ERROR; |
michael@0 | 977 | checkTransitionRules(status); |
michael@0 | 978 | if (U_FAILURE(status)) { |
michael@0 | 979 | return FALSE; |
michael@0 | 980 | } |
michael@0 | 981 | |
michael@0 | 982 | UDate firstTransitionTime = firstTransition->getTime(); |
michael@0 | 983 | if (base < firstTransitionTime || (inclusive && base == firstTransitionTime)) { |
michael@0 | 984 | result = *firstTransition; |
michael@0 | 985 | } |
michael@0 | 986 | UDate stdDate, dstDate; |
michael@0 | 987 | UBool stdAvail = stdRule->getNextStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate); |
michael@0 | 988 | UBool dstAvail = dstRule->getNextStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate); |
michael@0 | 989 | if (stdAvail && (!dstAvail || stdDate < dstDate)) { |
michael@0 | 990 | result.setTime(stdDate); |
michael@0 | 991 | result.setFrom((const TimeZoneRule&)*dstRule); |
michael@0 | 992 | result.setTo((const TimeZoneRule&)*stdRule); |
michael@0 | 993 | return TRUE; |
michael@0 | 994 | } |
michael@0 | 995 | if (dstAvail && (!stdAvail || dstDate < stdDate)) { |
michael@0 | 996 | result.setTime(dstDate); |
michael@0 | 997 | result.setFrom((const TimeZoneRule&)*stdRule); |
michael@0 | 998 | result.setTo((const TimeZoneRule&)*dstRule); |
michael@0 | 999 | return TRUE; |
michael@0 | 1000 | } |
michael@0 | 1001 | return FALSE; |
michael@0 | 1002 | } |
michael@0 | 1003 | |
michael@0 | 1004 | UBool |
michael@0 | 1005 | SimpleTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { |
michael@0 | 1006 | if (!useDaylight) { |
michael@0 | 1007 | return FALSE; |
michael@0 | 1008 | } |
michael@0 | 1009 | |
michael@0 | 1010 | UErrorCode status = U_ZERO_ERROR; |
michael@0 | 1011 | checkTransitionRules(status); |
michael@0 | 1012 | if (U_FAILURE(status)) { |
michael@0 | 1013 | return FALSE; |
michael@0 | 1014 | } |
michael@0 | 1015 | |
michael@0 | 1016 | UDate firstTransitionTime = firstTransition->getTime(); |
michael@0 | 1017 | if (base < firstTransitionTime || (!inclusive && base == firstTransitionTime)) { |
michael@0 | 1018 | return FALSE; |
michael@0 | 1019 | } |
michael@0 | 1020 | UDate stdDate, dstDate; |
michael@0 | 1021 | UBool stdAvail = stdRule->getPreviousStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate); |
michael@0 | 1022 | UBool dstAvail = dstRule->getPreviousStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate); |
michael@0 | 1023 | if (stdAvail && (!dstAvail || stdDate > dstDate)) { |
michael@0 | 1024 | result.setTime(stdDate); |
michael@0 | 1025 | result.setFrom((const TimeZoneRule&)*dstRule); |
michael@0 | 1026 | result.setTo((const TimeZoneRule&)*stdRule); |
michael@0 | 1027 | return TRUE; |
michael@0 | 1028 | } |
michael@0 | 1029 | if (dstAvail && (!stdAvail || dstDate > stdDate)) { |
michael@0 | 1030 | result.setTime(dstDate); |
michael@0 | 1031 | result.setFrom((const TimeZoneRule&)*stdRule); |
michael@0 | 1032 | result.setTo((const TimeZoneRule&)*dstRule); |
michael@0 | 1033 | return TRUE; |
michael@0 | 1034 | } |
michael@0 | 1035 | return FALSE; |
michael@0 | 1036 | } |
michael@0 | 1037 | |
michael@0 | 1038 | void |
michael@0 | 1039 | SimpleTimeZone::clearTransitionRules(void) { |
michael@0 | 1040 | initialRule = NULL; |
michael@0 | 1041 | firstTransition = NULL; |
michael@0 | 1042 | stdRule = NULL; |
michael@0 | 1043 | dstRule = NULL; |
michael@0 | 1044 | transitionRulesInitialized = FALSE; |
michael@0 | 1045 | } |
michael@0 | 1046 | |
michael@0 | 1047 | void |
michael@0 | 1048 | SimpleTimeZone::deleteTransitionRules(void) { |
michael@0 | 1049 | if (initialRule != NULL) { |
michael@0 | 1050 | delete initialRule; |
michael@0 | 1051 | } |
michael@0 | 1052 | if (firstTransition != NULL) { |
michael@0 | 1053 | delete firstTransition; |
michael@0 | 1054 | } |
michael@0 | 1055 | if (stdRule != NULL) { |
michael@0 | 1056 | delete stdRule; |
michael@0 | 1057 | } |
michael@0 | 1058 | if (dstRule != NULL) { |
michael@0 | 1059 | delete dstRule; |
michael@0 | 1060 | } |
michael@0 | 1061 | clearTransitionRules(); |
michael@0 | 1062 | } |
michael@0 | 1063 | |
michael@0 | 1064 | /* |
michael@0 | 1065 | * Lazy transition rules initializer |
michael@0 | 1066 | * |
michael@0 | 1067 | * Note On the removal of UMTX_CHECK from checkTransitionRules(): |
michael@0 | 1068 | * |
michael@0 | 1069 | * It would be faster to have a UInitOnce as part of a SimpleTimeZone object, |
michael@0 | 1070 | * which would avoid needing to lock a mutex to check the initialization state. |
michael@0 | 1071 | * But we can't easily because simpletz.h is a public header, and including |
michael@0 | 1072 | * a UInitOnce as a member of SimpleTimeZone would publicly expose internal ICU headers. |
michael@0 | 1073 | * |
michael@0 | 1074 | * Alternatively we could have a pointer to a UInitOnce in the SimpleTimeZone object, |
michael@0 | 1075 | * allocate it in the constructors. This would be a more intrusive change, but doable |
michael@0 | 1076 | * if performance turns out to be an issue. |
michael@0 | 1077 | */ |
michael@0 | 1078 | static UMutex gLock = U_MUTEX_INITIALIZER; |
michael@0 | 1079 | |
michael@0 | 1080 | void |
michael@0 | 1081 | SimpleTimeZone::checkTransitionRules(UErrorCode& status) const { |
michael@0 | 1082 | if (U_FAILURE(status)) { |
michael@0 | 1083 | return; |
michael@0 | 1084 | } |
michael@0 | 1085 | umtx_lock(&gLock); |
michael@0 | 1086 | if (!transitionRulesInitialized) { |
michael@0 | 1087 | SimpleTimeZone *ncThis = const_cast<SimpleTimeZone*>(this); |
michael@0 | 1088 | ncThis->initTransitionRules(status); |
michael@0 | 1089 | } |
michael@0 | 1090 | umtx_unlock(&gLock); |
michael@0 | 1091 | } |
michael@0 | 1092 | |
michael@0 | 1093 | void |
michael@0 | 1094 | SimpleTimeZone::initTransitionRules(UErrorCode& status) { |
michael@0 | 1095 | if (U_FAILURE(status)) { |
michael@0 | 1096 | return; |
michael@0 | 1097 | } |
michael@0 | 1098 | if (transitionRulesInitialized) { |
michael@0 | 1099 | return; |
michael@0 | 1100 | } |
michael@0 | 1101 | deleteTransitionRules(); |
michael@0 | 1102 | UnicodeString tzid; |
michael@0 | 1103 | getID(tzid); |
michael@0 | 1104 | |
michael@0 | 1105 | if (useDaylight) { |
michael@0 | 1106 | DateTimeRule* dtRule; |
michael@0 | 1107 | DateTimeRule::TimeRuleType timeRuleType; |
michael@0 | 1108 | UDate firstStdStart, firstDstStart; |
michael@0 | 1109 | |
michael@0 | 1110 | // Create a TimeZoneRule for daylight saving time |
michael@0 | 1111 | timeRuleType = (startTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME : |
michael@0 | 1112 | ((startTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME); |
michael@0 | 1113 | switch (startMode) { |
michael@0 | 1114 | case DOM_MODE: |
michael@0 | 1115 | dtRule = new DateTimeRule(startMonth, startDay, startTime, timeRuleType); |
michael@0 | 1116 | break; |
michael@0 | 1117 | case DOW_IN_MONTH_MODE: |
michael@0 | 1118 | dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, startTime, timeRuleType); |
michael@0 | 1119 | break; |
michael@0 | 1120 | case DOW_GE_DOM_MODE: |
michael@0 | 1121 | dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, true, startTime, timeRuleType); |
michael@0 | 1122 | break; |
michael@0 | 1123 | case DOW_LE_DOM_MODE: |
michael@0 | 1124 | dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, false, startTime, timeRuleType); |
michael@0 | 1125 | break; |
michael@0 | 1126 | default: |
michael@0 | 1127 | status = U_INVALID_STATE_ERROR; |
michael@0 | 1128 | return; |
michael@0 | 1129 | } |
michael@0 | 1130 | // Check for Null pointer |
michael@0 | 1131 | if (dtRule == NULL) { |
michael@0 | 1132 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 1133 | return; |
michael@0 | 1134 | } |
michael@0 | 1135 | // For now, use ID + "(DST)" as the name |
michael@0 | 1136 | dstRule = new AnnualTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), getDSTSavings(), |
michael@0 | 1137 | dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR); |
michael@0 | 1138 | |
michael@0 | 1139 | // Check for Null pointer |
michael@0 | 1140 | if (dstRule == NULL) { |
michael@0 | 1141 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 1142 | deleteTransitionRules(); |
michael@0 | 1143 | return; |
michael@0 | 1144 | } |
michael@0 | 1145 | |
michael@0 | 1146 | // Calculate the first DST start time |
michael@0 | 1147 | dstRule->getFirstStart(getRawOffset(), 0, firstDstStart); |
michael@0 | 1148 | |
michael@0 | 1149 | // Create a TimeZoneRule for standard time |
michael@0 | 1150 | timeRuleType = (endTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME : |
michael@0 | 1151 | ((endTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME); |
michael@0 | 1152 | switch (endMode) { |
michael@0 | 1153 | case DOM_MODE: |
michael@0 | 1154 | dtRule = new DateTimeRule(endMonth, endDay, endTime, timeRuleType); |
michael@0 | 1155 | break; |
michael@0 | 1156 | case DOW_IN_MONTH_MODE: |
michael@0 | 1157 | dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, endTime, timeRuleType); |
michael@0 | 1158 | break; |
michael@0 | 1159 | case DOW_GE_DOM_MODE: |
michael@0 | 1160 | dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, true, endTime, timeRuleType); |
michael@0 | 1161 | break; |
michael@0 | 1162 | case DOW_LE_DOM_MODE: |
michael@0 | 1163 | dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, false, endTime, timeRuleType); |
michael@0 | 1164 | break; |
michael@0 | 1165 | } |
michael@0 | 1166 | |
michael@0 | 1167 | // Check for Null pointer |
michael@0 | 1168 | if (dtRule == NULL) { |
michael@0 | 1169 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 1170 | deleteTransitionRules(); |
michael@0 | 1171 | return; |
michael@0 | 1172 | } |
michael@0 | 1173 | // For now, use ID + "(STD)" as the name |
michael@0 | 1174 | stdRule = new AnnualTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0, |
michael@0 | 1175 | dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR); |
michael@0 | 1176 | |
michael@0 | 1177 | //Check for Null pointer |
michael@0 | 1178 | if (stdRule == NULL) { |
michael@0 | 1179 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 1180 | deleteTransitionRules(); |
michael@0 | 1181 | return; |
michael@0 | 1182 | } |
michael@0 | 1183 | |
michael@0 | 1184 | // Calculate the first STD start time |
michael@0 | 1185 | stdRule->getFirstStart(getRawOffset(), dstRule->getDSTSavings(), firstStdStart); |
michael@0 | 1186 | |
michael@0 | 1187 | // Create a TimeZoneRule for initial time |
michael@0 | 1188 | if (firstStdStart < firstDstStart) { |
michael@0 | 1189 | initialRule = new InitialTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), dstRule->getDSTSavings()); |
michael@0 | 1190 | firstTransition = new TimeZoneTransition(firstStdStart, *initialRule, *stdRule); |
michael@0 | 1191 | } else { |
michael@0 | 1192 | initialRule = new InitialTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0); |
michael@0 | 1193 | firstTransition = new TimeZoneTransition(firstDstStart, *initialRule, *dstRule); |
michael@0 | 1194 | } |
michael@0 | 1195 | // Check for null pointers. |
michael@0 | 1196 | if (initialRule == NULL || firstTransition == NULL) { |
michael@0 | 1197 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 1198 | deleteTransitionRules(); |
michael@0 | 1199 | return; |
michael@0 | 1200 | } |
michael@0 | 1201 | |
michael@0 | 1202 | } else { |
michael@0 | 1203 | // Create a TimeZoneRule for initial time |
michael@0 | 1204 | initialRule = new InitialTimeZoneRule(tzid, getRawOffset(), 0); |
michael@0 | 1205 | // Check for null pointer. |
michael@0 | 1206 | if (initialRule == NULL) { |
michael@0 | 1207 | status = U_MEMORY_ALLOCATION_ERROR; |
michael@0 | 1208 | deleteTransitionRules(); |
michael@0 | 1209 | return; |
michael@0 | 1210 | } |
michael@0 | 1211 | } |
michael@0 | 1212 | |
michael@0 | 1213 | transitionRulesInitialized = TRUE; |
michael@0 | 1214 | } |
michael@0 | 1215 | |
michael@0 | 1216 | int32_t |
michael@0 | 1217 | SimpleTimeZone::countTransitionRules(UErrorCode& /*status*/) const { |
michael@0 | 1218 | return (useDaylight) ? 2 : 0; |
michael@0 | 1219 | } |
michael@0 | 1220 | |
michael@0 | 1221 | void |
michael@0 | 1222 | SimpleTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial, |
michael@0 | 1223 | const TimeZoneRule* trsrules[], |
michael@0 | 1224 | int32_t& trscount, |
michael@0 | 1225 | UErrorCode& status) const { |
michael@0 | 1226 | if (U_FAILURE(status)) { |
michael@0 | 1227 | return; |
michael@0 | 1228 | } |
michael@0 | 1229 | checkTransitionRules(status); |
michael@0 | 1230 | if (U_FAILURE(status)) { |
michael@0 | 1231 | return; |
michael@0 | 1232 | } |
michael@0 | 1233 | initial = initialRule; |
michael@0 | 1234 | int32_t cnt = 0; |
michael@0 | 1235 | if (stdRule != NULL) { |
michael@0 | 1236 | if (cnt < trscount) { |
michael@0 | 1237 | trsrules[cnt++] = stdRule; |
michael@0 | 1238 | } |
michael@0 | 1239 | if (cnt < trscount) { |
michael@0 | 1240 | trsrules[cnt++] = dstRule; |
michael@0 | 1241 | } |
michael@0 | 1242 | } |
michael@0 | 1243 | trscount = cnt; |
michael@0 | 1244 | } |
michael@0 | 1245 | |
michael@0 | 1246 | |
michael@0 | 1247 | U_NAMESPACE_END |
michael@0 | 1248 | |
michael@0 | 1249 | #endif /* #if !UCONFIG_NO_FORMATTING */ |
michael@0 | 1250 | |
michael@0 | 1251 | //eof |