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