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