Coverage Report

Created: 2025-06-24 06:43

/src/icu/source/i18n/basictz.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 "unicode/utypes.h"
11
12
#if !UCONFIG_NO_FORMATTING
13
14
#include "unicode/basictz.h"
15
#include "gregoimp.h"
16
#include "uvector.h"
17
#include "cmemory.h"
18
19
U_NAMESPACE_BEGIN
20
21
0
#define MILLIS_PER_YEAR (365*24*60*60*1000.0)
22
23
BasicTimeZone::BasicTimeZone()
24
0
: TimeZone() {
25
0
}
26
27
BasicTimeZone::BasicTimeZone(const UnicodeString &id)
28
0
: TimeZone(id) {
29
0
}
30
31
BasicTimeZone::BasicTimeZone(const BasicTimeZone& source)
32
0
: TimeZone(source) {
33
0
}
34
35
0
BasicTimeZone::~BasicTimeZone() {
36
0
}
37
38
UBool
39
BasicTimeZone::hasEquivalentTransitions(const BasicTimeZone& tz, UDate start, UDate end,
40
0
                                        UBool ignoreDstAmount, UErrorCode& status) const {
41
0
    if (U_FAILURE(status)) {
42
0
        return FALSE;
43
0
    }
44
0
    if (hasSameRules(tz)) {
45
0
        return TRUE;
46
0
    }
47
    // Check the offsets at the start time
48
0
    int32_t raw1, raw2, dst1, dst2;
49
0
    getOffset(start, FALSE, raw1, dst1, status);
50
0
    if (U_FAILURE(status)) {
51
0
        return FALSE;
52
0
    }
53
0
    tz.getOffset(start, FALSE, raw2, dst2, status);
54
0
    if (U_FAILURE(status)) {
55
0
        return FALSE;
56
0
    }
57
0
    if (ignoreDstAmount) {
58
0
        if ((raw1 + dst1 != raw2 + dst2)
59
0
            || (dst1 != 0 && dst2 == 0)
60
0
            || (dst1 == 0 && dst2 != 0)) {
61
0
            return FALSE;
62
0
        }
63
0
    } else {
64
0
        if (raw1 != raw2 || dst1 != dst2) {
65
0
            return FALSE;
66
0
        }            
67
0
    }
68
    // Check transitions in the range
69
0
    UDate time = start;
70
0
    TimeZoneTransition tr1, tr2;
71
0
    while (TRUE) {
72
0
        UBool avail1 = getNextTransition(time, FALSE, tr1);
73
0
        UBool avail2 = tz.getNextTransition(time, FALSE, tr2);
74
75
0
        if (ignoreDstAmount) {
76
            // Skip a transition which only differ the amount of DST savings
77
0
            while (TRUE) {
78
0
                if (avail1
79
0
                        && tr1.getTime() <= end
80
0
                        && (tr1.getFrom()->getRawOffset() + tr1.getFrom()->getDSTSavings()
81
0
                                == tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings())
82
0
                        && (tr1.getFrom()->getDSTSavings() != 0 && tr1.getTo()->getDSTSavings() != 0)) {
83
0
                    getNextTransition(tr1.getTime(), FALSE, tr1);
84
0
                } else {
85
0
                    break;
86
0
                }
87
0
            }
88
0
            while (TRUE) {
89
0
                if (avail2
90
0
                        && tr2.getTime() <= end
91
0
                        && (tr2.getFrom()->getRawOffset() + tr2.getFrom()->getDSTSavings()
92
0
                                == tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings())
93
0
                        && (tr2.getFrom()->getDSTSavings() != 0 && tr2.getTo()->getDSTSavings() != 0)) {
94
0
                    tz.getNextTransition(tr2.getTime(), FALSE, tr2);
95
0
                } else {
96
0
                    break;
97
0
                }
98
0
            }
99
0
        }
100
101
0
        UBool inRange1 = (avail1 && tr1.getTime() <= end);
102
0
        UBool inRange2 = (avail2 && tr2.getTime() <= end);
103
0
        if (!inRange1 && !inRange2) {
104
            // No more transition in the range
105
0
            break;
106
0
        }
107
0
        if (!inRange1 || !inRange2) {
108
0
            return FALSE;
109
0
        }
110
0
        if (tr1.getTime() != tr2.getTime()) {
111
0
            return FALSE;
112
0
        }
113
0
        if (ignoreDstAmount) {
114
0
            if (tr1.getTo()->getRawOffset() + tr1.getTo()->getDSTSavings()
115
0
                        != tr2.getTo()->getRawOffset() + tr2.getTo()->getDSTSavings()
116
0
                    || (tr1.getTo()->getDSTSavings() != 0 &&  tr2.getTo()->getDSTSavings() == 0)
117
0
                    || (tr1.getTo()->getDSTSavings() == 0 &&  tr2.getTo()->getDSTSavings() != 0)) {
118
0
                return FALSE;
119
0
            }
120
0
        } else {
121
0
            if (tr1.getTo()->getRawOffset() != tr2.getTo()->getRawOffset() ||
122
0
                tr1.getTo()->getDSTSavings() != tr2.getTo()->getDSTSavings()) {
123
0
                return FALSE;
124
0
            }
125
0
        }
126
0
        time = tr1.getTime();
127
0
    }
128
0
    return TRUE;
129
0
}
130
131
void
132
BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial,
133
0
        AnnualTimeZoneRule*& std, AnnualTimeZoneRule*& dst, UErrorCode& status) const {
134
0
    initial = NULL;
135
0
    std = NULL;
136
0
    dst = NULL;
137
0
    if (U_FAILURE(status)) {
138
0
        return;
139
0
    }
140
0
    int32_t initialRaw, initialDst;
141
0
    UnicodeString initialName;
142
143
0
    AnnualTimeZoneRule *ar1 = NULL;
144
0
    AnnualTimeZoneRule *ar2 = NULL;
145
0
    UnicodeString name;
146
147
0
    UBool avail;
148
0
    TimeZoneTransition tr;
149
    // Get the next transition
150
0
    avail = getNextTransition(date, FALSE, tr);
151
0
    if (avail) {
152
0
        tr.getFrom()->getName(initialName);
153
0
        initialRaw = tr.getFrom()->getRawOffset();
154
0
        initialDst = tr.getFrom()->getDSTSavings();
155
156
        // Check if the next transition is either DST->STD or STD->DST and
157
        // within roughly 1 year from the specified date
158
0
        UDate nextTransitionTime = tr.getTime();
159
0
        if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
160
0
              || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0))
