Coverage Report

Created: 2025-06-24 06:43

/src/icu/source/i18n/rbtz.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) 2007-2013, International Business Machines Corporation and
6
* others. All Rights Reserved.
7
*******************************************************************************
8
*/
9
10
#include "utypeinfo.h"  // for 'typeid' to work
11
12
#include "unicode/utypes.h"
13
14
#if !UCONFIG_NO_FORMATTING
15
16
#include "unicode/rbtz.h"
17
#include "unicode/gregocal.h"
18
#include "uvector.h"
19
#include "gregoimp.h"
20
#include "cmemory.h"
21
#include "umutex.h"
22
23
U_NAMESPACE_BEGIN
24
25
/**
26
 * A struct representing a time zone transition
27
 */
28
struct Transition {
29
    UDate time;
30
    TimeZoneRule* from;
31
    TimeZoneRule* to;
32
};
33
34
0
static UBool compareRules(UVector* rules1, UVector* rules2) {
35
0
    if (rules1 == NULL && rules2 == NULL) {
36
0
        return TRUE;
37
0
    } else if (rules1 == NULL || rules2 == NULL) {
38
0
        return FALSE;
39
0
    }
40
0
    int32_t size = rules1->size();
41
0
    if (size != rules2->size()) {
42
0
        return FALSE;
43
0
    }
44
0
    for (int32_t i = 0; i < size; i++) {
45
0
        TimeZoneRule *r1 = (TimeZoneRule*)rules1->elementAt(i);
46
0
        TimeZoneRule *r2 = (TimeZoneRule*)rules2->elementAt(i);
47
0
        if (*r1 != *r2) {
48
0
            return FALSE;
49
0
        }
50
0
    }
51
0
    return TRUE;
52
0
}
53
54
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(RuleBasedTimeZone)
55
56
RuleBasedTimeZone::RuleBasedTimeZone(const UnicodeString& id, InitialTimeZoneRule* initialRule)
57
0
: BasicTimeZone(id), fInitialRule(initialRule), fHistoricRules(NULL), fFinalRules(NULL),
58
0
  fHistoricTransitions(NULL), fUpToDate(FALSE) {
59
0
}
60
61
RuleBasedTimeZone::RuleBasedTimeZone(const RuleBasedTimeZone& source)
62
0
: BasicTimeZone(source), fInitialRule(source.fInitialRule->clone()),
63
0
  fHistoricTransitions(NULL), fUpToDate(FALSE) {
64
0
    fHistoricRules = copyRules(source.fHistoricRules);
65
0
    fFinalRules = copyRules(source.fFinalRules);
66
0
    if (source.fUpToDate) {
67
0
        UErrorCode status = U_ZERO_ERROR;
68
0
        complete(status);
69
0
    }
70
0
}
71
72
0
RuleBasedTimeZone::~RuleBasedTimeZone() {
73
0
    deleteTransitions();
74
0
    deleteRules();
75
0
}
76
77
RuleBasedTimeZone&
78
0
RuleBasedTimeZone::operator=(const RuleBasedTimeZone& right) {
79
0
    if (*this != right) {
80
0
        BasicTimeZone::operator=(right);
81
0
        deleteRules();
82
0
        fInitialRule = right.fInitialRule->clone();
83
0
        fHistoricRules = copyRules(right.fHistoricRules);
84
0
        fFinalRules = copyRules(right.fFinalRules);
85
0
        deleteTransitions();
86
0
        fUpToDate = FALSE;
87
0
    }
88
0
    return *this;
89
0
}
90
91
bool
92
0
RuleBasedTimeZone::operator==(const TimeZone& that) const {
93
0
    if (this == &that) {
94
0
        return TRUE;
95
0
    }
96
0
    if (typeid(*this) != typeid(that)
97
0
        || BasicTimeZone::operator==(that) == FALSE) {
98
0
        return FALSE;
99
0
    }
100
0
    RuleBasedTimeZone *rbtz = (RuleBasedTimeZone*)&that;
101
0
    if (*fInitialRule != *(rbtz->fInitialRule)) {
102
0
        return FALSE;
103
0
    }
104
0
    if (compareRules(fHistoricRules, rbtz->fHistoricRules)
105
0
        && compareRules(fFinalRules, rbtz->fFinalRules)) {
106
0
        return TRUE;
107
0
    }
108
0
    return FALSE;
109
0
}
110
111
bool
112
0
RuleBasedTimeZone::operator!=(const TimeZone& that) const {
113
0
    return !operator==(that);
114
0
}
115
116
void
117
0
RuleBasedTimeZone::addTransitionRule(TimeZoneRule* rule, UErrorCode& status) {
118
0
    if (U_FAILURE(status)) {
119
0
        return;
120
0
    }
121
0
    AnnualTimeZoneRule* atzrule = dynamic_cast<AnnualTimeZoneRule*>(rule);
122
0
    if (atzrule != NULL && atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) {
123
        // A final rule
124
0
        if (fFinalRules == NULL) {
125
0
            fFinalRules = new UVector(status);
126
0
            if (U_FAILURE(status)) {
127
0
                return;
128
0
            }
129
0
        } else if (fFinalRules->size() >= 2) {
130
            // Cannot handle more than two final rules
131
0
            status = U_INVALID_STATE_ERROR;
132
0
            return;
133
0
        }
134
0
        fFinalRules->addElementX((void*)rule, status);
135
0
    } else {
136
        // Non-final rule
137
0
        if (fHistoricRules == NULL) {
138
0
            fHistoricRules = new UVector(status);
139
0
            if (U_FAILURE(status)) {
140
0
                return;
141
0
            }
142
0
        }
143
0
        fHistoricRules->addElementX((void*)rule, status);
144
0
    }
145
    // Mark dirty, so transitions are recalculated at next complete() call
146
0
    fUpToDate = FALSE;
147
0
}
148
149
150
void
151
0
RuleBasedTimeZone::completeConst(UErrorCode& status) const {
152
0
    static UMutex gLock;
153
0
    if (U_FAILURE(status)) {
154
0
        return;
155
0
    }
156
0
    umtx_lock(&gLock);
157
0
    if (!fUpToDate) {
158
0
        RuleBasedTimeZone *ncThis = const_cast<RuleBasedTimeZone*>(this);
159
0
        ncThis->complete(status);
160
0
    }
161
0
    umtx_unlock(&gLock);
162
0
}
163
164
void
165
0
RuleBasedTimeZone::complete(UErrorCode& status) {
166
0
    if (U_FAILURE(status)) {
167
0
        return;
168
0
    }
169
0
    if (fUpToDate) {
170
0
        return;
171
0
    }
172
    // Make sure either no final rules or a pair of AnnualTimeZoneRules
173
    // are available.
174
0
    if (fFinalRules != NULL && fFinalRules->size() != 2) {
175
0
        status = U_INVALID_STATE_ERROR;
176
0
        return;
177
0
    }
178
179
0
    UBool *done = NULL;
180
    // Create a TimezoneTransition and add to the list
181
0
    if (fHistoricRules != NULL || fFinalRules != NULL) {
182
0
        TimeZoneRule *curRule = fInitialRule;
183
0
        UDate lastTransitionTime = MIN_MILLIS;
184
185
        // Build the transition array which represents historical time zone
186
        // transitions.
187
0
        if (fHistoricRules != NULL && fHistoricRules->size() > 0) {
188
0
            int32_t i;
189
0
            int32_t historicCount = fHistoricRules->size();
190
0
            done = (UBool*)uprv_malloc(sizeof(UBool) * historicCount);
191
0
            if (done == NULL) {
192
0
                status = U_MEMORY_ALLOCATION_ERROR;
193
0
                goto cleanup;
194
0
            }
195
0
            for (i = 0; i < historicCount; i++) {
196
0
                done[i] = FALSE;
197
0
            }
198
0
            while (TRUE) {
199
0
                int32_t curStdOffset = curRule->getRawOffset();
200
0
                int32_t curDstSavings = curRule->getDSTSavings();
201
0
                UDate nextTransitionTime = MAX_MILLIS;
202
0
                TimeZoneRule *nextRule = NULL;
203
0
                TimeZoneRule *r = NULL;
204
0
                UBool avail;
205
0
                UDate tt;
206
0
                UnicodeString curName, name;
207
0
                curRule->getName(curName);
208
209
0
                for (i = 0; i < historicCount; i++) {
210
0
                    if (done[i]) {
211
0
                        continue;
212
0
                    }
213
0
                    r = (TimeZoneRule*)fHistoricRules->elementAt(i);
214
0
                    avail = r->getNextStart(lastTransitionTime, curStdOffset, curDstSavings, false, tt);
215
0
                    if (!avail) {
216
                        // No more transitions from this rule - skip this rule next time
217
0
                        done[i] = TRUE;
218
0
                    } else {
219
0
                        r->getName(name);
220
0
                        if (*r == *curRule ||
221
0
                            (name == curName && r->getRawOffset() == curRule->getRawOffset()
222
0
                            && r->getDSTSavings() == curRule->getDSTSavings())) {
223
0
                            continue;
224
0
                        }
225
0
                        if (tt < nextTransitionTime) {
226
0
                            nextTransitionTime = tt;
227
0
                            nextRule = r;
228
0
                        }
229
0
                    }
230
0
                }
231
232
0
                if (nextRule ==  NULL) {
233
                    // Check if all historic rules are done
234
0
                    UBool bDoneAll = TRUE;
235
0
                    for (int32_t j = 0; j < historicCount; j++) {
236
0
                        if (!done[j]) {
237
0
                            bDoneAll = FALSE;
238
0
                            break;
239
0
                        }
240
0
                    }
241
0
                    if (bDoneAll) {
242
0
                        break;
243
0
                    }
244
0
                }
245
246
0
                if (fFinalRules != NULL) {
247
                    // Check if one of final rules has earlier transition date
248
0
                    for (i = 0; i < 2 /* fFinalRules->size() */; i++) {
249
0
                        TimeZoneRule *fr = (TimeZoneRule*)fFinalRules->elementAt(i);
250
0
                        if (*fr == *curRule) {
251
0
                            continue;
252
0
                        }
253
0
                        r = (TimeZoneRule*)fFinalRules->elementAt(i);
254
0
                        avail = r->getNextStart(lastTransitionTime, curStdOffset, curDstSavings, false, tt);
255
0
                        if (avail) {
256
0
                            if (tt < nextTransitionTime) {
257
0
                                nextTransitionTime = tt;
258
0
                                nextRule = r;
259
0
                            }
260
0
                        }
261
0
                    }
262
0
                }
263
264
0
                if (nextRule == NULL) {
265
                    // Nothing more
266
0
                    break;
267
0
                }
268
269
0
                if (fHistoricTransitions == NULL) {
270
0
                    fHistoricTransitions = new UVector(status);
271
0
                    if (U_FAILURE(status)) {
272
0
                        goto cleanup;
273
0
                    }
274
0
                }
275
0
                Transition *trst = (Transition*)uprv_malloc(sizeof(Transition));
276
0
                if (trst == NULL) {
277
0
                    status = U_MEMORY_ALLOCATION_ERROR;
278
0
                    goto cleanup;
279
0
                }
280
0
                trst->time = nextTransitionTime;
281
0
                trst->from = curRule;
282
0
                trst->to = nextRule;
283
0
                fHistoricTransitions->addElementX(trst, status);
284
0
                if (U_FAILURE(status)) {
285
0
                    goto cleanup;
286
0
                }
287
0
                lastTransitionTime = nextTransitionTime;
288
0
                curRule = nextRule;
289
0
            }
290
0
        }
291
0
        if (fFinalRules != NULL) {
292
0
            if (fHistoricTransitions == NULL) {
293
0
                fHistoricTransitions = new UVector(status);
294
0
                if (U_FAILURE(status)) {
295
0
                    goto cleanup;
296
0
                }
297
0
            }
298
            // Append the first transition for each
299
0
            TimeZoneRule *rule0 = (TimeZoneRule*)fFinalRules->elementAt(0);
300
0
            TimeZoneRule *rule1 = (TimeZoneRule*)fFinalRules->elementAt(1);
301
0
            UDate tt0, tt1;
302
0
            UBool avail0 = rule0->getNextStart(lastTransitionTime, curRule->getRawOffset(), curRule->getDSTSavings(), false, tt0);
303
0
            UBool avail1 = rule1->getNextStart(lastTransitionTime, curRule->getRawOffset(), curRule->getDSTSavings(), false, tt1);
304
0
            if (!avail0 || !avail1) {
305
                // Should not happen, because both rules are permanent
306
0
                status = U_INVALID_STATE_ERROR;
307
0
                goto cleanup;
308
0
            }
309
0
            Transition *final0 = (Transition*)uprv_malloc(sizeof(Transition));
310
0
            if (final0 == NULL) {
311
0
                status = U_MEMORY_ALLOCATION_ERROR;
312
0
                goto cleanup;
313
0
            }
314
0
            Transition *final1 = (Transition*)uprv_malloc(sizeof(Transition));
315
0
            if (final1 == NULL) {
316
0
                uprv_free(final0);
317
0
                status = U_MEMORY_ALLOCATION_ERROR;
318
0
                goto cleanup;
319
0
            }
320
0
            if (tt0 < tt1) {
321
0
                final0->time = tt0;
322
0
                final0->from = curRule;
323
0
                final0->to = rule0;
324
0
                rule1->getNextStart(tt0, rule0->getRawOffset(), rule0->getDSTSavings(), false, final1->time);
325
0
                final1->from = rule0;
326
0
                final1->to = rule1;
327
0
            } else {
328
0
                final0->time = tt1;
329
0
                final0->from = curRule;
330
0
                final0->to = rule1;
331
0
                rule0->getNextStart(tt1, rule1->getRawOffset(), rule1->getDSTSavings(), false, final1->time);
332
0
                final1->from = rule1;
333
0
                final1->to = rule0;
334
0
            }
335
0
            fHistoricTransitions->addElementX(final0, status);
336
0
            if (U_FAILURE(status)) {
337
0
                goto cleanup;
338
0
            }
339
0
            fHistoricTransitions->addElementX(final1, status);
340
0
            if (U_FAILURE(status)) {
341
0
                goto cleanup;
342
0
            }
343
0
        }
344
0
    }
345
0
    fUpToDate = TRUE;
346
0
    if (done != NULL) {
347
0
        uprv_free(done);
348
0
    }
349
0
    return;
350
351
0
cleanup:
352
0
    deleteTransitions();
353
0
    if (done != NULL) {
354
0
        uprv_free(done);
355
0
    }
356
0
    fUpToDate = FALSE;
357
0
}
358
359
RuleBasedTimeZone*
360
0
RuleBasedTimeZone::clone() const {
361
0
    return new RuleBasedTimeZone(*this);
362
0
}
363
364
int32_t
365
RuleBasedTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
366
0
                             uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const {
367
0
    if (U_FAILURE(status)) {
368
0
        return 0;
369
0
    }
370
0
    if (month < UCAL_JANUARY || month > UCAL_DECEMBER) {
371
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
372
0
        return 0;
373
0
    } else {
374
0
        return getOffset(era, year, month, day, dayOfWeek, millis,
375
0
                         Grego::monthLength(year, month), status);
376
0
    }
377
0
}
378
379
int32_t
380
RuleBasedTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
381
                             uint8_t /*dayOfWeek*/, int32_t millis,
