Coverage Report

Created: 2025-12-07 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/icu/icu4c/source/i18n/simpletz.cpp
Line
Count
Source
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 char16_t DST_STR[] = {0x0028,0x0044,0x0053,0x0054,0x0029,0}; // "(DST)"
57
static const char16_t 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
244
:   BasicTimeZone(ID),
67
244
    startMonth(0),
68
244
    startDay(0),
69
244
    startDayOfWeek(0),
70
244
    startTime(0),
71
244
    startTimeMode(WALL_TIME),
72
244
    endTimeMode(WALL_TIME),
73
244
    endMonth(0),
74
244
    endDay(0),
75
244
    endDayOfWeek(0),
76
244
    endTime(0),
77
244
    startYear(0),
78
244
    rawOffset(rawOffsetGMT),
79
244
    useDaylight(false),
80
244
    startMode(DOM_MODE),
81
244
    endMode(DOM_MODE),
82
244
    dstSavings(U_MILLIS_PER_HOUR)
83
244
{
84
244
    clearTransitionRules();
85
244
}
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
1.85k
:   BasicTimeZone(ID)
136
1.85k
{
137
1.85k
    clearTransitionRules();
138
1.85k
    construct(rawOffsetGMT,
139
1.85k
              savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
140
1.85k
              savingsStartTime, savingsStartTimeMode,
141
1.85k
              savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
142
1.85k
              savingsEndTime, savingsEndTimeMode,
143
1.85k
              savingsDST, status);
144
1.85k
}
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
1.85k
{
163
1.85k
    this->rawOffset      = rawOffsetGMT;
164
1.85k
    this->startMonth     = savingsStartMonth;
165
1.85k
    this->startDay       = savingsStartDay;
166
1.85k
    this->startDayOfWeek = savingsStartDayOfWeek;
167
1.85k
    this->startTime      = savingsStartTime;
168
1.85k
    this->startTimeMode  = savingsStartTimeMode;
169
1.85k
    this->endMonth       = savingsEndMonth;
170
1.85k
    this->endDay         = savingsEndDay;
171
1.85k
    this->endDayOfWeek   = savingsEndDayOfWeek;
172
1.85k
    this->endTime        = savingsEndTime;
173
1.85k
    this->endTimeMode    = savingsEndTimeMode;
174
1.85k
    this->dstSavings     = savingsDST;
175
1.85k
    this->startYear      = 0;
176
1.85k
    this->startMode      = DOM_MODE;
177
1.85k
    this->endMode        = DOM_MODE;
178
179
1.85k
    decodeRules(status);
180
181
1.85k
    if (savingsDST == 0) {
182
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
183
0
    }
184
1.85k
}
185
186
// -------------------------------------
187
188
SimpleTimeZone::~SimpleTimeZone()
189
188k
{
190
188k
    deleteTransitionRules();
191
188k
}
192
193
// -------------------------------------
194
195
// Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
196
SimpleTimeZone::SimpleTimeZone(const SimpleTimeZone &source)
197
186k
:   BasicTimeZone(source)
198
186k
{
199
186k
    *this = source;
200
186k
}
201
202
// -------------------------------------
203
204
// Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
205
SimpleTimeZone &
206
SimpleTimeZone::operator=(const SimpleTimeZone &right)
207
186k
{
208
186k
    if (this != &right)
209
186k
    {
210
186k
        TimeZone::operator=(right);
211
186k
        rawOffset      = right.rawOffset;
212
186k
        startMonth     = right.startMonth;
213
186k
        startDay       = right.startDay;
214
186k
        startDayOfWeek = right.startDayOfWeek;
215
186k
        startTime      = right.startTime;
216
186k
        startTimeMode  = right.startTimeMode;
217
186k
        startMode      = right.startMode;
218
186k
        endMonth       = right.endMonth;
219
186k
        endDay         = right.endDay;
220
186k
        endDayOfWeek   = right.endDayOfWeek;
221
186k
        endTime        = right.endTime;
222
186k
        endTimeMode    = right.endTimeMode;
223
186k
        endMode        = right.endMode;
224
186k
        startYear      = right.startYear;
225
186k
        dstSavings     = right.dstSavings;
226
186k
        useDaylight    = right.useDaylight;
227
186k
        clearTransitionRules();
228
186k
    }
229
186k
    return *this;
230
186k
}
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
186k
{
249
186k
    return new SimpleTimeZone(*this);
250
186k
}
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 = static_cast<int8_t>(month);
314
0
    startDay = static_cast<int8_t>(dayOfWeekInMonth);
315
0
    startDayOfWeek = static_cast<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 = static_cast<int8_t>(month);
366
0
    endDay = static_cast<int8_t>(dayOfWeekInMonth);
367
0
    endDayOfWeek = static_cast<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