161
0
            && (date + MILLIS_PER_YEAR > nextTransitionTime)) {
162
 
163
0
            int32_t year, month, dom, dow, doy, mid;
164
0
            UDate d;
165
166
            // Get local wall time for the next transition time
167
0
            Grego::timeToFields(nextTransitionTime + initialRaw + initialDst,
168
0
                year, month, dom, dow, doy, mid);
169
0
            int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
170
            // Create DOW rule
171
0
            DateTimeRule *dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
172
0
            tr.getTo()->getName(name);
173
174
            // Note:  SimpleTimeZone does not support raw offset change.
175
            // So we always use raw offset of the given time for the rule,
176
            // even raw offset is changed.  This will result that the result
177
            // zone to return wrong offset after the transition.
178
            // When we encounter such case, we do not inspect next next
179
            // transition for another rule.
180
0
            ar1 = new AnnualTimeZoneRule(name, initialRaw, tr.getTo()->getDSTSavings(),
181
0
                dtr, year, AnnualTimeZoneRule::MAX_YEAR);
182
183
0
            if (tr.getTo()->getRawOffset() == initialRaw) {
184
                // Get the next next transition
185
0
                avail = getNextTransition(nextTransitionTime, FALSE, tr);
186
0
                if (avail) {
187
                    // Check if the next next transition is either DST->STD or STD->DST
188
                    // and within roughly 1 year from the next transition
189
0
                    if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
190
0
                          || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0))
191
0
                         && nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) {
192
193
                        // Get local wall time for the next transition time
194
0
                        Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
195
0
                            year, month, dom, dow, doy, mid);