382
0
                             int32_t /*monthLength*/, UErrorCode& status) const {
383
    // dayOfWeek and monthLength are unused
384
0
    if (U_FAILURE(status)) {
385
0
        return 0;
386
0
    }
387
0
    if (era == GregorianCalendar::BC) {
388
        // Convert to extended year
389
0
        year = 1 - year;
390
0
    }
391
0
    int32_t rawOffset, dstOffset;
392
0
    UDate time = (UDate)Grego::fieldsToDay(year, month, day) * U_MILLIS_PER_DAY + millis;
393
0
    getOffsetInternal(time, TRUE, kDaylight, kStandard, rawOffset, dstOffset, status);
394
0
    if (U_FAILURE(status)) {
395
0
        return 0;
396
0
    }
397
0
    return (rawOffset + dstOffset);
398
0
}
399
400
void
401
RuleBasedTimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
402
0
                             int32_t& dstOffset, UErrorCode& status) const {
403
0
    getOffsetInternal(date, local, kFormer, kLatter, rawOffset, dstOffset, status);
404
0
}
405
406
void RuleBasedTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingTimeOpt,
407
                                           UTimeZoneLocalOption duplicatedTimeOpt,
408
0
                                           int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) const {
409
0
    getOffsetInternal(date, TRUE, nonExistingTimeOpt, duplicatedTimeOpt, rawOffset, dstOffset, status);
410
0
}
411
412
413
/*
414
 * The internal getOffset implementation
415
 */
