Coverage Report

Created: 2025-09-05 07:16

/src/icu/icu4c/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
2.27k
: TimeZone() {
25
2.27k
}
26
27
BasicTimeZone::BasicTimeZone(const UnicodeString &id)
28
6.95k
: TimeZone(id) {
29
6.95k
}
30
31
BasicTimeZone::BasicTimeZone(const BasicTimeZone& source)
32
915k
: TimeZone(source) {
33
915k
}
34
35
922k
BasicTimeZone::~BasicTimeZone() {
36
922k
}
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 = nullptr;
135
0
    std = nullptr;
136
0
    dst = nullptr;
137
0
    if (U_FAILURE(status)) {
138
0
        return;
139
0
    }
140
0
    int32_t initialRaw, initialDst;
141
0
    UnicodeString initialName;
142
143
0
    LocalPointer<AnnualTimeZoneRule> ar1;
144
0
    LocalPointer<AnnualTimeZoneRule> ar2;
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, mid;
164
0
            int8_t month, dom, dow;
165
0
            UDate d;
166
167
            // Get local wall time for the next transition time
168
0
            Grego::timeToFields(nextTransitionTime + initialRaw + initialDst,
169
0
                year, month, dom, dow, mid, status);
170
0
            if (U_FAILURE(status)) return;
171
0
            int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
172
            // Create DOW rule
173
0
            DateTimeRule *dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
174
0
            tr.getTo()->getName(name);
175
176
            // Note:  SimpleTimeZone does not support raw offset change.
177
            // So we always use raw offset of the given time for the rule,
178
            // even raw offset is changed.  This will result that the result
179
            // zone to return wrong offset after the transition.
180
            // When we encounter such case, we do not inspect next next
181
            // transition for another rule.
182
0
            ar1.adoptInstead(new AnnualTimeZoneRule(name, initialRaw, tr.getTo()->getDSTSavings(),
183
0
                dtr, year, AnnualTimeZoneRule::MAX_YEAR));
184
185
0
            if (tr.getTo()->getRawOffset() == initialRaw) {
186
                // Get the next next transition
187
0
                avail = getNextTransition(nextTransitionTime, false, tr);
188
0
                if (avail) {
189
                    // Check if the next next transition is either DST->STD or STD->DST
190
                    // and within roughly 1 year from the next transition
191
0
                    if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
192
0
                          || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0))
193
0
                         && nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) {
194
195
                        // Get local wall time for the next transition time
196
0
                        Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
197
0
                            year, month, dom, dow, mid, status);
198
0
                        if (U_FAILURE(status)) return;
199
0
                        weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
200
                        // Generate another DOW rule
201
0
                        dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
202
0
                        tr.getTo()->getName(name);
203
0
                        ar2.adoptInstead(new AnnualTimeZoneRule(name, tr.getTo()->getRawOffset(), tr.getTo()->getDSTSavings(),
204
0
                            dtr, year - 1, AnnualTimeZoneRule::MAX_YEAR));
205
206
                        // Make sure this rule can be applied to the specified date
207
0
                        avail = ar2->getPreviousStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), true, d);
208
0
                        if (!avail || d > date
209
0
                                || initialRaw != tr.getTo()->getRawOffset()
210
0
                                || initialDst != tr.getTo()->getDSTSavings()) {
211
                            // We cannot use this rule as the second transition rule
212
0
                            ar2.adoptInstead(nullptr);
213
0
                        }
214
0
                    }
215
0
                }
216
0
            }
217
0
            if (ar2.isNull()) {
218
                // Try previous transition
219
0
                avail = getPreviousTransition(date, true, tr);
220
0
                if (avail) {
221
                    // Check if the previous transition is either DST->STD or STD->DST.
222
                    // The actual transition time does not matter here.
223
0
                    if ((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0)
224
0
                        || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) {
225
226
                        // Generate another DOW rule
227
0
                        Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(),
228
0
                            year, month, dom, dow, mid, status);
229
0
                        if (U_FAILURE(status)) return;
230
0
                        weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
231
0
                        dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME);
232
0
                        tr.getTo()->getName(name);
233
234
                        // second rule raw/dst offsets should match raw/dst offsets
235
                        // at the given time
236
0
                        ar2.adoptInstead(new AnnualTimeZoneRule(name, initialRaw, initialDst,
237
0
                            dtr, ar1->getStartYear() - 1, AnnualTimeZoneRule::MAX_YEAR));
238
239
                        // Check if this rule start after the first rule after the specified date
240
0
                        avail = ar2->getNextStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), false, d);