1.42M
{
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
1.42M
    if (month < UCAL_JANUARY
427
1.42M
        || 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
1.42M
    return getOffset(era, year, month, day, dayOfWeek, millis,
436
1.42M
                     Grego::monthLength(year, month),
437
1.42M
                     Grego::previousMonthLength(year, month),
438
1.42M
                     status);
439
1.42M
}
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
1.42M
{
447
1.42M
    if(U_FAILURE(status)) return 0;
448
449
1.42M
    if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC)
450
1.42M
        || month < UCAL_JANUARY
451
1.42M
        || month > UCAL_DECEMBER
452
1.42M
        || day < 1
453
1.42M
        || day > monthLength
454
1.42M
        || dayOfWeek < UCAL_SUNDAY
455
1.42M
        || dayOfWeek > UCAL_SATURDAY
456
1.42M
        || millis < 0
457
1.42M
        || millis >= U_MILLIS_PER_DAY
458
1.42M
        || monthLength < 28
459
1.42M
        || monthLength > 31
460
1.42M
        || prevMonthLength < 28
461
1.42M
        || prevMonthLength > 31) {
462
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
463
0
        return -1;
464
0
    }
465
466
1.42M
    int32_t result = rawOffset;
467
468
    // Bail out if we are before the onset of daylight savings time
469
1.42M
    if(!useDaylight || year < startYear || era != GregorianCalendar::AD) 
470
583k
        return result;
471
472
    // Check for southern hemisphere.  We assume that the start and end
473
    // month are different.
474
846k
    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
846k
    int32_t startCompare = compareToRule(static_cast<int8_t>(month), static_cast<int8_t>(monthLength), static_cast<int8_t>(prevMonthLength),
479
846k
                                         static_cast<int8_t>(day), static_cast<int8_t>(dayOfWeek), millis,
480
846k
                                         startTimeMode == UTC_TIME ? -rawOffset : 0,
481
846k
                                         startMode, startMonth, startDayOfWeek,
482
846k
                                         startDay, startTime);
483
846k
    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
846k
    if(southern != (startCompare >= 0)) {
492
374k
        endCompare = compareToRule(static_cast<int8_t>(month), static_cast<int8_t>(monthLength), static_cast<int8_t>(prevMonthLength),
493
374k
                                   static_cast<int8_t>(day), static_cast<int8_t>(dayOfWeek), millis,
494
374k
                                   endTimeMode == WALL_TIME ? dstSavings :
495
374k
                                    (endTimeMode == UTC_TIME ? -rawOffset : 0),
496
374k
                                   endMode, endMonth, endDayOfWeek,
497
374k
                                   endDay, endTime);
498
374k
    }
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
846k
    if ((!southern && (startCompare >= 0 && endCompare < 0)) ||
505
818k
        (southern && (startCompare >= 0 || endCompare < 0)))
506
433k
        result += dstSavings;
507
508
846k
    return result;
509
1.42M
}
510
511
void
512
SimpleTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingTimeOpt,
513
                                   UTimeZoneLocalOption duplicatedTimeOpt, int32_t& rawOffsetGMT,
514
                                   int32_t& savingsDST, UErrorCode& status) const
515
345k
{
516
345k
    if (U_FAILURE(status)) {
517
0
        return;
518
0
    }
519
520
345k
    rawOffsetGMT = getRawOffset();
521
345k
    int32_t year, millis;
522
345k
    int8_t month, dom, dow;
523
524
345k
    Grego::timeToFields(date, year, month, dom, dow, millis, status);
525
345k
    if (U_FAILURE(status)) return;
526
527
345k
    savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
528
345k
                          static_cast<uint8_t>(dow), millis,
529
345k
                          Grego::monthLength(year, month),
530
345k
                          status) - rawOffsetGMT;
531
345k
    if (U_FAILURE(status)) {
532
0
        return;
533
0
    }
534
535
345k
    UBool recalc = false;
536
537
    // Now we need some adjustment
538
345k
    if (savingsDST > 0) {
539
142k
        if ((nonExistingTimeOpt & kStdDstMask) == kStandard
540
142k
            || ((nonExistingTimeOpt & kStdDstMask) != kDaylight && (nonExistingTimeOpt & kFormerLatterMask) != kLatter)) {
541
142k
            date -= getDSTSavings();
542
142k
            recalc = true;
543
142k
        }
544
203k
    } else {
545
203k
        if ((duplicatedTimeOpt & kStdDstMask) == kDaylight
546
203k
                || ((duplicatedTimeOpt & kStdDstMask) != kStandard && (duplicatedTimeOpt & kFormerLatterMask) == kFormer)) {
547
0
            date -= getDSTSavings();
548
0
            recalc = true;
549
0
        }
550
203k
    }
551
345k
    if (recalc) {
552
142k
        Grego::timeToFields(date, year, month, dom, dow, millis, status);
553
142k
        if (U_FAILURE(status)) return;
554
142k
        savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
555
142k
                          static_cast<uint8_t>(dow), millis,
556
142k
                          Grego::monthLength(year, month),
557
142k
                          status) - rawOffsetGMT;
