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