416
void
417
RuleBasedTimeZone::getOffsetInternal(UDate date, UBool local,
418
                                     int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt,
419
                                     int32_t& rawOffset, int32_t& dstOffset,
420
0
                                     UErrorCode& status) const {
421
0
    rawOffset = 0;
422
0
    dstOffset = 0;
423
424
0
    if (U_FAILURE(status)) {
425
0
        return;
426
0
    }
427
0
    if (!fUpToDate) {
428
        // Transitions are not yet resolved.  We cannot do it here
429
        // because this method is const.  Thus, do nothing and return
430
        // error status.
431
0
        status = U_INVALID_STATE_ERROR;
432
0
        return;
433
0
    }
434
0
    const TimeZoneRule *rule = NULL;
435
0
    if (fHistoricTransitions == NULL) {
436
0
        rule = fInitialRule;
437
0
    } else {
438
0
        UDate tstart = getTransitionTime((Transition*)fHistoricTransitions->elementAt(0),
439
0
            local, NonExistingTimeOpt, DuplicatedTimeOpt);
440
0
        if (date < tstart) {
441
0
            rule = fInitialRule;
442
0
        } else {
443
0
            int32_t idx = fHistoricTransitions->size() - 1;
444
0
            UDate tend = getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx),
445
0
                local, NonExistingTimeOpt, DuplicatedTimeOpt);
446
0
            if (date > tend) {
447
0
                if (fFinalRules != NULL) {
448
0
                    rule = findRuleInFinal(date, local, NonExistingTimeOpt, DuplicatedTimeOpt);
449
0
                }
450
0
                if (rule == NULL) {
451
                    // no final rules or the given time is before the first transition
452
                    // specified by the final rules -> use the last rule 
453
0
                    rule = ((Transition*)fHistoricTransitions->elementAt(idx))->to;
454
0
                }
455
0
            } else {
456
                // Find a historical transition
457
0
                while (idx >= 0) {
458
0
                    if (date >= getTransitionTime((Transition*)fHistoricTransitions->elementAt(idx),
459
0
                        local, NonExistingTimeOpt, DuplicatedTimeOpt)) {
460
0
                        break;
461
0
                    }
462
0
                    idx--;
463
0
                }
464
0
                rule = ((Transition*)fHistoricTransitions->elementAt(idx))->to;
465
0
            }
466
0
        }