558
142k
    }
559
345k
}
560
561
// -------------------------------------
562
563
/**
564
 * Compare a given date in the year to a rule. Return 1, 0, or -1, depending
565
 * on whether the date is after, equal to, or before the rule date. The
566
 * millis are compared directly against the ruleMillis, so any
567
 * standard-daylight adjustments must be handled by the caller.
568
 *
569
 * @return  1 if the date is after the rule date, -1 if the date is before
570
 *          the rule date, or 0 if the date is equal to the rule date.
571
 */
572
int32_t 
573
SimpleTimeZone::compareToRule(int8_t month, int8_t monthLen, int8_t prevMonthLen,
574
                              int8_t dayOfMonth,
575
                              int8_t dayOfWeek, int32_t millis, int32_t millisDelta,
576
                              EMode ruleMode, int8_t ruleMonth, int8_t ruleDayOfWeek,
577
                              int8_t ruleDay, int32_t ruleMillis)
578
1.22M
{
579
    // Make adjustments for startTimeMode and endTimeMode
580
1.22M
    millis += millisDelta;
581
1.33M
    while (millis >= U_MILLIS_PER_DAY) {
582
112k
        millis -= U_MILLIS_PER_DAY;
583
112k
        ++dayOfMonth;
584
112k
        dayOfWeek = static_cast<int8_t>(1 + (dayOfWeek % 7)); // dayOfWeek is one-based
585
112k
        if (dayOfMonth > monthLen) {
586
11.2k
            dayOfMonth = 1;
587
            /* When incrementing the month, it is desirable to overflow
588
             * from DECEMBER to DECEMBER+1, since we use the result to
589
             * compare against a real month. Wraparound of the value
590
             * leads to bug 4173604. */
591
11.2k
            ++month;
592
11.2k
        }
593
112k
    }
594
1.33M
    while (millis < 0) {
595
112k
        millis += U_MILLIS_PER_DAY;
596
112k
        --dayOfMonth;
597
112k
        dayOfWeek = static_cast<int8_t>(1 + ((dayOfWeek + 5) % 7)); // dayOfWeek is one-based
598
112k
        if (dayOfMonth < 1) {
599
59.7k
            dayOfMonth = prevMonthLen;
600
59.7k
            --month;
601
59.7k
        }
602
112k
    }
603
604
    // first compare months.  If they're different, we don't have to worry about days
605
    // and times
606
1.22M
    if (month < ruleMonth) return -1;
607
433k
    else if (month > ruleMonth) return 1;
608
609
    // calculate the actual day of month for the rule
610
11.9k
    int32_t ruleDayOfMonth = 0;
611
612
    // Adjust the ruleDay to the monthLen, for non-leap year February 29 rule days.
613
11.9k
    if (ruleDay > monthLen) {
614
540
        ruleDay = monthLen;
615
540
    }
616
617
11.9k
    switch (ruleMode)
618
11.9k
    {
619
    // if the mode is day-of-month, the day of month is given
620
0
    case DOM_MODE:
621
0
        ruleDayOfMonth = ruleDay;
622
0
        break;
623
624
    // if the mode is day-of-week-in-month, calculate the day-of-month from it
625
0
    case DOW_IN_MONTH_MODE:
626
        // In this case ruleDay is the day-of-week-in-month (this code is using
627
        // the dayOfWeek and dayOfMonth parameters to figure out the day-of-week
628
        // of the first day of the month, so it's trusting that they're really
629
        // consistent with each other)
630
0
        if (ruleDay > 0)
631
0
            ruleDayOfMonth = 1 + (ruleDay - 1) * 7 +
632
0
                (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7;
633
        
634
        // if ruleDay is negative (we assume it's not zero here), we have to do
635
        // the same calculation figuring backward from the last day of the month.
636
0
        else
637
0
        {
638
            // (again, this code is trusting that dayOfWeek and dayOfMonth are
639
            // consistent with each other here, since we're using them to figure
640
            // the day of week of the first of the month)
641
0
            ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 -
642
0
                (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7;
643
0
        }
644
0
        break;
645
646
7.44k
    case DOW_GE_DOM_MODE:
647
7.44k
        ruleDayOfMonth = ruleDay +
648
7.44k
            (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7;
649
7.44k
        break;
650
651
4.49k
    case DOW_LE_DOM_MODE:
652
4.49k
        ruleDayOfMonth = ruleDay -
653
4.49k
            (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7;
654
        // Note at this point ruleDayOfMonth may be <1, although it will
655
        // be >=1 for well-formed rules.
656
4.49k
        break;
657
11.9k
    }
658
659
    // now that we have a real day-in-month for the rule, we can compare days...
660
11.9k
    if (dayOfMonth < ruleDayOfMonth) return -1;
661
7.49k
    else if (dayOfMonth > ruleDayOfMonth) return 1;
662
663
    // ...and if they're equal, we compare times
664
1.24k
    if (millis < ruleMillis) return -1;
665
763
    else if (millis > ruleMillis) return 1;
666
320
    else return 0;
667
1.24k
}
668
669
// -------------------------------------
670
671
int32_t
672
SimpleTimeZone::getRawOffset() const
673
1.28M
{
674
1.28M
    return rawOffset;
675
1.28M
}
676
677
// -------------------------------------
678
679
void
680
SimpleTimeZone::setRawOffset(int32_t offsetMillis)
681
0
{
682
0
    rawOffset = offsetMillis;
683
0
    transitionRulesInitialized = false;
684
0
}
685
686
// -------------------------------------
687
688
void 
689
SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST, UErrorCode& status) 
690
0
{
691
0
    if (millisSavedDuringDST == 0) {
692
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
693
0
    }
694
0
    else {
695
0
        dstSavings = millisSavedDuringDST;
696
0
    }
697
0
    transitionRulesInitialized = false;
698
0
}
699
700
// -------------------------------------
701
702
int32_t 
703
SimpleTimeZone::getDSTSavings() const
704
142k
{
705
142k
    return dstSavings;
706
142k
}
707
708
// -------------------------------------
709
710
UBool
711
SimpleTimeZone::useDaylightTime() const
712
0
{
713
0
    return useDaylight;
714
0
}
715
716
// -------------------------------------
717
718
/**
719
 * Overrides TimeZone
720
 * Queries if the given date is in Daylight Savings Time.
721
 */
722
UBool SimpleTimeZone::inDaylightTime(UDate date, UErrorCode& status) const
723
0
{
724
    // This method is wasteful since it creates a new GregorianCalendar and
725
    // deletes it each time it is called.  However, this is a deprecated method
726
    // and provided only for Java compatibility as of 8/6/97 [LIU].
727
0
    if (U_FAILURE(status)) return false;
728
0
    GregorianCalendar *gc = new GregorianCalendar(*this, status);
729
    /* test for nullptr */
730
0
    if (gc == nullptr) {
731
0
        status = U_MEMORY_ALLOCATION_ERROR;
732
0
        return false;
733
0
    }
734
0
    gc->setTime(date, status);
735
0
    UBool result = gc->inDaylightTime(status);
736
0
    delete gc;
737
0
    return result;
738
0
}
739
740
// -------------------------------------
741
742
/**
743
 * Return true if this zone has the same rules and offset as another zone.
744
 * @param other the TimeZone object to be compared with
745
 * @return true if the given zone has the same rules and offset as this one
746
 */
747
UBool 
748
SimpleTimeZone::hasSameRules(const TimeZone& other) const
749
0
{
750
0
    if (this == &other) return true;
751
0
    if (typeid(*this) != typeid(other)) return false;
752
0
    SimpleTimeZone *that = (SimpleTimeZone*)&other;
753
0
    return rawOffset     == that->rawOffset &&
754
0
        useDaylight     == that->useDaylight &&
755
0
        (!useDaylight
756
         // Only check rules if using DST
757
0
         || (dstSavings     == that->dstSavings &&
758
0
             startMode      == that->startMode &&
759
0
             startMonth     == that->startMonth &&
760
0
             startDay       == that->startDay &&
761
0
             startDayOfWeek == that->startDayOfWeek &&
762
0
             startTime      == that->startTime &&
763
0
             startTimeMode  == that->startTimeMode &&
764
0
             endMode        == that->endMode &&
765
0
             endMonth       == that->endMonth &&
766
0
             endDay         == that->endDay &&
767
0
             endDayOfWeek   == that->endDayOfWeek &&
768
0
             endTime        == that->endTime &&
769
0
             endTimeMode    == that->endTimeMode &&
770
0
             startYear      == that->startYear));
771
0
}
772
773
// -------------------------------------
774
775
//----------------------------------------------------------------------
776
// Rule representation
777
//
778
// We represent the following flavors of rules:
779
//       5        the fifth of the month
780
//       lastSun  the last Sunday in the month
781
//       lastMon  the last Monday in the month
782
//       Sun>=8   first Sunday on or after the eighth
783
//       Sun<=25  last Sunday on or before the 25th
784
// This is further complicated by the fact that we need to remain
785
// backward compatible with the 1.1 FCS.  Finally, we need to minimize
786
// API changes.  In order to satisfy these requirements, we support
787
// three representation systems, and we translate between them.
788
//
789
// INTERNAL REPRESENTATION
790
// This is the format SimpleTimeZone objects take after construction or
791
// streaming in is complete.  Rules are represented directly, using an
792
// unencoded format.  We will discuss the start rule only below; the end
793
// rule is analogous.
794
//   startMode      Takes on enumerated values DAY_OF_MONTH,
795
//                  DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
796
//   startDay       The day of the month, or for DOW_IN_MONTH mode, a
797
//                  value indicating which DOW, such as +1 for first,
798
//                  +2 for second, -1 for last, etc.
799
//   startDayOfWeek The day of the week.  Ignored for DAY_OF_MONTH.
800
//
801
// ENCODED REPRESENTATION
802
// This is the format accepted by the constructor and by setStartRule()
803
// and setEndRule().  It uses various combinations of positive, negative,
804
// and zero values to encode the different rules.  This representation
805
// allows us to specify all the different rule flavors without altering
806
// the API.
807
//   MODE              startMonth    startDay    startDayOfWeek
808
//   DOW_IN_MONTH_MODE >=0           !=0         >0
809
//   DOM_MODE          >=0           >0          ==0
810
//   DOW_GE_DOM_MODE   >=0           >0          <0
811
//   DOW_LE_DOM_MODE   >=0           <0          <0
812
//   (no DST)          don't care    ==0         don't care
813
//
814
// STREAMED REPRESENTATION
815
// We must retain binary compatibility with the 1.1 FCS.  The 1.1 code only
816
// handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
817
// flag useDaylight.  When we stream an object out, we translate into an
818
// approximate DOW_IN_MONTH_MODE representation so the object can be parsed
819
// and used by 1.1 code.  Following that, we write out the full
820
// representation separately so that contemporary code can recognize and
821
// parse it.  The full representation is written in a "packed" format,
822
// consisting of a version number, a length, and an array of bytes.  Future
823
// versions of this class may specify different versions.  If they wish to
824
// include additional data, they should do so by storing them after the
825
// packed representation below.
826
//----------------------------------------------------------------------
827
828
/**
829
 * Given a set of encoded rules in startDay and startDayOfMonth, decode
830
 * them and set the startMode appropriately.  Do the same for endDay and
831
 * endDayOfMonth.  Upon entry, the day of week variables may be zero or
832
 * negative, in order to indicate special modes.  The day of month
833
 * variables may also be negative.  Upon exit, the mode variables will be
834
 * set, and the day of week and day of month variables will be positive.
835
 * This method also recognizes a startDay or endDay of zero as indicating
836
 * no DST.
837
 */
838
void 
839
SimpleTimeZone::decodeRules(UErrorCode& status)
840
1.85k
{
841
1.85k
    decodeStartRule(status);
842
1.85k
    decodeEndRule(status);
843
1.85k
}
844
845
/**
846
 * Decode the start rule and validate the parameters.  The parameters are
847
 * expected to be in encoded form, which represents the various rule modes
848
 * by negating or zeroing certain values.  Representation formats are:
849
 * <p>
850
 * <pre>
851
 *            DOW_IN_MONTH  DOM    DOW>=DOM  DOW<=DOM  no DST
852
 *            ------------  -----  --------  --------  ----------
853
 * month       0..11        same    same      same     don't care
854
 * day        -5..5         1..31   1..31    -1..-31   0
855
 * dayOfWeek   1..7         0      -1..-7    -1..-7    don't care
856
 * time        0..ONEDAY    same    same      same     don't care
857
 * </pre>
858
 * The range for month does not include UNDECIMBER since this class is
859
 * really specific to GregorianCalendar, which does not use that month.
860
 * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
861
 * end rule is an exclusive limit point.  That is, the range of times that
862
 * are in DST include those >= the start and < the end.  For this reason,
863
 * it should be possible to specify an end of ONEDAY in order to include the
864
 * entire day.  Although this is equivalent to time 0 of the following day,
865
 * it's not always possible to specify that, for example, on December 31.
866
 * While arguably the start range should still be 0..ONEDAY-1, we keep
867
 * the start and end ranges the same for consistency.
868
 */
869
void 
870
SimpleTimeZone::decodeStartRule(UErrorCode& status) 
871
1.85k
{
872
1.85k
    if(U_FAILURE(status)) return;
873
874
1.85k
    useDaylight = static_cast<UBool>(startDay != 0 && endDay != 0);
875
1.85k
    if (useDaylight && dstSavings == 0) {
876
0
        dstSavings = U_MILLIS_PER_HOUR;
877
0
    }
878
1.85k
    if (startDay != 0) {
879
1.85k
        if (startMonth < UCAL_JANUARY || startMonth > UCAL_DECEMBER) {
880
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
881
0
            return;
882
0
        }
883
1.85k
        if (startTime < 0 || startTime > U_MILLIS_PER_DAY ||
884
1.85k
            startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) {
885
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
886
0
            return;
887
0
        }
888
1.85k
        if (startDayOfWeek == 0) {
889
0
            startMode = DOM_MODE;
890
1.85k
        } else {
891
1.85k
            if (startDayOfWeek > 0) {
892
0
                startMode = DOW_IN_MONTH_MODE;
893
1.85k
            } else {
894
1.85k
                startDayOfWeek = static_cast<int8_t>(-startDayOfWeek);
895
1.85k
                if (startDay > 0) {
896
868
                    startMode = DOW_GE_DOM_MODE;
897
989
                } else {
898
989
                    startDay = static_cast<int8_t>(-startDay);
899
989
                    startMode = DOW_LE_DOM_MODE;
900
989
                }
901
1.85k
            }
902
1.85k
            if (startDayOfWeek > UCAL_SATURDAY) {
903
0
                status = U_ILLEGAL_ARGUMENT_ERROR;
904
0
                return;
905
0
            }
906
1.85k
        }
907
1.85k
        if (startMode == DOW_IN_MONTH_MODE) {
908
0
            if (startDay < -5 || startDay > 5) {
909
0
                status = U_ILLEGAL_ARGUMENT_ERROR;
910
0
                return;
911
0
            }
912
1.85k
        } else if (startDay<1 || startDay > STATICMONTHLENGTH[startMonth]) {
913
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
914
0
            return;
915
0
        }
916
1.85k
    }
917
1.85k
}
918
919
/**
920
 * Decode the end rule and validate the parameters.  This method is exactly
921
 * analogous to decodeStartRule().
922
 * @see decodeStartRule
923
 */
924
void 
925
SimpleTimeZone::decodeEndRule(UErrorCode& status) 
926
1.85k
{
927
1.85k
    if(U_FAILURE(status)) return;
928
929
1.85k
    useDaylight = static_cast<UBool>(startDay != 0 && endDay != 0);
930
1.85k
    if (useDaylight && dstSavings == 0) {
931
0
        dstSavings = U_MILLIS_PER_HOUR;
932
0
    }
933
1.85k
    if (endDay != 0) {
934
1.85k
        if (endMonth < UCAL_JANUARY || endMonth > UCAL_DECEMBER) {
935
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
936
0
            return;
937
0
        }
938
1.85k
        if (endTime < 0 || endTime > U_MILLIS_PER_DAY ||
939
1.85k
            endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) {
940
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
941
0
            return;
942
0
        }
943
1.85k
        if (endDayOfWeek == 0) {
944
0
            endMode = DOM_MODE;
945
1.85k
        } else {
946
1.85k
            if (endDayOfWeek > 0) {
947
0
                endMode = DOW_IN_MONTH_MODE;
948
1.85k
            } else {
949
1.85k
                endDayOfWeek = static_cast<int8_t>(-endDayOfWeek);
950
1.85k
                if (endDay > 0) {
951
919
                    endMode = DOW_GE_DOM_MODE;
952
938
                } else {
953
938
                    endDay = static_cast<int8_t>(-endDay);
954
938
                    endMode = DOW_LE_DOM_MODE;
955
938
                }
956
1.85k
            }
957
1.85k
            if (endDayOfWeek > UCAL_SATURDAY) {
958
0
                status = U_ILLEGAL_ARGUMENT_ERROR;
959
0
                return;
960
0
            }
961
1.85k
        }
962
1.85k
        if (endMode == DOW_IN_MONTH_MODE) {
963
0
            if (endDay < -5 || endDay > 5) {
964
0
                status = U_ILLEGAL_ARGUMENT_ERROR;
965
0
                return;
966
0
            }
967
1.85k
        } else if (endDay<1 || endDay > STATICMONTHLENGTH[endMonth]) {
968
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
969
0
            return;
970
0
        }
971
1.85k
    }
972
1.85k
}
973
974
UBool
975
0
SimpleTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
976
0
    if (!useDaylight) {
977
0
        return false;
978
0
    }
979
980
0
    UErrorCode status = U_ZERO_ERROR;
981
0
    checkTransitionRules(status);
982
0
    if (U_FAILURE(status)) {
983
0
        return false;
984
0
    }
985
986
0
    UDate firstTransitionTime = firstTransition->getTime();
987
0
    if (base < firstTransitionTime || (inclusive && base == firstTransitionTime)) {
988
0
        result = *firstTransition;
989
0
    }
990
0
    UDate stdDate, dstDate;
991
0
    UBool stdAvail = stdRule->getNextStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
992
0
    UBool dstAvail = dstRule->getNextStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
993
0
    if (stdAvail && (!dstAvail || stdDate < dstDate)) {
994
0
        result.setTime(stdDate);
995
0
        result.setFrom(*dstRule);
996
0
        result.setTo(*stdRule);
997
0
        return true;
998
0
    }
999
0
    if (dstAvail && (!stdAvail || dstDate < stdDate)) {
1000
0
        result.setTime(dstDate);
1001
0
        result.setFrom(*stdRule);
1002
0
        result.setTo(*dstRule);
1003
0
        return true;
1004
0
    }
1005
0
    return false;
1006
0
}
1007
1008
UBool
1009
0
SimpleTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
1010
0
    if (!useDaylight) {
1011
0
        return false;
1012
0
    }
1013
1014
0
    UErrorCode status = U_ZERO_ERROR;
1015
0
    checkTransitionRules(status);
1016
0
    if (U_FAILURE(status)) {
1017
0
        return false;
1018
0
    }
1019
1020
0
    UDate firstTransitionTime = firstTransition->getTime();
1021
0
    if (base < firstTransitionTime || (!inclusive && base == firstTransitionTime)) {
1022
0
        return false;
1023
0
    }
1024
0
    UDate stdDate, dstDate;
1025
0
    UBool stdAvail = stdRule->getPreviousStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
1026
0
    UBool dstAvail = dstRule->getPreviousStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
1027
0
    if (stdAvail && (!dstAvail || stdDate > dstDate)) {
1028
0
        result.setTime(stdDate);
1029
0
        result.setFrom(*dstRule);
1030
0
        result.setTo(*stdRule);
1031
0
        return true;
1032
0
    }
1033
0
    if (dstAvail && (!stdAvail || dstDate > stdDate)) {
1034
0
        result.setTime(dstDate);
1035
0
        result.setFrom(*stdRule);
1036
0
        result.setTo(*dstRule);
1037
0
        return true;
1038
0
    }
1039
0
    return false;
1040
0
}
1041
1042
void
1043
377k
SimpleTimeZone::clearTransitionRules() {
1044
377k
    initialRule = nullptr;
1045
377k
    firstTransition = nullptr;
1046
377k
    stdRule = nullptr;
1047
377k
    dstRule = nullptr;
1048
377k
    transitionRulesInitialized = false;
1049
377k
}
1050
1051
void
1052
188k
SimpleTimeZone::deleteTransitionRules() {
1053
188k
    delete initialRule;
1054
188k
    delete firstTransition;
1055
188k
    delete stdRule;
1056
188k
    delete dstRule;
1057
188k
    clearTransitionRules();
1058
188k
 }
1059
1060
/*
1061
 * Lazy transition rules initializer
1062
 *
1063
 *    Note On the removal of UMTX_CHECK from checkTransitionRules():
1064
 *
1065
 *         It would be faster to have a UInitOnce as part of a SimpleTimeZone object,
1066
 *         which would avoid needing to lock a mutex to check the initialization state.
1067
 *         But we can't easily because simpletz.h is a public header, and including
1068
 *         a UInitOnce as a member of SimpleTimeZone would publicly expose internal ICU headers.
1069
 *
1070
 *         Alternatively we could have a pointer to a UInitOnce in the SimpleTimeZone object,
1071
 *         allocate it in the constructors. This would be a more intrusive change, but doable
1072
 *         if performance turns out to be an issue.
1073
 */
1074
1075
void
1076
0
SimpleTimeZone::checkTransitionRules(UErrorCode& status) const {
1077
0
    if (U_FAILURE(status)) {
1078
0
        return;
1079
0
    }
1080
0
    static UMutex gLock;
1081
0
    umtx_lock(&gLock);
1082
0
    if (!transitionRulesInitialized) {
1083
0
        SimpleTimeZone *ncThis = const_cast<SimpleTimeZone*>(this);
1084
0
        ncThis->initTransitionRules(status);
1085
0
    }
1086
0
    umtx_unlock(&gLock);
1087
0
}
1088
1089
void
1090
0
SimpleTimeZone::initTransitionRules(UErrorCode& status) {
1091
0
    if (U_FAILURE(status)) {
1092
0
        return;
1093
0
    }
1094
0
    if (transitionRulesInitialized) {
1095
0
        return;
1096
0
    }
1097
0
    deleteTransitionRules();
1098
0
    UnicodeString tzid;
1099
0
    getID(tzid);
1100
1101
0
    if (useDaylight) {
1102
0
        DateTimeRule* dtRule;
1103
0
        DateTimeRule::TimeRuleType timeRuleType;
1104
0
        UDate firstStdStart, firstDstStart;
1105
1106
        // Create a TimeZoneRule for daylight saving time
1107
0
        timeRuleType = (startTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME :
1108
0
            ((startTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME);
1109
0
        switch (startMode) {
1110
0
        case DOM_MODE:
1111
0
            dtRule = new DateTimeRule(startMonth, startDay, startTime, timeRuleType);
1112
0
            break;
1113
0
        case DOW_IN_MONTH_MODE:
1114
0
            dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, startTime, timeRuleType);
1115
0
            break;
1116
0
        case DOW_GE_DOM_MODE:
1117
0
            dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, true, startTime, timeRuleType);
1118
0
            break;
1119
0
        case DOW_LE_DOM_MODE:
1120
0
            dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, false, startTime, timeRuleType);
1121
0
            break;
1122
0
        default:
1123
0
            status = U_INVALID_STATE_ERROR;
1124
0
            return;
1125
0
        }
1126
        // Check for Null pointer
1127
0
        if (dtRule == nullptr) {
1128
0
            status = U_MEMORY_ALLOCATION_ERROR;
1129
0
            return;
1130
0
        }
1131
        // For now, use ID + "(DST)" as the name
1132
0
        dstRule = new AnnualTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), getDSTSavings(),
1133
0
            dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR);
