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 accommodate 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 bool
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 specified 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,UTimeZoneLocalOption nonExistingTimeOpt,UTimeZoneLocalOption duplicatedTimeOpt,int32_t & rawOffsetGMT,int32_t & savingsDST,UErrorCode & status) const512 SimpleTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingTimeOpt,
513 UTimeZoneLocalOption duplicatedTimeOpt, int32_t& rawOffsetGMT,
514 int32_t& savingsDST, UErrorCode& status) const
515 {
516 if (U_FAILURE(status)) {
517 return;
518 }
519
520 rawOffsetGMT = getRawOffset();
521 int32_t year, month, dom, dow;
522 double day = uprv_floor(date / U_MILLIS_PER_DAY);
523 int32_t millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
524
525 Grego::dayToFields(day, year, month, dom, dow);
526
527 savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
528 (uint8_t) dow, millis,
529 Grego::monthLength(year, month),
530 status) - rawOffsetGMT;
531 if (U_FAILURE(status)) {
532 return;
533 }
534
535 UBool recalc = FALSE;
536
537 // Now we need some adjustment
538 if (savingsDST > 0) {
539 if ((nonExistingTimeOpt & kStdDstMask) == kStandard
540 || ((nonExistingTimeOpt & kStdDstMask) != kDaylight && (nonExistingTimeOpt & kFormerLatterMask) != kLatter)) {
541 date -= getDSTSavings();
542 recalc = TRUE;
543 }
544 } else {
545 if ((duplicatedTimeOpt & kStdDstMask) == kDaylight
546 || ((duplicatedTimeOpt & kStdDstMask) != kStandard && (duplicatedTimeOpt & kFormerLatterMask) == kFormer)) {
547 date -= getDSTSavings();
548 recalc = TRUE;
549 }
550 }
551 if (recalc) {
552 day = uprv_floor(date / U_MILLIS_PER_DAY);
553 millis = (int32_t) (date - day * U_MILLIS_PER_DAY);
554 Grego::dayToFields(day, year, month, dom, dow);
555 savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
556 (uint8_t) dow, millis,
557 Grego::monthLength(year, month),
558 status) - rawOffsetGMT;
559 }
560 }
561
562 // -------------------------------------
563
564 /**
565 * Compare a given date in the year to a rule. Return 1, 0, or -1, depending
566 * on whether the date is after, equal to, or before the rule date. The
567 * millis are compared directly against the ruleMillis, so any
568 * standard-daylight adjustments must be handled by the caller.
569 *
570 * @return 1 if the date is after the rule date, -1 if the date is before
571 * the rule date, or 0 if the date is equal to the rule date.
572 */
573 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)574 SimpleTimeZone::compareToRule(int8_t month, int8_t monthLen, int8_t prevMonthLen,
575 int8_t dayOfMonth,
576 int8_t dayOfWeek, int32_t millis, int32_t millisDelta,
577 EMode ruleMode, int8_t ruleMonth, int8_t ruleDayOfWeek,
578 int8_t ruleDay, int32_t ruleMillis)
579 {
580 // Make adjustments for startTimeMode and endTimeMode
581 millis += millisDelta;
582 while (millis >= U_MILLIS_PER_DAY) {
583 millis -= U_MILLIS_PER_DAY;
584 ++dayOfMonth;
585 dayOfWeek = (int8_t)(1 + (dayOfWeek % 7)); // dayOfWeek is one-based
586 if (dayOfMonth > monthLen) {
587 dayOfMonth = 1;
588 /* When incrementing the month, it is desirable to overflow
589 * from DECEMBER to DECEMBER+1, since we use the result to
590 * compare against a real month. Wraparound of the value
591 * leads to bug 4173604. */
592 ++month;
593 }
594 }
595 while (millis < 0) {
596 millis += U_MILLIS_PER_DAY;
597 --dayOfMonth;
598 dayOfWeek = (int8_t)(1 + ((dayOfWeek+5) % 7)); // dayOfWeek is one-based
599 if (dayOfMonth < 1) {
600 dayOfMonth = prevMonthLen;
601 --month;
602 }
603 }
604
605 // first compare months. If they're different, we don't have to worry about days
606 // and times
607 if (month < ruleMonth) return -1;
608 else if (month > ruleMonth) return 1;
609
610 // calculate the actual day of month for the rule
611 int32_t ruleDayOfMonth = 0;
612
613 // Adjust the ruleDay to the monthLen, for non-leap year February 29 rule days.
614 if (ruleDay > monthLen) {
615 ruleDay = monthLen;
616 }
617
618 switch (ruleMode)
619 {
620 // if the mode is day-of-month, the day of month is given
621 case DOM_MODE:
622 ruleDayOfMonth = ruleDay;
623 break;
624
625 // if the mode is day-of-week-in-month, calculate the day-of-month from it
626 case DOW_IN_MONTH_MODE:
627 // In this case ruleDay is the day-of-week-in-month (this code is using
628 // the dayOfWeek and dayOfMonth parameters to figure out the day-of-week
629 // of the first day of the month, so it's trusting that they're really
630 // consistent with each other)
631 if (ruleDay > 0)
632 ruleDayOfMonth = 1 + (ruleDay - 1) * 7 +
633 (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7;
634
635 // if ruleDay is negative (we assume it's not zero here), we have to do
636 // the same calculation figuring backward from the last day of the month.
637 else
638 {
639 // (again, this code is trusting that dayOfWeek and dayOfMonth are
640 // consistent with each other here, since we're using them to figure
641 // the day of week of the first of the month)
642 ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 -
643 (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7;
644 }
645 break;
646
647 case DOW_GE_DOM_MODE:
648 ruleDayOfMonth = ruleDay +
649 (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7;
650 break;
651
652 case DOW_LE_DOM_MODE:
653 ruleDayOfMonth = ruleDay -
654 (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7;
655 // Note at this point ruleDayOfMonth may be <1, although it will
656 // be >=1 for well-formed rules.
657 break;
658 }
659
660 // now that we have a real day-in-month for the rule, we can compare days...
661 if (dayOfMonth < ruleDayOfMonth) return -1;
662 else if (dayOfMonth > ruleDayOfMonth) return 1;
663
664 // ...and if they're equal, we compare times
665 if (millis < ruleMillis) return -1;
666 else if (millis > ruleMillis) return 1;
667 else return 0;
668 }
669
670 // -------------------------------------
671
672 int32_t
getRawOffset() const673 SimpleTimeZone::getRawOffset() const
674 {
675 return rawOffset;
676 }
677
678 // -------------------------------------
679
680 void
setRawOffset(int32_t offsetMillis)681 SimpleTimeZone::setRawOffset(int32_t offsetMillis)
682 {
683 rawOffset = offsetMillis;
684 transitionRulesInitialized = FALSE;
685 }
686
687 // -------------------------------------
688
689 void
setDSTSavings(int32_t millisSavedDuringDST,UErrorCode & status)690 SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST, UErrorCode& status)
691 {
692 if (millisSavedDuringDST == 0) {
693 status = U_ILLEGAL_ARGUMENT_ERROR;
694 }
695 else {
696 dstSavings = millisSavedDuringDST;
697 }
698 transitionRulesInitialized = FALSE;
699 }
700
701 // -------------------------------------
702
703 int32_t
getDSTSavings() const704 SimpleTimeZone::getDSTSavings() const
705 {
706 return dstSavings;
707 }
708
709 // -------------------------------------
710
711 UBool
useDaylightTime() const712 SimpleTimeZone::useDaylightTime() const
713 {
714 return useDaylight;
715 }
716
717 // -------------------------------------
718
719 /**
720 * Overrides TimeZone
721 * Queries if the given date is in Daylight Savings Time.
722 */
inDaylightTime(UDate date,UErrorCode & status) const723 UBool SimpleTimeZone::inDaylightTime(UDate date, UErrorCode& status) const
724 {
725 // This method is wasteful since it creates a new GregorianCalendar and
726 // deletes it each time it is called. However, this is a deprecated method
727 // and provided only for Java compatibility as of 8/6/97 [LIU].
728 if (U_FAILURE(status)) return FALSE;
729 GregorianCalendar *gc = new GregorianCalendar(*this, status);
730 /* test for NULL */
731 if (gc == 0) {
732 status = U_MEMORY_ALLOCATION_ERROR;
733 return FALSE;
734 }
735 gc->setTime(date, status);
736 UBool result = gc->inDaylightTime(status);
737 delete gc;
738 return result;
739 }
740
741 // -------------------------------------
742
743 /**
744 * Return true if this zone has the same rules and offset as another zone.
745 * @param other the TimeZone object to be compared with
746 * @return true if the given zone has the same rules and offset as this one
747 */
748 UBool
hasSameRules(const TimeZone & other) const749 SimpleTimeZone::hasSameRules(const TimeZone& other) const
750 {
751 if (this == &other) return TRUE;
752 if (typeid(*this) != typeid(other)) return FALSE;
753 SimpleTimeZone *that = (SimpleTimeZone*)&other;
754 return rawOffset == that->rawOffset &&
755 useDaylight == that->useDaylight &&
756 (!useDaylight
757 // Only check rules if using DST
758 || (dstSavings == that->dstSavings &&
759 startMode == that->startMode &&
760 startMonth == that->startMonth &&
761 startDay == that->startDay &&
762 startDayOfWeek == that->startDayOfWeek &&
763 startTime == that->startTime &&
764 startTimeMode == that->startTimeMode &&
765 endMode == that->endMode &&
766 endMonth == that->endMonth &&
767 endDay == that->endDay &&
768 endDayOfWeek == that->endDayOfWeek &&
769 endTime == that->endTime &&
770 endTimeMode == that->endTimeMode &&
771 startYear == that->startYear));
772 }
773
774 // -------------------------------------
775
776 //----------------------------------------------------------------------
777 // Rule representation
778 //
779 // We represent the following flavors of rules:
780 // 5 the fifth of the month
781 // lastSun the last Sunday in the month
782 // lastMon the last Monday in the month
783 // Sun>=8 first Sunday on or after the eighth
784 // Sun<=25 last Sunday on or before the 25th
785 // This is further complicated by the fact that we need to remain
786 // backward compatible with the 1.1 FCS. Finally, we need to minimize
787 // API changes. In order to satisfy these requirements, we support
788 // three representation systems, and we translate between them.
789 //
790 // INTERNAL REPRESENTATION
791 // This is the format SimpleTimeZone objects take after construction or
792 // streaming in is complete. Rules are represented directly, using an
793 // unencoded format. We will discuss the start rule only below; the end
794 // rule is analogous.
795 // startMode Takes on enumerated values DAY_OF_MONTH,
796 // DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
797 // startDay The day of the month, or for DOW_IN_MONTH mode, a
798 // value indicating which DOW, such as +1 for first,
799 // +2 for second, -1 for last, etc.
800 // startDayOfWeek The day of the week. Ignored for DAY_OF_MONTH.
801 //
802 // ENCODED REPRESENTATION
803 // This is the format accepted by the constructor and by setStartRule()
804 // and setEndRule(). It uses various combinations of positive, negative,
805 // and zero values to encode the different rules. This representation
806 // allows us to specify all the different rule flavors without altering
807 // the API.
808 // MODE startMonth startDay startDayOfWeek
809 // DOW_IN_MONTH_MODE >=0 !=0 >0
810 // DOM_MODE >=0 >0 ==0
811 // DOW_GE_DOM_MODE >=0 >0 <0
812 // DOW_LE_DOM_MODE >=0 <0 <0
813 // (no DST) don't care ==0 don't care
814 //
815 // STREAMED REPRESENTATION
816 // We must retain binary compatibility with the 1.1 FCS. The 1.1 code only
817 // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
818 // flag useDaylight. When we stream an object out, we translate into an
819 // approximate DOW_IN_MONTH_MODE representation so the object can be parsed
820 // and used by 1.1 code. Following that, we write out the full
821 // representation separately so that contemporary code can recognize and
822 // parse it. The full representation is written in a "packed" format,
823 // consisting of a version number, a length, and an array of bytes. Future
824 // versions of this class may specify different versions. If they wish to
825 // include additional data, they should do so by storing them after the
826 // packed representation below.
827 //----------------------------------------------------------------------
828
829 /**
830 * Given a set of encoded rules in startDay and startDayOfMonth, decode
831 * them and set the startMode appropriately. Do the same for endDay and
832 * endDayOfMonth. Upon entry, the day of week variables may be zero or
833 * negative, in order to indicate special modes. The day of month
834 * variables may also be negative. Upon exit, the mode variables will be
835 * set, and the day of week and day of month variables will be positive.
836 * This method also recognizes a startDay or endDay of zero as indicating
837 * no DST.
838 */
839 void
decodeRules(UErrorCode & status)840 SimpleTimeZone::decodeRules(UErrorCode& status)
841 {
842 decodeStartRule(status);
843 decodeEndRule(status);
844 }
845
846 /**
847 * Decode the start rule and validate the parameters. The parameters are
848 * expected to be in encoded form, which represents the various rule modes
849 * by negating or zeroing certain values. Representation formats are:
850 * <p>
851 * <pre>
852 * DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST
853 * ------------ ----- -------- -------- ----------
854 * month 0..11 same same same don't care
855 * day -5..5 1..31 1..31 -1..-31 0
856 * dayOfWeek 1..7 0 -1..-7 -1..-7 don't care
857 * time 0..ONEDAY same same same don't care
858 * </pre>
859 * The range for month does not include UNDECIMBER since this class is
860 * really specific to GregorianCalendar, which does not use that month.
861 * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
862 * end rule is an exclusive limit point. That is, the range of times that
863 * are in DST include those >= the start and < the end. For this reason,
864 * it should be possible to specify an end of ONEDAY in order to include the
865 * entire day. Although this is equivalent to time 0 of the following day,
866 * it's not always possible to specify that, for example, on December 31.
867 * While arguably the start range should still be 0..ONEDAY-1, we keep
868 * the start and end ranges the same for consistency.
869 */
870 void
decodeStartRule(UErrorCode & status)871 SimpleTimeZone::decodeStartRule(UErrorCode& status)
872 {
873 if(U_FAILURE(status)) return;
874
875 useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE);
876 if (useDaylight && dstSavings == 0) {
877 dstSavings = U_MILLIS_PER_HOUR;
878 }
879 if (startDay != 0) {
880 if (startMonth < UCAL_JANUARY || startMonth > UCAL_DECEMBER) {
881 status = U_ILLEGAL_ARGUMENT_ERROR;
882 return;
883 }
884 if (startTime < 0 || startTime > U_MILLIS_PER_DAY ||
885 startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) {
886 status = U_ILLEGAL_ARGUMENT_ERROR;
887 return;
888 }
889 if (startDayOfWeek == 0) {
890 startMode = DOM_MODE;
891 } else {
892 if (startDayOfWeek > 0) {
893 startMode = DOW_IN_MONTH_MODE;
894 } else {
895 startDayOfWeek = (int8_t)-startDayOfWeek;
896 if (startDay > 0) {
897 startMode = DOW_GE_DOM_MODE;
898 } else {
899 startDay = (int8_t)-startDay;
900 startMode = DOW_LE_DOM_MODE;
901 }
902 }
903 if (startDayOfWeek > UCAL_SATURDAY) {
904 status = U_ILLEGAL_ARGUMENT_ERROR;
905 return;
906 }
907 }
908 if (startMode == DOW_IN_MONTH_MODE) {
909 if (startDay < -5 || startDay > 5) {
910 status = U_ILLEGAL_ARGUMENT_ERROR;
911 return;
912 }
913 } else if (startDay<1 || startDay > STATICMONTHLENGTH[startMonth]) {
914 status = U_ILLEGAL_ARGUMENT_ERROR;
915 return;
916 }
917 }
918 }
919
920 /**
921 * Decode the end rule and validate the parameters. This method is exactly
922 * analogous to decodeStartRule().
923 * @see decodeStartRule
924 */
925 void
decodeEndRule(UErrorCode & status)926 SimpleTimeZone::decodeEndRule(UErrorCode& status)
927 {
928 if(U_FAILURE(status)) return;
929
930 useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE);
931 if (useDaylight && dstSavings == 0) {
932 dstSavings = U_MILLIS_PER_HOUR;
933 }
934 if (endDay != 0) {
935 if (endMonth < UCAL_JANUARY || endMonth > UCAL_DECEMBER) {
936 status = U_ILLEGAL_ARGUMENT_ERROR;
937 return;
938 }
939 if (endTime < 0 || endTime > U_MILLIS_PER_DAY ||
940 endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) {
941 status = U_ILLEGAL_ARGUMENT_ERROR;
942 return;
943 }
944 if (endDayOfWeek == 0) {
945 endMode = DOM_MODE;
946 } else {
947 if (endDayOfWeek > 0) {
948 endMode = DOW_IN_MONTH_MODE;
949 } else {
950 endDayOfWeek = (int8_t)-endDayOfWeek;
951 if (endDay > 0) {
952 endMode = DOW_GE_DOM_MODE;
953 } else {
954 endDay = (int8_t)-endDay;
955 endMode = DOW_LE_DOM_MODE;
956 }
957 }
958 if (endDayOfWeek > UCAL_SATURDAY) {
959 status = U_ILLEGAL_ARGUMENT_ERROR;
960 return;
961 }
962 }
963 if (endMode == DOW_IN_MONTH_MODE) {
964 if (endDay < -5 || endDay > 5) {
965 status = U_ILLEGAL_ARGUMENT_ERROR;
966 return;
967 }
968 } else if (endDay<1 || endDay > STATICMONTHLENGTH[endMonth]) {
969 status = U_ILLEGAL_ARGUMENT_ERROR;
970 return;
971 }
972 }
973 }
974
975 UBool
getNextTransition(UDate base,UBool inclusive,TimeZoneTransition & result) const976 SimpleTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
977 if (!useDaylight) {
978 return FALSE;
979 }
980
981 UErrorCode status = U_ZERO_ERROR;
982 checkTransitionRules(status);
983 if (U_FAILURE(status)) {
984 return FALSE;
985 }
986
987 UDate firstTransitionTime = firstTransition->getTime();
988 if (base < firstTransitionTime || (inclusive && base == firstTransitionTime)) {
989 result = *firstTransition;
990 }
991 UDate stdDate, dstDate;
992 UBool stdAvail = stdRule->getNextStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
993 UBool dstAvail = dstRule->getNextStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
994 if (stdAvail && (!dstAvail || stdDate < dstDate)) {
995 result.setTime(stdDate);
996 result.setFrom((const TimeZoneRule&)*dstRule);
997 result.setTo((const TimeZoneRule&)*stdRule);
998 return TRUE;
999 }
1000 if (dstAvail && (!stdAvail || dstDate < stdDate)) {
1001 result.setTime(dstDate);
1002 result.setFrom((const TimeZoneRule&)*stdRule);
1003 result.setTo((const TimeZoneRule&)*dstRule);
1004 return TRUE;
1005 }
1006 return FALSE;
1007 }
1008
1009 UBool
getPreviousTransition(UDate base,UBool inclusive,TimeZoneTransition & result) const1010 SimpleTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
1011 if (!useDaylight) {
1012 return FALSE;
1013 }
1014
1015 UErrorCode status = U_ZERO_ERROR;
1016 checkTransitionRules(status);
1017 if (U_FAILURE(status)) {
1018 return FALSE;
1019 }
1020
1021 UDate firstTransitionTime = firstTransition->getTime();
1022 if (base < firstTransitionTime || (!inclusive && base == firstTransitionTime)) {
1023 return FALSE;
1024 }
1025 UDate stdDate, dstDate;
1026 UBool stdAvail = stdRule->getPreviousStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
1027 UBool dstAvail = dstRule->getPreviousStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
1028 if (stdAvail && (!dstAvail || stdDate > dstDate)) {
1029 result.setTime(stdDate);
1030 result.setFrom((const TimeZoneRule&)*dstRule);
1031 result.setTo((const TimeZoneRule&)*stdRule);
1032 return TRUE;
1033 }
1034 if (dstAvail && (!stdAvail || dstDate > stdDate)) {
1035 result.setTime(dstDate);
1036 result.setFrom((const TimeZoneRule&)*stdRule);
1037 result.setTo((const TimeZoneRule&)*dstRule);
1038 return TRUE;
1039 }
1040 return FALSE;
1041 }
1042
1043 void
clearTransitionRules(void)1044 SimpleTimeZone::clearTransitionRules(void) {
1045 initialRule = NULL;
1046 firstTransition = NULL;
1047 stdRule = NULL;
1048 dstRule = NULL;
1049 transitionRulesInitialized = FALSE;
1050 }
1051
1052 void
deleteTransitionRules(void)1053 SimpleTimeZone::deleteTransitionRules(void) {
1054 if (initialRule != NULL) {
1055 delete initialRule;
1056 }
1057 if (firstTransition != NULL) {
1058 delete firstTransition;
1059 }
1060 if (stdRule != NULL) {
1061 delete stdRule;
1062 }
1063 if (dstRule != NULL) {
1064 delete dstRule;
1065 }
1066 clearTransitionRules();
1067 }
1068
1069 /*
1070 * Lazy transition rules initializer
1071 *
1072 * Note On the removal of UMTX_CHECK from checkTransitionRules():
1073 *
1074 * It would be faster to have a UInitOnce as part of a SimpleTimeZone object,
1075 * which would avoid needing to lock a mutex to check the initialization state.
1076 * But we can't easily because simpletz.h is a public header, and including
1077 * a UInitOnce as a member of SimpleTimeZone would publicly expose internal ICU headers.
1078 *
1079 * Alternatively we could have a pointer to a UInitOnce in the SimpleTimeZone object,
1080 * allocate it in the constructors. This would be a more intrusive change, but doable
1081 * if performance turns out to be an issue.
1082 */
1083
1084 void
checkTransitionRules(UErrorCode & status) const1085 SimpleTimeZone::checkTransitionRules(UErrorCode& status) const {
1086 if (U_FAILURE(status)) {
1087 return;
1088 }
1089 static UMutex gLock;
1090 umtx_lock(&gLock);
1091 if (!transitionRulesInitialized) {
1092 SimpleTimeZone *ncThis = const_cast<SimpleTimeZone*>(this);
1093 ncThis->initTransitionRules(status);
1094 }
1095 umtx_unlock(&gLock);
1096 }
1097
1098 void
initTransitionRules(UErrorCode & status)1099 SimpleTimeZone::initTransitionRules(UErrorCode& status) {
1100 if (U_FAILURE(status)) {
1101 return;
1102 }
1103 if (transitionRulesInitialized) {
1104 return;
1105 }
1106 deleteTransitionRules();
1107 UnicodeString tzid;
1108 getID(tzid);
1109
1110 if (useDaylight) {
1111 DateTimeRule* dtRule;
1112 DateTimeRule::TimeRuleType timeRuleType;
1113 UDate firstStdStart, firstDstStart;
1114
1115 // Create a TimeZoneRule for daylight saving time
1116 timeRuleType = (startTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME :
1117 ((startTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME);
1118 switch (startMode) {
1119 case DOM_MODE:
1120 dtRule = new DateTimeRule(startMonth, startDay, startTime, timeRuleType);
1121 break;
1122 case DOW_IN_MONTH_MODE:
1123 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, startTime, timeRuleType);
1124 break;
1125 case DOW_GE_DOM_MODE:
1126 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, true, startTime, timeRuleType);
1127 break;
1128 case DOW_LE_DOM_MODE:
1129 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, false, startTime, timeRuleType);
1130 break;
1131 default:
1132 status = U_INVALID_STATE_ERROR;
1133 return;
1134 }
1135 // Check for Null pointer
1136 if (dtRule == NULL) {
1137 status = U_MEMORY_ALLOCATION_ERROR;
1138 return;
1139 }
1140 // For now, use ID + "(DST)" as the name
1141 dstRule = new AnnualTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), getDSTSavings(),
1142 dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR);
1143
1144 // Check for Null pointer
1145 if (dstRule == NULL) {
1146 status = U_MEMORY_ALLOCATION_ERROR;
1147 deleteTransitionRules();
1148 return;
1149 }
1150
1151 // Calculate the first DST start time
1152 dstRule->getFirstStart(getRawOffset(), 0, firstDstStart);
1153
1154 // Create a TimeZoneRule for standard time
1155 timeRuleType = (endTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME :
1156 ((endTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME);
1157 switch (endMode) {
1158 case DOM_MODE:
1159 dtRule = new DateTimeRule(endMonth, endDay, endTime, timeRuleType);
1160 break;
1161 case DOW_IN_MONTH_MODE:
1162 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, endTime, timeRuleType);
1163 break;
1164 case DOW_GE_DOM_MODE:
1165 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, true, endTime, timeRuleType);
1166 break;
1167 case DOW_LE_DOM_MODE:
1168 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, false, endTime, timeRuleType);
1169 break;
1170 }
1171
1172 // Check for Null pointer
1173 if (dtRule == NULL) {
1174 status = U_MEMORY_ALLOCATION_ERROR;
1175 deleteTransitionRules();
1176 return;
1177 }
1178 // For now, use ID + "(STD)" as the name
1179 stdRule = new AnnualTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0,
1180 dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR);
1181
1182 //Check for Null pointer
1183 if (stdRule == NULL) {
1184 status = U_MEMORY_ALLOCATION_ERROR;
1185 deleteTransitionRules();
1186 return;
1187 }
1188
1189 // Calculate the first STD start time
1190 stdRule->getFirstStart(getRawOffset(), dstRule->getDSTSavings(), firstStdStart);
1191
1192 // Create a TimeZoneRule for initial time
1193 if (firstStdStart < firstDstStart) {
1194 initialRule = new InitialTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), dstRule->getDSTSavings());
1195 if (initialRule == NULL) {
1196 status = U_MEMORY_ALLOCATION_ERROR;
1197 deleteTransitionRules();
1198 return;
1199 }
1200 firstTransition = new TimeZoneTransition(firstStdStart, *initialRule, *stdRule);
1201 } else {
1202 initialRule = new InitialTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0);
1203 if (initialRule == NULL) {
1204 status = U_MEMORY_ALLOCATION_ERROR;
1205 deleteTransitionRules();
1206 return;
1207 }
1208 firstTransition = new TimeZoneTransition(firstDstStart, *initialRule, *dstRule);
1209 }
1210 if (firstTransition == NULL) {
1211 status = U_MEMORY_ALLOCATION_ERROR;
1212 deleteTransitionRules();
1213 return;
1214 }
1215
1216 } else {
1217 // Create a TimeZoneRule for initial time
1218 initialRule = new InitialTimeZoneRule(tzid, getRawOffset(), 0);
1219 // Check for null pointer.
1220 if (initialRule == NULL) {
1221 status = U_MEMORY_ALLOCATION_ERROR;
1222 deleteTransitionRules();
1223 return;
1224 }
1225 }
1226
1227 transitionRulesInitialized = TRUE;
1228 }
1229
1230 int32_t
countTransitionRules(UErrorCode &) const1231 SimpleTimeZone::countTransitionRules(UErrorCode& /*status*/) const {
1232 return (useDaylight) ? 2 : 0;
1233 }
1234
1235 void
getTimeZoneRules(const InitialTimeZoneRule * & initial,const TimeZoneRule * trsrules[],int32_t & trscount,UErrorCode & status) const1236 SimpleTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
1237 const TimeZoneRule* trsrules[],
1238 int32_t& trscount,
1239 UErrorCode& status) const {
1240 if (U_FAILURE(status)) {
1241 return;
1242 }
1243 checkTransitionRules(status);
1244 if (U_FAILURE(status)) {
1245 return;
1246 }
1247 initial = initialRule;
1248 int32_t cnt = 0;
1249 if (stdRule != NULL) {
1250 if (cnt < trscount) {
1251 trsrules[cnt++] = stdRule;
1252 }
1253 if (cnt < trscount) {
1254 trsrules[cnt++] = dstRule;
1255 }
1256 }
1257 trscount = cnt;
1258 }
1259
1260
1261 U_NAMESPACE_END
1262
1263 #endif /* #if !UCONFIG_NO_FORMATTING */
1264
1265 //eof
1266