241
0
                        if (!avail || d <= nextTransitionTime) {
242
                            // We cannot use this rule as the second transition rule
243
0
                            ar2.adoptInstead(nullptr);
244
0
                        }
245
0
                    }
246
0
                }
247
0
            }
248
0
            if (ar2.isNull()) {
249
                // Cannot find a good pair of AnnualTimeZoneRule
250
0
                ar1.adoptInstead(nullptr);
251
0
            } else {
252
                // The initial rule should represent the rule before the previous transition
253
0
                ar1->getName(initialName);
254
0
                initialRaw = ar1->getRawOffset();
255
0
                initialDst = ar1->getDSTSavings();
256
0
            }
257
0
        }
258
0
    }
259
0
    else {
260
        // Try the previous one
261
0
        avail = getPreviousTransition(date, true, tr);
262
0
        if (avail) {
263
0
            tr.getTo()->getName(initialName);
264
0
            initialRaw = tr.getTo()->getRawOffset();
265
0
            initialDst = tr.getTo()->getDSTSavings();
266
0
        } else {
267
            // No transitions in the past.  Just use the current offsets
268
0
            getOffset(date, false, initialRaw, initialDst, status);
269
0
            if (U_FAILURE(status)) {
270
0
                return;
271
0
            }
272
0
        }
273
0
    }
274
    // Set the initial rule
275
0
    initial = new InitialTimeZoneRule(initialName, initialRaw, initialDst);
276
277
    // Set the standard and daylight saving rules
278
0
    if (ar1.isValid() && ar2.isValid()) {
279
0
        if (ar1->getDSTSavings() != 0) {
280
0
            dst = ar1.orphan();
281
0
            std = ar2.orphan();
282
0
        } else {
283
0
            std = ar1.orphan();
284
0
            dst = ar2.orphan();
285
0
        }
286
0
    }