1134
        
1135
        // Check for Null pointer
1136
0
        if (dstRule == nullptr) {
1137
0
            status = U_MEMORY_ALLOCATION_ERROR;
1138
0
            deleteTransitionRules();
1139
0
            return;
1140
0
        }
1141
 
1142
        // Calculate the first DST start time
1143
0
        dstRule->getFirstStart(getRawOffset(), 0, firstDstStart);
1144
1145
        // Create a TimeZoneRule for standard time
1146
0
        timeRuleType = (endTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME :
1147
0
            ((endTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME);
1148
0
        switch (endMode) {
1149
0
        case DOM_MODE:
1150
0
            dtRule = new DateTimeRule(endMonth, endDay, endTime, timeRuleType);
1151
0
            break;
1152
0
        case DOW_IN_MONTH_MODE:
1153
0
            dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, endTime, timeRuleType);
1154
0
            break;
1155
0
        case DOW_GE_DOM_MODE:
1156
0
            dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, true, endTime, timeRuleType);
1157
0
            break;
1158
0
        case DOW_LE_DOM_MODE:
1159
0
            dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, false, endTime, timeRuleType);
1160
0
            break;
1161
0
        }
1162
        
1163
        // Check for Null pointer
1164
0
        if (dtRule == nullptr) {
1165
0
            status = U_MEMORY_ALLOCATION_ERROR;
1166
0
            deleteTransitionRules();
1167
0
            return;
1168
0
        }
