Coverage Report

Created: 2025-06-24 06:43

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