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