1169
        // For now, use ID + "(STD)" as the name
1170
0
        stdRule = new AnnualTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0,
1171
0
            dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR);
1172
        
1173
        //Check for Null pointer
1174
0
        if (stdRule == nullptr) {
1175
0
            status = U_MEMORY_ALLOCATION_ERROR;
1176
0
            deleteTransitionRules();
1177
0
            return;
1178
0
        }
1179
1180
        // Calculate the first STD start time
1181
0
        stdRule->getFirstStart(getRawOffset(), dstRule->getDSTSavings(), firstStdStart);
1182
1183
        // Create a TimeZoneRule for initial time
1184
0
        if (firstStdStart < firstDstStart) {
1185
0
            initialRule = new InitialTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), dstRule->getDSTSavings());
1186
0
            if (initialRule == nullptr) {
1187
0
                status = U_MEMORY_ALLOCATION_ERROR;
1188
0
                deleteTransitionRules();
1189
0
                return;
1190
0
            }
1191
0
            firstTransition = new TimeZoneTransition(firstStdStart, *initialRule, *stdRule);
1192
0
        } else {
1193
0
            initialRule = new InitialTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0);
1194
0
            if (initialRule == nullptr) {
1195
0
                status = U_MEMORY_ALLOCATION_ERROR;
1196
0
                deleteTransitionRules();
1197
0
                return;
1198
0
            }
