Coverage Report

Created: 2026-01-22 06:31

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