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