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