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