1199
0
            firstTransition = new TimeZoneTransition(firstDstStart, *initialRule, *dstRule);
1200
0
        }
1201
0
        if (firstTransition == nullptr) {
1202
0
            status = U_MEMORY_ALLOCATION_ERROR;
1203
0
            deleteTransitionRules();
1204
0
            return;
1205
0
        }
1206
        
1207
0
    } else {
1208
        // Create a TimeZoneRule for initial time
1209
0
        initialRule = new InitialTimeZoneRule(tzid, getRawOffset(), 0);
1210
        // Check for null pointer.
1211
0
        if (initialRule == nullptr) {
1212
0
            status = U_MEMORY_ALLOCATION_ERROR;
1213
0
            deleteTransitionRules();
1214
0
            return;
1215
0
        }
1216
0
    }
1217
1218
0
    transitionRulesInitialized = true;
1219
0
}
1220
1221
int32_t
1222
0
SimpleTimeZone::countTransitionRules(UErrorCode& /*status*/) const {
1223
0
    return (useDaylight) ? 2 : 0;
1224
0
}
1225
1226
void
1227
SimpleTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
1228
                                 const TimeZoneRule* trsrules[],
1229
                                 int32_t& trscount,
1230
0
                                 UErrorCode& status) const {
1231
0
    if (U_FAILURE(status)) {
1232
0
        return;
1233
0
    }
1234
0
    checkTransitionRules(status);
1235
0
    if (U_FAILURE(status)) {
1236
0
        return;
1237
0
    }
1238
0
    initial = initialRule;
1239
0
    int32_t cnt = 0;
1240
0
    if (stdRule != nullptr) {
1241
0
        if (cnt < trscount) {
1242
0
            trsrules[cnt++] = stdRule;
1243
0
        }
1244
0
        if (cnt < trscount) {
1245
0
            trsrules[cnt++] = dstRule;
1246
0
        }
1247
0
    }
1248
0
    trscount = cnt;
1249
0
}
1250
1251
1252
U_NAMESPACE_END
1253
1254
#endif /* #if !UCONFIG_NO_FORMATTING */
1255
1256
//eof