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