467
0
    }
468
0
    if (rule != NULL) {
469
0
        rawOffset = rule->getRawOffset();
470
0
        dstOffset = rule->getDSTSavings();
471
0
    }
472
0
}
473
474
void
475
0
RuleBasedTimeZone::setRawOffset(int32_t /*offsetMillis*/) {
476
    // We don't support this operation at this moment.
477
    // Nothing to do!
478
0
}
479
480
int32_t
481
0
RuleBasedTimeZone::getRawOffset(void) const {
482
    // Note: This implementation returns standard GMT offset
483
    // as of current time.
484
0
    UErrorCode status = U_ZERO_ERROR;
485
0
    int32_t raw, dst;
486
0
    getOffset(uprv_getUTCtime() * U_MILLIS_PER_SECOND,
487
0
        FALSE, raw, dst, status);
488
0
    return raw;
489
0
}
490
491
UBool
492
0
RuleBasedTimeZone::useDaylightTime(void) const {
493
    // Note: This implementation returns true when
494
    // daylight saving time is used as of now or
495
    // after the next transition.
496
0
    UErrorCode status = U_ZERO_ERROR;
497
0
    UDate now = uprv_getUTCtime() * U_MILLIS_PER_SECOND;
498
0
    int32_t raw, dst;
499
0
    getOffset(now, FALSE, raw, dst, status);
500
0
    if (dst != 0) {
501
0
        return TRUE;
502
0
    }
503
    // If DST is not used now, check if DST is used after the next transition
504
0
    UDate time;
505
0
    TimeZoneRule *from, *to;
506
0
    UBool avail = findNext(now, FALSE, time, from, to);
507
0
    if (avail && to->getDSTSavings() != 0) {
508
0
        return TRUE;
509
0
    }
510
0
    return FALSE;
511
0
}
512
513
UBool
514
0
RuleBasedTimeZone::inDaylightTime(UDate date, UErrorCode& status) const {
515
0
    if (U_FAILURE(status)) {
516
0
        return FALSE;
517
0
    }
518
0
    int32_t raw, dst;
519
0
    getOffset(date, FALSE, raw, dst, status);
520
0
    if (dst != 0) {
521
0
        return TRUE;
522
0
    }
523
0
    return FALSE;
524
0
}
525
526
UBool
527
0
RuleBasedTimeZone::hasSameRules(const TimeZone& other) const {
528
0
    if (this == &other) {
529
0
        return TRUE;
530
0
    }
531
0
    if (typeid(*this) != typeid(other)) {
532
0
        return FALSE;
533
0
    }
534
0
    const RuleBasedTimeZone& that = (const RuleBasedTimeZone&)other;
535
0
    if (*fInitialRule != *(that.fInitialRule)) {
536
0
        return FALSE;
537
0
    }
538
0
    if (compareRules(fHistoricRules, that.fHistoricRules)
539
0
        && compareRules(fFinalRules, that.fFinalRules)) {
540
0
        return TRUE;
541
0
    }
542
0
    return FALSE;
543
0
}
544
545
UBool
546
0
RuleBasedTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
547
0
    UErrorCode status = U_ZERO_ERROR;
