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