196
0
                        weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
197
                        // Generate another DOW rule
198
0
                        dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
199
0
                        tr.getTo()->getName(name);
200
0
                        ar2 = new AnnualTimeZoneRule(name, tr.getTo()->getRawOffset(), tr.getTo()->getDSTSavings(),
201
0
                            dtr, year - 1, AnnualTimeZoneRule::MAX_YEAR);
202
203
                        // Make sure this rule can be applied to the specified date
204
0
                        avail = ar2->getPreviousStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), TRUE, d);
205
0
                        if (!avail || d > date
206
0
                                || initialRaw != tr.getTo()->getRawOffset()
207
0
                                || initialDst != tr.getTo()->getDSTSavings()) {
208
                            // We cannot use this rule as the second transition rule
209
0
                            delete ar2;
210
0
                            ar2 = NULL;
211
0
                        }
212
0
                    }
213
0
                }
214
0
            }
215
0
            if (ar2 == NULL) {
216
                // Try previous transition
217
0
                avail = getPreviousTransition(date, TRUE, tr);
218
0
                if (avail) {
219
                    // Check if the previous transition is either DST->STD or STD->DST.
220
                    // The actual transition time does not matter here.
221
0
                    if ((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
222
0
                        || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) {
223
224
                        // Generate another DOW rule
225
0
                        Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
226
0
                            year, month, dom, dow, doy, mid);
227
0
                        weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
228
0
                        dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
229
0
                        tr.getTo()->getName(name);
230
231
                        // second rule raw/dst offsets should match raw/dst offsets
232
                        // at the given time
233
0
                        ar2 = new AnnualTimeZoneRule(name, initialRaw, initialDst,
234
0
                            dtr, ar1->getStartYear() - 1, AnnualTimeZoneRule::MAX_YEAR);
235
236
                        // Check if this rule start after the first rule after the specified date
237
0
                        avail = ar2->getNextStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), FALSE, d);
238
0
                        if (!avail || d <= nextTransitionTime) {
239
                            // We cannot use this rule as the second transition rule
240
0
                            delete ar2;
241
0
                            ar2 = NULL;
242
0
                        }
243
0
                    }
244
0
                }
245
0
            }
246
0
            if (ar2 == NULL) {
247
                // Cannot find a good pair of AnnualTimeZoneRule
248
0
                delete ar1;
249
0
                ar1 = NULL;
250
0
            } else {
251
                // The initial rule should represent the rule before the previous transition
252
0
                ar1->getName(initialName);
253
0
                initialRaw = ar1->getRawOffset();
254
0
                initialDst = ar1->getDSTSavings();
255
0
            }
256
0
        }
257
0
    }
258
0
    else {
259
        // Try the previous one
260
0
        avail = getPreviousTransition(date, TRUE, tr);
261
0
        if (avail) {
262
0
            tr.getTo()->getName(initialName);
263
0
            initialRaw = tr.getTo()->getRawOffset();
264
0
            initialDst = tr.getTo()->getDSTSavings();
265
0
        } else {
266
            // No transitions in the past.  Just use the current offsets
267
0
            getOffset(date, FALSE, initialRaw, initialDst, status);
268
0
            if (U_FAILURE(status)) {
269
0
                return;
270
0
            }
271
0
        }
272
0
    }
273
    // Set the initial rule
274
0
    initial = new InitialTimeZoneRule(initialName, initialRaw, initialDst);
275
276
    // Set the standard and daylight saving rules
277
0
    if (ar1 != NULL && ar2 != NULL) {
278
0
        if (ar1->getDSTSavings() != 0) {
279
0
            dst = ar1;
280
0
            std = ar2;
281
0
        } else {
282
0
            std = ar1;
283
0
            dst = ar2;
284
0
        }
285
0
    }
