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 UBool
operator ==(const TimeZone & that) const235 SimpleTimeZone::operator==(const TimeZone& that) const
236 {
237     return ((this == &that) ||
238             (typeid(*this) == typeid(that) &&
239             TimeZone::operator==(that) &&
240             hasSameRules(that)));
241 }
242 
243 // -------------------------------------
244 
245 // Called by TimeZone::createDefault() inside a Mutex - be careful.
246 SimpleTimeZone*
clone() const247 SimpleTimeZone::clone() const
248 {
249     return new SimpleTimeZone(*this);
250 }
251 
252 // -------------------------------------
253 
254 /**
255  * Sets the daylight savings starting year, that is, the year this time zone began
256  * observing its specified daylight savings time rules.  The time zone is considered
257  * not to observe daylight savings time prior to that year; SimpleTimeZone doesn't
258  * support historical daylight-savings-time rules.
259  * @param year the daylight savings starting year.
260  */
261 void
setStartYear(int32_t year)262 SimpleTimeZone::setStartYear(int32_t year)
263 {
264     startYear = year;
265     transitionRulesInitialized = FALSE;
266 }
267 
268 // -------------------------------------
269 
270 /**
271  * Sets the daylight savings starting rule. For example, in the U.S., Daylight Savings
272  * Time starts at the first Sunday in April, at 2 AM in standard time.
273  * Therefore, you can set the start rule by calling:
274  * setStartRule(TimeFields.APRIL, 1, TimeFields.SUNDAY, 2*60*60*1000);
275  * The dayOfWeekInMonth and dayOfWeek parameters together specify how to calculate
276  * the exact starting date.  Their exact meaning depend on their respective signs,
277  * allowing various types of rules to be constructed, as follows:<ul>
278  *   <li>If both dayOfWeekInMonth and dayOfWeek are positive, they specify the
279  *       day of week in the month (e.g., (2, WEDNESDAY) is the second Wednesday
280  *       of the month).
281  *   <li>If dayOfWeek is positive and dayOfWeekInMonth is negative, they specify
282  *       the day of week in the month counting backward from the end of the month.
283  *       (e.g., (-1, MONDAY) is the last Monday in the month)
284  *   <li>If dayOfWeek is zero and dayOfWeekInMonth is positive, dayOfWeekInMonth
285  *       specifies the day of the month, regardless of what day of the week it is.
286  *       (e.g., (10, 0) is the tenth day of the month)
287  *   <li>If dayOfWeek is zero and dayOfWeekInMonth is negative, dayOfWeekInMonth
288  *       specifies the day of the month counting backward from the end of the
289  *       month, regardless of what day of the week it is (e.g., (-2, 0) is the
290  *       next-to-last day of the month).
291  *   <li>If dayOfWeek is negative and dayOfWeekInMonth is positive, they specify the
292  *       first specified day of the week on or after the specfied day of the month.
293  *       (e.g., (15, -SUNDAY) is the first Sunday after the 15th of the month
294  *       [or the 15th itself if the 15th is a Sunday].)
295  *   <li>If dayOfWeek and DayOfWeekInMonth are both negative, they specify the
296  *       last specified day of the week on or before the specified day of the month.
297  *       (e.g., (-20, -TUESDAY) is the last Tuesday before the 20th of the month
298  *       [or the 20th itself if the 20th is a Tuesday].)</ul>
299  * @param month the daylight savings starting month. Month is 0-based.
300  * eg, 0 for January.
301  * @param dayOfWeekInMonth the daylight savings starting
302  * day-of-week-in-month. Please see the member description for an example.
303  * @param dayOfWeek the daylight savings starting day-of-week. Please see
304  * the member description for an example.
305  * @param time the daylight savings starting time. Please see the member
306  * description for an example.
307  */
308 
309 void
setStartRule(int32_t month,int32_t dayOfWeekInMonth,int32_t dayOfWeek,int32_t time,TimeMode mode,UErrorCode & status)310 SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek,
311                              int32_t time, TimeMode mode, UErrorCode& status)
312 {
313     startMonth     = (int8_t)month;
314     startDay       = (int8_t)dayOfWeekInMonth;
315     startDayOfWeek = (int8_t)dayOfWeek;
316     startTime      = time;
317     startTimeMode  = mode;
318     decodeStartRule(status);
319     transitionRulesInitialized = FALSE;
320 }
321 
322 // -------------------------------------
323 
324 void
setStartRule(int32_t month,int32_t dayOfMonth,int32_t time,TimeMode mode,UErrorCode & status)325 SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth,
326                              int32_t time, TimeMode mode, UErrorCode& status)
327 {
328     setStartRule(month, dayOfMonth, 0, time, mode, status);
329 }
330 
331 // -------------------------------------
332 
333 void
setStartRule(int32_t month,int32_t dayOfMonth,int32_t dayOfWeek,int32_t time,TimeMode mode,UBool after,UErrorCode & status)334 SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
335                              int32_t time, TimeMode mode, UBool after, UErrorCode& status)
336 {
337     setStartRule(month, after ? dayOfMonth : -dayOfMonth,
338                  -dayOfWeek, time, mode, status);
339 }
340 
341 // -------------------------------------
342 
343 /**
344  * Sets the daylight savings ending rule. For example, in the U.S., Daylight
345  * Savings Time ends at the last (-1) Sunday in October, at 2 AM in standard time.
346  * Therefore, you can set the end rule by calling:
347  * setEndRule(TimeFields.OCTOBER, -1, TimeFields.SUNDAY, 2*60*60*1000);
348  * Various other types of rules can be specified by manipulating the dayOfWeek
349  * and dayOfWeekInMonth parameters.  For complete details, see the documentation
350  * for setStartRule().
351  * @param month the daylight savings ending month. Month is 0-based.
352  * eg, 0 for January.
353  * @param dayOfWeekInMonth the daylight savings ending
354  * day-of-week-in-month. See setStartRule() for a complete explanation.
355  * @param dayOfWeek the daylight savings ending day-of-week. See setStartRule()
356  * for a complete explanation.
357  * @param time the daylight savings ending time. Please see the member
358  * description for an example.
359  */
360 
361 void
setEndRule(int32_t month,int32_t dayOfWeekInMonth,int32_t dayOfWeek,int32_t time,TimeMode mode,UErrorCode & status)362 SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek,
363                            int32_t time, TimeMode mode, UErrorCode& status)
364 {
365     endMonth     = (int8_t)month;
366     endDay       = (int8_t)dayOfWeekInMonth;
367     endDayOfWeek = (int8_t)dayOfWeek;
368     endTime      = time;
369     endTimeMode  = mode;
370     decodeEndRule(status);
371     transitionRulesInitialized = FALSE;
372 }
373 
374 // -------------------------------------
375 
376 void
setEndRule(int32_t month,int32_t dayOfMonth,int32_t time,TimeMode mode,UErrorCode & status)377 SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth,
378                            int32_t time, TimeMode mode, UErrorCode& status)
379 {
380     setEndRule(month, dayOfMonth, 0, time, mode, status);
381 }
382 
383 // -------------------------------------
384 
385 void
setEndRule(int32_t month,int32_t dayOfMonth,int32_t dayOfWeek,int32_t time,TimeMode mode,UBool after,UErrorCode & status)386 SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
387                            int32_t time, TimeMode mode, UBool after, UErrorCode& status)
388 {
389     setEndRule(month, after ? dayOfMonth : -dayOfMonth,
390                -dayOfWeek, time, mode, status);
391 }
392 
393 // -------------------------------------
394 
395 int32_t
getOffset(uint8_t era,int32_t year,int32_t month,int32_t day,uint8_t dayOfWeek,int32_t millis,UErrorCode & status) const396 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
397                           uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const
398 {
399     // Check the month before calling Grego::monthLength(). This
400     // duplicates the test that occurs in the 7-argument getOffset(),
401     // however, this is unavoidable. We don't mind because this method, in
402     // fact, should not be called; internal code should always call the
403     // 7-argument getOffset(), and outside code should use Calendar.get(int
404     // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
405     // this method because it's public API. - liu 8/10/98
406     if(month < UCAL_JANUARY || month > UCAL_DECEMBER) {
407         status = U_ILLEGAL_ARGUMENT_ERROR;
408         return 0;
409     }
410 
411     return getOffset(era, year, month, day, dayOfWeek, millis, Grego::monthLength(year, month), status);
412 }
413 
414 int32_t
getOffset(uint8_t era,int32_t year,int32_t month,int32_t day,uint8_t dayOfWeek,int32_t millis,int32_t,UErrorCode & status) const415 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
416                           uint8_t dayOfWeek, int32_t millis,
417                           int32_t /*monthLength*/, UErrorCode& status) const
418 {
419     // Check the month before calling Grego::monthLength(). This
420     // duplicates a test that occurs in the 9-argument getOffset(),
421     // however, this is unavoidable. We don't mind because this method, in
422     // fact, should not be called; internal code should always call the
423     // 9-argument getOffset(), and outside code should use Calendar.get(int
424     // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
425     // this method because it's public API. - liu 8/10/98
426     if (month < UCAL_JANUARY
427         || month > UCAL_DECEMBER) {
428         status = U_ILLEGAL_ARGUMENT_ERROR;
429         return -1;
430     }
431 
432     // We ignore monthLength because it can be derived from year and month.
433     // This is so that February in leap years is calculated correctly.
434     // We keep this argument in this function for backwards compatibility.
435     return getOffset(era, year, month, day, dayOfWeek, millis,
436                      Grego::monthLength(year, month),
437                      Grego::previousMonthLength(year, month),
438                      status);
439 }
440 
441 int32_t
getOffset(uint8_t era,int32_t year,int32_t month,int32_t day,uint8_t dayOfWeek,int32_t millis,int32_t monthLength,int32_t prevMonthLength,UErrorCode & status) const442 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
443                           uint8_t dayOfWeek, int32_t millis,
444                           int32_t monthLength, int32_t prevMonthLength,
445                           UErrorCode& status) const
446 {
447     if(U_FAILURE(status)) return 0;
448 
449     if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC)
450         || month < UCAL_JANUARY
451         || month > UCAL_DECEMBER
452         || day < 1
453         || day > monthLength
454         || dayOfWeek < UCAL_SUNDAY
455         || dayOfWeek > UCAL_SATURDAY
456         || millis < 0
457         || millis >= U_MILLIS_PER_DAY
458         || monthLength < 28
459         || monthLength > 31
460         || prevMonthLength < 28
461         || prevMonthLength > 31) {
462         status = U_ILLEGAL_ARGUMENT_ERROR;
463         return -1;
464     }
465 
466     int32_t result = rawOffset;
467 
468     // Bail out if we are before the onset of daylight savings time
469     if(!useDaylight || year < startYear || era != GregorianCalendar::AD)
470         return result;
471 
472     // Check for southern hemisphere.  We assume that the start and end
473     // month are different.
474     UBool southern = (startMonth > endMonth);
475 
476     // Compare the date to the starting and ending rules.+1 = date>rule, -1
477     // = date<rule, 0 = date==rule.
478     int32_t startCompare = compareToRule((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength,
479                                          (int8_t)day, (int8_t)dayOfWeek, millis,
480                                          startTimeMode == UTC_TIME ? -rawOffset : 0,
481                                          startMode, (int8_t)startMonth, (int8_t)startDayOfWeek,
482                                          (int8_t)startDay, startTime);
483     int32_t endCompare = 0;
484 
485     /* We don't always have to compute endCompare.  For many instances,
486      * startCompare is enough to determine if we are in DST or not.  In the
487      * northern hemisphere, if we are before the start rule, we can't have
488      * DST.  In the southern hemisphere, if we are after the start rule, we
489      * must have DST.  This is reflected in the way the next if statement
490      * (not the one immediately following) short circuits. */
491     if(southern != (startCompare >= 0)) {
492         endCompare = compareToRule((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength,
493                                    (int8_t)day, (int8_t)dayOfWeek, millis,
494                                    endTimeMode == WALL_TIME ? dstSavings :
495                                     (endTimeMode == UTC_TIME ? -rawOffset : 0),
496                                    endMode, (int8_t)endMonth, (int8_t)endDayOfWeek,
497                                    (int8_t)endDay, endTime);
498     }
499 
500     // Check for both the northern and southern hemisphere cases.  We
501     // assume that in the northern hemisphere, the start rule is before the
502     // end rule within the calendar year, and vice versa for the southern
503     // hemisphere.
504     if ((!southern && (startCompare >= 0 && endCompare < 0)) ||
505         (southern && (startCompare >= 0 || endCompare < 0)))
506         result += dstSavings;
507 
508     return result;
509 }
510 
511 void
getOffsetFromLocal(UDate date,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 desirible 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