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