286
0
}
287
288
void
289
BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial,
290
0
                                     UVector*& transitionRules, UErrorCode& status) const {
291
0
    if (U_FAILURE(status)) {
292
0
        return;
293
0
    }
294
295
0
    const InitialTimeZoneRule *orgini;
296
0
    const TimeZoneRule **orgtrs = NULL;
297
0
    TimeZoneTransition tzt;
298
0
    UBool avail;
299
0
    UVector *orgRules = NULL;
300
0
    int32_t ruleCount;
301
0
    TimeZoneRule *r = NULL;
302
0
    UBool *done = NULL;
303
0
    InitialTimeZoneRule *res_initial = NULL;
304
0
    UVector *filteredRules = NULL;
305
0
    UnicodeString name;
306
0
    int32_t i;
307
0
    UDate time, t;
308
0
    UDate *newTimes = NULL;
309
0
    UDate firstStart;
310
0
    UBool bFinalStd = FALSE, bFinalDst = FALSE;
311
312
    // Original transition rules
313
0
    ruleCount = countTransitionRules(status);
314
0
    if (U_FAILURE(status)) {
315
0
        return;
316
0
    }
317
0
    orgRules = new UVector(ruleCount, status);
318
0
    if (U_FAILURE(status)) {
319
0
        return;
320
0
    }
321
0
    orgtrs = (const TimeZoneRule**)uprv_malloc(sizeof(TimeZoneRule*)*ruleCount);
322
0
    if (orgtrs == NULL) {
323
0
        status = U_MEMORY_ALLOCATION_ERROR;
324
0
        goto error;
325
0
    }
326
0
    getTimeZoneRules(orgini, orgtrs, ruleCount, status);
327
0
    if (U_FAILURE(status)) {
328
0
        goto error;
329
0
    }
330
0
    for (i = 0; i < ruleCount; i++) {
331
0
        orgRules->addElementX(orgtrs[i]->clone(), status);
332
0
        if (U_FAILURE(status)) {
333
0
            goto error;
334
0
        }
335
0
    }
336
0
    uprv_free(orgtrs);
337
0
    orgtrs = NULL;
338
339
0
    avail = getPreviousTransition(start, TRUE, tzt);
340
0
    if (!avail) {
341
        // No need to filter out rules only applicable to time before the start
342
0
        initial = orgini->clone();
343
0
        transitionRules = orgRules;
344
0
        return;
345
0
    }
346
347
0
    done = (UBool*)uprv_malloc(sizeof(UBool)*ruleCount);
348
0
    if (done == NULL) {
349
0
        status = U_MEMORY_ALLOCATION_ERROR;
350
0
        goto error;
351
0
    }
352
0
    filteredRules = new UVector(status);
353
0
    if (U_FAILURE(status)) {
354
0
        goto error;
355
0
    }
356
357
    // Create initial rule
358
0
    tzt.getTo()->getName(name);
359
0
    res_initial = new InitialTimeZoneRule(name, tzt.getTo()->getRawOffset(),
360
0
        tzt.getTo()->getDSTSavings());
361
362
    // Mark rules which does not need to be processed
363
0
    for (i = 0; i < ruleCount; i++) {
364
0
        r = (TimeZoneRule*)orgRules->elementAt(i);
365
0
        avail = r->getNextStart(start, res_initial->getRawOffset(), res_initial->getDSTSavings(), FALSE, time);
366
0
        done[i] = !avail;
367
0
    }
368
369
0
    time = start;
370
0
    while (!bFinalStd || !bFinalDst) {
371
0
        avail = getNextTransition(time, FALSE, tzt);
372
0
        if (!avail) {
373
0
            break;
374
0
        }
375
0
        UDate updatedTime = tzt.getTime();
376
0
        if (updatedTime == time) {
377
            // Can get here if rules for start & end of daylight time have exactly
378
            // the same time.  
379
            // TODO:  fix getNextTransition() to prevent it?
380
0
            status = U_INVALID_STATE_ERROR;
381
0
            goto error;
382
0
        }
383
0
        time = updatedTime;
384
 
385
0
        const TimeZoneRule *toRule = tzt.getTo();
386
0
        for (i = 0; i < ruleCount; i++) {
387
0
            r = (TimeZoneRule*)orgRules->elementAt(i);
388
0
            if (*r == *toRule) {
389
0
                break;
390
0
            }
391
0
        }
392
0
        if (i >= ruleCount) {
393
            // This case should never happen
394
0
            status = U_INVALID_STATE_ERROR;
395
0
            goto error;
396
0
        }
397
0
        if (done[i]) {
398
0
            continue;
399
0
        }
400
0
        const TimeArrayTimeZoneRule *tar = dynamic_cast<const TimeArrayTimeZoneRule *>(toRule);
401
0
        const AnnualTimeZoneRule *ar;
402
0
        if (tar != NULL) {
403
            // Get the previous raw offset and DST savings before the very first start time
404
0
            TimeZoneTransition tzt0;
405
0
            t = start;
406
0
            while (TRUE) {
407
0
                avail = getNextTransition(t, FALSE, tzt0);
408
0
                if (!avail) {
409
0
                    break;
410
0
                }
411
0
                if (*(tzt0.getTo()) == *tar) {
412
0
                    break;
413
0
                }
414
0
                t = tzt0.getTime();
415
0
            }
416
0
            if (avail) {
417
                // Check if the entire start times to be added
418
0
                tar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
419
0
                if (firstStart > start) {
420
                    // Just add the rule as is
421
0
                    filteredRules->addElementX(tar->clone(), status);
422
0
                    if (U_FAILURE(status)) {
423
0
                        goto error;
424
0
                    }
425
0
                } else {
426
                    // Collect transitions after the start time
427
0
                    int32_t startTimes;
428
0
                    DateTimeRule::TimeRuleType timeType;
429
0
                    int32_t idx;
430
431
0
                    startTimes = tar->countStartTimes();
432
0
                    timeType = tar->getTimeType();
433
0
                    for (idx = 0; idx < startTimes; idx++) {
434
0
                        tar->getStartTimeAt(idx, t);
435
0
                        if (timeType == DateTimeRule::STANDARD_TIME) {
436
0
                            t -= tzt.getFrom()->getRawOffset();
437
0
                        }
438
0
                        if (timeType == DateTimeRule::WALL_TIME) {
439
0
                            t -= tzt.getFrom()->getDSTSavings();
440
0
                        }
441
0
                        if (t > start) {
442
0
                            break;
443
0
                        }
444
0
                    }
445
0
                    int32_t asize = startTimes - idx;
446
0
                    if (asize > 0) {
447
0
                        newTimes = (UDate*)uprv_malloc(sizeof(UDate) * asize);
448
0
                        if (newTimes == NULL) {
449
0
                            status = U_MEMORY_ALLOCATION_ERROR;
450
0
                            goto error;
451
0
                        }
452
0
                        for (int32_t newidx = 0; newidx < asize; newidx++) {
453
0
                            tar->getStartTimeAt(idx + newidx, newTimes[newidx]);
454
0
                            if (U_FAILURE(status)) {
455
0
                                uprv_free(newTimes);
456
0
                                newTimes = NULL;
457
0
                                goto error;
458
0
                            }
459
0
                        }
460
0
                        tar->getName(name);
461
0
                        TimeArrayTimeZoneRule *newTar = new TimeArrayTimeZoneRule(name,
462
0
                            tar->getRawOffset(), tar->getDSTSavings(), newTimes, asize, timeType);
463
0
                        uprv_free(newTimes);
464
0
                        filteredRules->addElementX(newTar, status);
465
0
                        if (U_FAILURE(status)) {
466
0
                            goto error;
467
0
                        }
468
0
                    }
469
0
                }
470
0
            }
471
0
        } else if ((ar = dynamic_cast<const AnnualTimeZoneRule *>(toRule)) != NULL) {
472
0
            ar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
473
0
            if (firstStart == tzt.getTime()) {
474
                // Just add the rule as is
475
0
                filteredRules->addElementX(ar->clone(), status);
476
0
                if (U_FAILURE(status)) {
477
0
                    goto error;
478
0
                }
479
0
            } else {
480
                // Calculate the transition year
481
0
                int32_t year, month, dom, dow, doy, mid;
482
0
                Grego::timeToFields(tzt.getTime(), year, month, dom, dow, doy, mid);
483
                // Re-create the rule
484
0
                ar->getName(name);
485
0
                AnnualTimeZoneRule *newAr = new AnnualTimeZoneRule(name, ar->getRawOffset(), ar->getDSTSavings(),
486
0
                    *(ar->getRule()), year, ar->getEndYear());
487
0
                filteredRules->addElementX(newAr, status);
488
0
                if (U_FAILURE(status)) {
489
0
                    goto error;
490
0
                }
491
0
            }
492
            // check if this is a final rule
493
0
            if (ar->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) {
494
                // After bot final standard and dst rules are processed,
495
                // exit this while loop.
496
0
                if (ar->getDSTSavings() == 0) {
497
0
                    bFinalStd = TRUE;
498
0
                } else {
499
0
                    bFinalDst = TRUE;
500
0
                }
501
0
            }
502
0
        }
503
0
        done[i] = TRUE;
504
0
    }
505
506
    // Set the results
507
0
    if (orgRules != NULL) {
508
0
        while (!orgRules->isEmpty()) {
509
0
            r = (TimeZoneRule*)orgRules->orphanElementAt(0);
510
0
            delete r;
511
0
        }
512
0
        delete orgRules;
513
0
    }
514
0
    if (done != NULL) {
515
0
        uprv_free(done);
516
0
    }
517
518
0
    initial = res_initial;
519
0
    transitionRules = filteredRules;
520
0
    return;
521
522
0
error:
523
0
    if (orgtrs != NULL) {
524
0
        uprv_free(orgtrs);
525
0
    }
526
0
    if (orgRules != NULL) {
527
0
        while (!orgRules->isEmpty()) {
528
0
            r = (TimeZoneRule*)orgRules->orphanElementAt(0);
529
0
            delete r;
530
0
        }
531
0
        delete orgRules;
532
0
    }
533
0
    if (done != NULL) {
534
0
        if (filteredRules != NULL) {
535
0
            while (!filteredRules->isEmpty()) {
536
0
                r = (TimeZoneRule*)filteredRules->orphanElementAt(0);
537
0
                delete r;
538
0
            }
539
0
            delete filteredRules;
540
0
        }
541
0
        delete res_initial;
542
0
        uprv_free(done);
543
0
    }
544
545
0
    initial = NULL;
546
0
    transitionRules = NULL;
547
0
}
548
549
void
550
BasicTimeZone::getOffsetFromLocal(UDate /*date*/, UTimeZoneLocalOption /*nonExistingTimeOpt*/,
551
                                  UTimeZoneLocalOption /*duplicatedTimeOpt*/,
552
                                  int32_t& /*rawOffset*/, int32_t& /*dstOffset*/,
553
0
                                  UErrorCode& status) const {
554
0
    if (U_FAILURE(status)) {
555
0
        return;
556
0
    }
557
0
    status = U_UNSUPPORTED_ERROR;
558
0
}
559
560
void BasicTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
561
                                       int32_t& rawOffset, int32_t& dstOffset,
562
0
                                       UErrorCode& status) const {
563
0
    getOffsetFromLocal(date, (UTimeZoneLocalOption)nonExistingTimeOpt,
564
0
                       (UTimeZoneLocalOption)duplicatedTimeOpt, rawOffset, dstOffset, status);
565
0
}
566
567
U_NAMESPACE_END
568
569
#endif /* #if !UCONFIG_NO_FORMATTING */
570
571
//eof