548
0
    completeConst(status);
549
0
    if (U_FAILURE(status)) {
550
0
        return FALSE;
551
0
    }
552
0
    UDate transitionTime;
553
0
    TimeZoneRule *fromRule, *toRule;
554
0
    UBool found = findNext(base, inclusive, transitionTime, fromRule, toRule);
555
0
    if (found) {
556
0
        result.setTime(transitionTime);
557
0
        result.setFrom((const TimeZoneRule&)*fromRule);
558
0
        result.setTo((const TimeZoneRule&)*toRule);
559
0
        return TRUE;
560
0
    }
561
0
    return FALSE;
562
0
}
563
564
UBool
565
0
RuleBasedTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
566
0
    UErrorCode status = U_ZERO_ERROR;
567
0
    completeConst(status);
568
0
    if (U_FAILURE(status)) {
569
0
        return FALSE;
570
0
    }
571
0
    UDate transitionTime;
572
0
    TimeZoneRule *fromRule, *toRule;
573
0
    UBool found = findPrev(base, inclusive, transitionTime, fromRule, toRule);
574
0
    if (found) {
575
0
        result.setTime(transitionTime);
576
0
        result.setFrom((const TimeZoneRule&)*fromRule);
577
0
        result.setTo((const TimeZoneRule&)*toRule);
578
0
        return TRUE;
579
0
    }
580
0
    return FALSE;
581
0
}
582
583
int32_t
584
0
RuleBasedTimeZone::countTransitionRules(UErrorCode& /*status*/) const {
585
0
    int32_t count = 0;
586
0
    if (fHistoricRules != NULL) {
587
0
        count += fHistoricRules->size();
588
0
    }
589
0
    if (fFinalRules != NULL) {
590
0
        count += fFinalRules->size();
591
0
    }
592
0
    return count;
593
0
}
594
595
void
596
RuleBasedTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
597
                                    const TimeZoneRule* trsrules[],
598
                                    int32_t& trscount,
