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