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