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