Coverage Report

Created: 2018-09-25 14:53

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