287
0
}
288
289
void
290
BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial,
291
0
                                     UVector*& transitionRules, UErrorCode& status) const {
292
0
    if (U_FAILURE(status)) {
293
0
        return;
294
0
    }
295
296
0
    const InitialTimeZoneRule *orgini;
297
0
    TimeZoneTransition tzt;
298
0
    bool avail;
299
0
    int32_t ruleCount;
300
0
    TimeZoneRule *r = nullptr;
301
0
    UnicodeString name;
302
0
    int32_t i;
303
0
    UDate time, t;
304
0
    UDate firstStart;
305
0
    UBool bFinalStd = false, bFinalDst = false;
306
307
0
    initial = nullptr;
308
0
    transitionRules = nullptr;
309
310
    // Original transition rules
311
0
    ruleCount = countTransitionRules(status);
312
0
    if (U_FAILURE(status)) {
313
0
        return;
314
0
    }
315
0
    LocalPointer<UVector> orgRules(
316
0
        new UVector(uprv_deleteUObject, nullptr, ruleCount, status), status);
317
0
    if (U_FAILURE(status)) {
318
0
        return;
319
0
    }
320
0
    LocalMemory<const TimeZoneRule *> orgtrs(
321
0
        static_cast<const TimeZoneRule **>(uprv_malloc(sizeof(TimeZoneRule*)*ruleCount)));
322
0
    if (orgtrs.isNull()) {
323
0
        status = U_MEMORY_ALLOCATION_ERROR;
324
0
        return;
325
0
    }
326
0
    getTimeZoneRules(orgini, &orgtrs[0], ruleCount, status);
327
0
    if (U_FAILURE(status)) {
328
0
        return;
329
0
    }
330
0
    for (i = 0; i < ruleCount; i++) {
331
0
        LocalPointer<TimeZoneRule> lpRule(orgtrs[i]->clone(), status);
332
0
        orgRules->adoptElement(lpRule.orphan(), status);
333
0
        if (U_FAILURE(status)) {
334
0
            return;
335
0
        }
336
0
    }
337
338
0
    avail = getPreviousTransition(start, true, tzt);
339
0
    if (!avail) {
340
        // No need to filter out rules only applicable to time before the start
341
0
        initial = orgini->clone();
342
0
        if (initial == nullptr) {
343
0
            status = U_MEMORY_ALLOCATION_ERROR;
344
0
            return;
345
0
        }
346
0
        transitionRules = orgRules.orphan();
347
0
        return;
348
0
    }
349
350
0
    LocalMemory<bool> done(static_cast<bool *>(uprv_malloc(sizeof(bool)*ruleCount)));
351
0
    if (done.isNull()) {
352
0
        status = U_MEMORY_ALLOCATION_ERROR;
353
0
        return;
354
0
    }
355
0
    LocalPointer<UVector> filteredRules(
356
0
        new UVector(uprv_deleteUObject, nullptr, status), status);
357
0
    if (U_FAILURE(status)) {
358
0
        return;
359
0
    }
360
361
    // Create initial rule
362
0
    tzt.getTo()->getName(name);
363
0
    LocalPointer<InitialTimeZoneRule> res_initial(
364
0
        new InitialTimeZoneRule(name, tzt.getTo()->getRawOffset(), tzt.getTo()->getDSTSavings()), status);
365
0
    if (U_FAILURE(status)) {
366
0
        return;
367
0
    }
368
369
    // Mark rules which does not need to be processed
370
0
    for (i = 0; i < ruleCount; i++) {
371
0
        r = static_cast<TimeZoneRule*>(orgRules->elementAt(i));
372
0
        avail = r->getNextStart(start, res_initial->getRawOffset(), res_initial->getDSTSavings(), false, time);
373
0
        done[i] = !avail;
374
0
    }
375
376
0
    time = start;
377
0
    while (!bFinalStd || !bFinalDst) {
378
0
        avail = getNextTransition(time, false, tzt);
379
0
        if (!avail) {
380
0
            break;
381
0
        }
382
0
        UDate updatedTime = tzt.getTime();
383
0
        if (updatedTime == time) {
384
            // Can get here if rules for start & end of daylight time have exactly
385
            // the same time.  
386
            // TODO:  fix getNextTransition() to prevent it?
387
0
            status = U_INVALID_STATE_ERROR;
388
0
            return;
389
0
        }
390
0
        time = updatedTime;
391
 
392
0
        const TimeZoneRule *toRule = tzt.getTo();
393
0
        for (i = 0; i < ruleCount; i++) {
394
0
            r = static_cast<TimeZoneRule*>(orgRules->elementAt(i));
395
0
            if (*r == *toRule) {
396
0
                break;
397
0
            }
398
0
        }
399
0
        if (i >= ruleCount) {
400
            // This case should never happen
401
0
            status = U_INVALID_STATE_ERROR;
402
0
            return;
403
0
        }
404
0
        if (done[i]) {
405
0
            continue;
406
0
        }
407
0
        const TimeArrayTimeZoneRule *tar = dynamic_cast<const TimeArrayTimeZoneRule *>(toRule);
408
0
        const AnnualTimeZoneRule *ar;
409
0
        if (tar != nullptr) {
410
            // Get the previous raw offset and DST savings before the very first start time
411
0
            TimeZoneTransition tzt0;
412
0
            t = start;
413
0
            while (true) {
414
0
                avail = getNextTransition(t, false, tzt0);
415
0
                if (!avail) {
416
0
                    break;
417
0
                }
418
0
                if (*(tzt0.getTo()) == *tar) {
419
0
                    break;
420
0
                }
421
0
                t = tzt0.getTime();
422
0
            }
423
0
            if (avail) {
424
                // Check if the entire start times to be added
425
0
                tar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
426
0
                if (firstStart > start) {
427
                    // Just add the rule as is
428
0
                    LocalPointer<TimeArrayTimeZoneRule> lpTar(tar->clone(), status);
429
0
                    filteredRules->adoptElement(lpTar.orphan(), status);
430
0
                    if (U_FAILURE(status)) {
431
0
                        return;
432
0
                    }
433
0
                } else {
434
                    // Collect transitions after the start time
435
0
                    int32_t startTimes;
436
0
                    DateTimeRule::TimeRuleType timeType;
437
0
                    int32_t idx;
438
439
0
                    startTimes = tar->countStartTimes();
440
0
                    timeType = tar->getTimeType();
441
0
                    for (idx = 0; idx < startTimes; idx++) {
442
0
                        tar->getStartTimeAt(idx, t);
443
0
                        if (timeType == DateTimeRule::STANDARD_TIME) {
444
0
                            t -= tzt.getFrom()->getRawOffset();
445
0
                        }
446
0
                        if (timeType == DateTimeRule::WALL_TIME) {
447
0
                            t -= tzt.getFrom()->getDSTSavings();
448
0
                        }
449
0
                        if (t > start) {
450
0
                            break;
451
0
                        }
452
0
                    }
453
0
                    if (U_FAILURE(status)) {
454
0
                        return;
455
0
                    }
456
0
                    int32_t asize = startTimes - idx;
457
0
                    if (asize > 0) {
458
0
                        LocalMemory<UDate> newTimes(static_cast<UDate *>(uprv_malloc(sizeof(UDate) * asize)));
459
0
                        if (newTimes.isNull()) {
460
0
                            status = U_MEMORY_ALLOCATION_ERROR;
461
0
                            return;
462
0
                        }
463
0
                        for (int32_t newidx = 0; newidx < asize; newidx++) {
464
0
                            tar->getStartTimeAt(idx + newidx, newTimes[newidx]);
465
0
                        }
466
0
                        tar->getName(name);
467
0
                        LocalPointer<TimeArrayTimeZoneRule> newTar(new TimeArrayTimeZoneRule(
468
0
                                name, tar->getRawOffset(), tar->getDSTSavings(), &newTimes[0], asize, timeType), status);
469
0
                        filteredRules->adoptElement(newTar.orphan(), status);
470
0
                        if (U_FAILURE(status)) {
471
0
                            return;
472
0
                        }
473
0
                    }
474
0
                }
475
0
            }
476
0
        } else if ((ar = dynamic_cast<const AnnualTimeZoneRule *>(toRule)) != nullptr) {
477
0
            ar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart);
478
0
            if (firstStart == tzt.getTime()) {
479
                // Just add the rule as is
480
0
                LocalPointer<AnnualTimeZoneRule> arClone(ar->clone(), status);
481
0
                filteredRules->adoptElement(arClone.orphan(), status);
482
0
                if (U_FAILURE(status)) {
483
0
                    return;
484
0
                }
485
0
            } else {
486
                // Calculate the transition year
487
0
                int32_t year = Grego::timeToYear(tzt.getTime(), status);
488
0
                if (U_FAILURE(status)) {
489
0
                    return;
490
0
                }
491
                // Re-create the rule
492
0
                ar->getName(name);
493
0
                LocalPointer<AnnualTimeZoneRule> newAr(new AnnualTimeZoneRule(name, ar->getRawOffset(), ar->getDSTSavings(),
494
0
                    *(ar->getRule()), year, ar->getEndYear()), status);
495
0
                filteredRules->adoptElement(newAr.orphan(), status);
496
0
                if (U_FAILURE(status)) {
497
0
                    return;
498
0
                }
499
0
            }
500
            // check if this is a final rule
501
0
            if (ar->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) {
502
                // After bot final standard and dst rules are processed,
503
                // exit this while loop.
504
0
                if (ar->getDSTSavings() == 0) {
505
0
                    bFinalStd = true;
506
0
                } else {
507
0
                    bFinalDst = true;
508
0
                }
509
0
            }
510
0
        }
511
0
        done[i] = true;
512
0
    }
513
514
    // Set the results
515
0
    initial = res_initial.orphan();
516
0
    transitionRules = filteredRules.orphan();
517
0
}
518
519
void
520
BasicTimeZone::getOffsetFromLocal(UDate /*date*/, UTimeZoneLocalOption /*nonExistingTimeOpt*/,
521
                                  UTimeZoneLocalOption /*duplicatedTimeOpt*/,
522
                                  int32_t& /*rawOffset*/, int32_t& /*dstOffset*/,
523
0
                                  UErrorCode& status) const {
524
0
    if (U_FAILURE(status)) {
525
0
        return;
526
0
    }
527
0
    status = U_UNSUPPORTED_ERROR;
528
0
}
529
530
void BasicTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
531
                                       int32_t& rawOffset, int32_t& dstOffset,
532
0
                                       UErrorCode& status) const {
533
0
    getOffsetFromLocal(date, static_cast<UTimeZoneLocalOption>(nonExistingTimeOpt),
534
0
                       static_cast<UTimeZoneLocalOption>(duplicatedTimeOpt), rawOffset, dstOffset, status);
535
0
}
536
537
U_NAMESPACE_END
538
539
#endif /* #if !UCONFIG_NO_FORMATTING */
540
541
//eof