599
0
                                    UErrorCode& status) const {
600
0
    if (U_FAILURE(status)) {
601
0
        return;
602
0
    }
603
    // Initial rule
604
0
    initial = fInitialRule;
605
606
    // Transition rules
607
0
    int32_t cnt = 0;
608
0
    int32_t idx;
609
0
    if (fHistoricRules != NULL && cnt < trscount) {
610
0
        int32_t historicCount = fHistoricRules->size();
611
0
        idx = 0;
612
0
        while (cnt < trscount && idx < historicCount) {
613
0
            trsrules[cnt++] = (const TimeZoneRule*)fHistoricRules->elementAt(idx++);
614
0
        }
615
0
    }
616
0
    if (fFinalRules != NULL && cnt < trscount) {
617
0
        int32_t finalCount = fFinalRules->size();
618
0
        idx = 0;
619
0
        while (cnt < trscount && idx < finalCount) {
620
0
            trsrules[cnt++] = (const TimeZoneRule*)fFinalRules->elementAt(idx++);
621
0
        }
622
0
    }
623
    // Set the result length
624
0
    trscount = cnt;
625
0
}
626
627
void
628
0
RuleBasedTimeZone::deleteRules(void) {
629
0
    delete fInitialRule;
630
0
    fInitialRule = NULL;
631
0
    if (fHistoricRules != NULL) {
632
0
        while (!fHistoricRules->isEmpty()) {
633
0
            delete (TimeZoneRule*)(fHistoricRules->orphanElementAt(0));
634
0
        }
635
0
        delete fHistoricRules;
636
0
        fHistoricRules = NULL;
637
0
    }
638
0
    if (fFinalRules != NULL) {
639
0
        while (!fFinalRules->isEmpty()) {
640
0
            delete (AnnualTimeZoneRule*)(fFinalRules->orphanElementAt(0));
641
0
        }
642
0
        delete fFinalRules;
643
0
        fFinalRules = NULL;
644
0
    }
645
0
}
646
647
void
648
0
RuleBasedTimeZone::deleteTransitions(void) {
649
0
    if (fHistoricTransitions != NULL) {
650
0
        while (!fHistoricTransitions->isEmpty()) {
651
0
            Transition *trs = (Transition*)fHistoricTransitions->orphanElementAt(0);
652
0
            uprv_free(trs);
653
0
        }
654
0
        delete fHistoricTransitions;
655
0
    }
656
0
    fHistoricTransitions = NULL;
657
0
}
658
659
UVector*
660
0
RuleBasedTimeZone::copyRules(UVector* source) {
661
0
    if (source == NULL) {
662
0
        return NULL;
663
0
    }
664
0
    UErrorCode ec = U_ZERO_ERROR;
665
0
    int32_t size = source->size();
666
0
    UVector *rules = new UVector(size, ec);
667
0
    if (U_FAILURE(ec)) {
668
0
        return NULL;
669
0
    }
670
0
    int32_t i;
671
0
    for (i = 0; i < size; i++) {
672
0
        rules->addElementX(((TimeZoneRule*)source->elementAt(i))->clone(), ec);
673
0
        if (U_FAILURE(ec)) {
674
0
            break;
675
0
        }
676
0
    }
677
0
    if (U_FAILURE(ec)) {
678
        // In case of error, clean up
679
0
        for (i = 0; i < rules->size(); i++) {
680
0
            TimeZoneRule *rule = (TimeZoneRule*)rules->orphanElementAt(i);
681
0
            delete rule;
682
0
        }
683
0
        delete rules;
684
0
        return NULL;
685
0
    }
686
0
    return rules;
687
0
}
688
689
TimeZoneRule*
690
RuleBasedTimeZone::findRuleInFinal(UDate date, UBool local,
691
0
                                   int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const {
692
0
    if (fFinalRules == NULL) {
693
0
        return NULL;
694
0
    }
695
696
0
    AnnualTimeZoneRule* fr0 = (AnnualTimeZoneRule*)fFinalRules->elementAt(0);
697
0
    AnnualTimeZoneRule* fr1 = (AnnualTimeZoneRule*)fFinalRules->elementAt(1);
698
0
    if (fr0 == NULL || fr1 == NULL) {
699
0
        return NULL;
700
0
    }
701
702
0
    UDate start0, start1;
703
0
    UDate base;
704
0
    int32_t localDelta;
705
706
0
    base = date;
707
0
    if (local) {
708
0
        localDelta = getLocalDelta(fr1->getRawOffset(), fr1->getDSTSavings(),
709
0
                                   fr0->getRawOffset(), fr0->getDSTSavings(),
710
0
                                   NonExistingTimeOpt, DuplicatedTimeOpt);
711
0
        base -= localDelta;
712
0
    }
713
0
    UBool avail0 = fr0->getPreviousStart(base, fr1->getRawOffset(), fr1->getDSTSavings(), TRUE, start0);
714
715
0
    base = date;
716
0
    if (local) {
717
0
        localDelta = getLocalDelta(fr0->getRawOffset(), fr0->getDSTSavings(),
718
0
                                   fr1->getRawOffset(), fr1->getDSTSavings(),
719
0
                                   NonExistingTimeOpt, DuplicatedTimeOpt);
720
0
        base -= localDelta;
721
0
    }
722
0
    UBool avail1 = fr1->getPreviousStart(base, fr0->getRawOffset(), fr0->getDSTSavings(), TRUE, start1);
723
724
0
    if (!avail0 || !avail1) {
725
0
        if (avail0) {
726
0
            return fr0;
727
0
        } else if (avail1) {
728
0
            return fr1;
729
0
        }
730
        // Both rules take effect after the given time
731
0
        return NULL;
732
0
    }
733
734
0
    return (start0 > start1) ? fr0 : fr1;
735
0
}
736
737
UBool
738
RuleBasedTimeZone::findNext(UDate base, UBool inclusive, UDate& transitionTime,
739
0
                            TimeZoneRule*& fromRule, TimeZoneRule*& toRule) const {
740
0
    if (fHistoricTransitions == NULL) {
741
0
        return FALSE;
742
0
    }
743
0
    UBool isFinal = FALSE;
744
0
    UBool found = FALSE;
745
0
    Transition result;
746
0
    Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0);
747
0
    UDate tt = tzt->time;
748
0
    if (tt > base || (inclusive && tt == base)) {
749
0
        result = *tzt;
750
0
        found = TRUE;
751
0
    } else {
752
0
        int32_t idx = fHistoricTransitions->size() - 1;        
753
0
        tzt = (Transition*)fHistoricTransitions->elementAt(idx);
754
0
        tt = tzt->time;
755
0
        if (inclusive && tt == base) {
756
0
            result = *tzt;
757
0
            found = TRUE;
758
0
        } else if (tt <= base) {
759
0
            if (fFinalRules != NULL) {
760
                // Find a transion time with finalRules
761
0
                TimeZoneRule *r0 = (TimeZoneRule*)fFinalRules->elementAt(0);
762
0
                TimeZoneRule *r1 = (TimeZoneRule*)fFinalRules->elementAt(1);
763
0
                UDate start0, start1;
764
0
                UBool avail0 = r0->getNextStart(base, r1->getRawOffset(), r1->getDSTSavings(), inclusive, start0);
765
0
                UBool avail1 = r1->getNextStart(base, r0->getRawOffset(), r0->getDSTSavings(), inclusive, start1);
766
                //  avail0/avail1 should be always TRUE
767
0
                if (!avail0 && !avail1) {
768
0
                    return FALSE;
769
0
                }
770
0
                if (!avail1 || start0 < start1) {
771
0
                    result.time = start0;
772
0
                    result.from = r1;
773
0
                    result.to = r0;
774
0
                } else {
775
0
                    result.time = start1;
776
0
                    result.from = r0;
777
0
                    result.to = r1;
778
0
                }
779
0
                isFinal = TRUE;
780
0
                found = TRUE;
781
0
            }
782
0
        } else {
783
            // Find a transition within the historic transitions
784
0
            idx--;
785
0
            Transition *prev = tzt;
786
0
            while (idx > 0) {
787
0
                tzt = (Transition*)fHistoricTransitions->elementAt(idx);
788
0
                tt = tzt->time;
789
0
                if (tt < base || (!inclusive && tt == base)) {
790
0
                    break;
791
0
                }
792
0
                idx--;
793
0
                prev = tzt;
794
0
            }
795
0
            result.time = prev->time;
796
0
            result.from = prev->from;
797
0
            result.to = prev->to;
798
0
            found = TRUE;
799
0
        }
800
0
    }
801
0
    if (found) {
802
        // For now, this implementation ignore transitions with only zone name changes.
803
0
        if (result.from->getRawOffset() == result.to->getRawOffset()
804
0
            && result.from->getDSTSavings() == result.to->getDSTSavings()) {
805
0
            if (isFinal) {
806
0
                return FALSE;
807
0
            } else {
808
                // No offset changes.  Try next one if not final
809
0
                return findNext(result.time, FALSE /* always exclusive */,
810
0
                    transitionTime, fromRule, toRule);
811
0
            }
812
0
        }
813
0
        transitionTime = result.time;
814
0
        fromRule = result.from;
815
0
        toRule = result.to;
816
0
        return TRUE;
817
0
    }
818
0
    return FALSE;
819
0
}
820
821
UBool
822
RuleBasedTimeZone::findPrev(UDate base, UBool inclusive, UDate& transitionTime,
823
0
                            TimeZoneRule*& fromRule, TimeZoneRule*& toRule) const {
824
0
    if (fHistoricTransitions == NULL) {
825
0
        return FALSE;
826
0
    }
827
0
    UBool found = FALSE;
828
0
    Transition result;
829
0
    Transition *tzt = (Transition*)fHistoricTransitions->elementAt(0);
830
0
    UDate tt = tzt->time;
831
0
    if (inclusive && tt == base) {
832
0
        result = *tzt;
833
0
        found = TRUE;
834
0
    } else if (tt < base) {
835
0
        int32_t idx = fHistoricTransitions->size() - 1;        
836
0
        tzt = (Transition*)fHistoricTransitions->elementAt(idx);
837
0
        tt = tzt->time;
838
0
        if (inclusive && tt == base) {
839
0
            result = *tzt;
840
0
            found = TRUE;
841
0
        } else if (tt < base) {
842
0
            if (fFinalRules != NULL) {
843
                // Find a transion time with finalRules
844
0
                TimeZoneRule *r0 = (TimeZoneRule*)fFinalRules->elementAt(0);
845
0
                TimeZoneRule *r1 = (TimeZoneRule*)fFinalRules->elementAt(1);
846
0
                UDate start0, start1;
847
0
                UBool avail0 = r0->getPreviousStart(base, r1->getRawOffset(), r1->getDSTSavings(), inclusive, start0);
848
0
                UBool avail1 = r1->getPreviousStart(base, r0->getRawOffset(), r0->getDSTSavings(), inclusive, start1);
849
                //  avail0/avail1 should be always TRUE
850
0
                if (!avail0 && !avail1) {
851
0
                    return FALSE;
852
0
                }
853
0
                if (!avail1 || start0 > start1) {
854
0
                    result.time = start0;
855
0
                    result.from = r1;
856
0
                    result.to = r0;
857
0
                } else {
858
0
                    result.time = start1;
859
0
                    result.from = r0;
860
0
                    result.to = r1;
861
0
                }
862
0
            } else {
863
0
                result = *tzt;
864
0
            }
865
0
            found = TRUE;
866
0
        } else {
867
            // Find a transition within the historic transitions
868
0
            idx--;
869
0
            while (idx >= 0) {
870
0
                tzt = (Transition*)fHistoricTransitions->elementAt(idx);
871
0
                tt = tzt->time;
872
0
                if (tt < base || (inclusive && tt == base)) {
873
0
                    break;
874
0
                }
875
0
                idx--;
876
0
            }
877
0
            result = *tzt;
878
0
            found = TRUE;
879
0
        }
880
0
    }
881
0
    if (found) {
882
        // For now, this implementation ignore transitions with only zone name changes.
883
0
        if (result.from->getRawOffset() == result.to->getRawOffset()
884
0
            && result.from->getDSTSavings() == result.to->getDSTSavings()) {
885
            // No offset changes.  Try next one if not final
886
0
            return findPrev(result.time, FALSE /* always exclusive */,
887
0
                transitionTime, fromRule, toRule);
888
0
        }
889
0
        transitionTime = result.time;
890
0
        fromRule = result.from;
891
0
        toRule = result.to;
892
0
        return TRUE;
893
0
    }
894
0
    return FALSE;
895
0
}
896
897
UDate
898
RuleBasedTimeZone::getTransitionTime(Transition* transition, UBool local,
899
0
                                     int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const {
900
0
    UDate time = transition->time;
901
0
    if (local) {
902
0
        time += getLocalDelta(transition->from->getRawOffset(), transition->from->getDSTSavings(),
903
0
                              transition->to->getRawOffset(), transition->to->getDSTSavings(),
904
0
                              NonExistingTimeOpt, DuplicatedTimeOpt);
905
0
    }
906
0
    return time;
907
0
}
908
909
int32_t
910
RuleBasedTimeZone::getLocalDelta(int32_t rawBefore, int32_t dstBefore, int32_t rawAfter, int32_t dstAfter,
911
0
                             int32_t NonExistingTimeOpt, int32_t DuplicatedTimeOpt) const {
912
0
    int32_t delta = 0;
913
914
0
    int32_t offsetBefore = rawBefore + dstBefore;
915
0
    int32_t offsetAfter = rawAfter + dstAfter;
916
917
0
    UBool dstToStd = (dstBefore != 0) && (dstAfter == 0);
918
0
    UBool stdToDst = (dstBefore == 0) && (dstAfter != 0);
919
920
0
    if (offsetAfter - offsetBefore >= 0) {
921
        // Positive transition, which makes a non-existing local time range
922
0
        if (((NonExistingTimeOpt & kStdDstMask) == kStandard && dstToStd)
923
0
                || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
924
0
            delta = offsetBefore;
925
0
        } else if (((NonExistingTimeOpt & kStdDstMask) == kStandard && stdToDst)
926
0
                || ((NonExistingTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
927
0
            delta = offsetAfter;
928
0
        } else if ((NonExistingTimeOpt & kFormerLatterMask) == kLatter) {
929
0
            delta = offsetBefore;
930
0
        } else {
931
            // Interprets the time with rule before the transition,
932
            // default for non-existing time range
933
0
            delta = offsetAfter;
934
0
        }
935
0
    } else {
936
        // Negative transition, which makes a duplicated local time range
937
0
        if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && dstToStd)
938
0
                || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && stdToDst)) {
939
0
            delta = offsetAfter;
940
0
        } else if (((DuplicatedTimeOpt & kStdDstMask) == kStandard && stdToDst)
941
0
                || ((DuplicatedTimeOpt & kStdDstMask) == kDaylight && dstToStd)) {
942
0
            delta = offsetBefore;
943
0
        } else if ((DuplicatedTimeOpt & kFormerLatterMask) == kFormer) {
944
0
            delta = offsetBefore;
945
0
        } else {
946
            // Interprets the time with rule after the transition,
947
            // default for duplicated local time range
948
0
            delta = offsetAfter;
949
0
        }
950
0
    }
951
0
    return delta;
952
0
}
953
954
U_NAMESPACE_END
955
956
#endif /* #if !UCONFIG_NO_FORMATTING */
957
958
//eof
959