1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 * Copyright (C) 1997-2013, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 *******************************************************************************
8 *
9 * File SIMPLETZ.H
10 *
11 * Modification History:
12 *
13 * Date Name Description
14 * 12/05/96 clhuang Creation.
15 * 04/21/97 aliu Fixed miscellaneous bugs found by inspection and
16 * testing.
17 * 07/29/97 aliu Ported source bodies back from Java version with
18 * numerous feature enhancements and bug fixes.
19 * 08/10/98 stephen JDK 1.2 sync.
20 * 09/17/98 stephen Fixed getOffset() for last hour of year and DST
21 * 12/02/99 aliu Added TimeMode and constructor and setStart/EndRule
22 * methods that take TimeMode. Whitespace cleanup.
23 ********************************************************************************
24 */
25
26 #include "utypeinfo.h" // for 'typeid' to work
27
28 #include "unicode/utypes.h"
29
30 #if !UCONFIG_NO_FORMATTING
31
32 #include "unicode/simpletz.h"
33 #include "unicode/gregocal.h"
34 #include "unicode/smpdtfmt.h"
35
36 #include "cmemory.h"
37 #include "gregoimp.h"
38 #include "umutex.h"
39
40 U_NAMESPACE_BEGIN
41
42 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleTimeZone)
43
44 // Use only for decodeStartRule() and decodeEndRule() where the year is not
45 // available. Set February to 29 days to accommodate rules with that date
46 // and day-of-week-on-or-before-that-date mode (DOW_LE_DOM_MODE).
47 // The compareToRule() method adjusts to February 28 in non-leap years.
48 //
49 // For actual getOffset() calculations, use Grego::monthLength() and
50 // Grego::previousMonthLength() which take leap years into account.
51 // We handle leap years assuming always
52 // Gregorian, since we know they didn't have daylight time when
53 // Gregorian calendar started.
54 const int8_t SimpleTimeZone::STATICMONTHLENGTH[] = {31,29,31,30,31,30,31,31,30,31,30,31};
55
56 static const char16_t DST_STR[] = {0x0028,0x0044,0x0053,0x0054,0x0029,0}; // "(DST)"
57 static const char16_t STD_STR[] = {0x0028,0x0053,0x0054,0x0044,0x0029,0}; // "(STD)"
58
59
60 // *****************************************************************************
61 // class SimpleTimeZone
62 // *****************************************************************************
63
64
SimpleTimeZone(int32_t rawOffsetGMT,const UnicodeString & ID)65 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID)
66 : BasicTimeZone(ID),
67 startMonth(0),
68 startDay(0),
69 startDayOfWeek(0),
70 startTime(0),
71 startTimeMode(WALL_TIME),
72 endTimeMode(WALL_TIME),
73 endMonth(0),
74 endDay(0),
75 endDayOfWeek(0),
76 endTime(0),
77 startYear(0),
78 rawOffset(rawOffsetGMT),
79 useDaylight(false),
80 startMode(DOM_MODE),
81 endMode(DOM_MODE),
82 dstSavings(U_MILLIS_PER_HOUR)
83 {
84 clearTransitionRules();
85 }
86
87 // -------------------------------------
88
SimpleTimeZone(int32_t rawOffsetGMT,const UnicodeString & ID,int8_t savingsStartMonth,int8_t savingsStartDay,int8_t savingsStartDayOfWeek,int32_t savingsStartTime,int8_t savingsEndMonth,int8_t savingsEndDay,int8_t savingsEndDayOfWeek,int32_t savingsEndTime,UErrorCode & status)89 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID,
90 int8_t savingsStartMonth, int8_t savingsStartDay,
91 int8_t savingsStartDayOfWeek, int32_t savingsStartTime,
92 int8_t savingsEndMonth, int8_t savingsEndDay,
93 int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
94 UErrorCode& status)
95 : BasicTimeZone(ID)
96 {
97 clearTransitionRules();
98 construct(rawOffsetGMT,
99 savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
100 savingsStartTime, WALL_TIME,
101 savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
102 savingsEndTime, WALL_TIME,
103 U_MILLIS_PER_HOUR, status);
104 }
105
106 // -------------------------------------
107
SimpleTimeZone(int32_t rawOffsetGMT,const UnicodeString & ID,int8_t savingsStartMonth,int8_t savingsStartDay,int8_t savingsStartDayOfWeek,int32_t savingsStartTime,int8_t savingsEndMonth,int8_t savingsEndDay,int8_t savingsEndDayOfWeek,int32_t savingsEndTime,int32_t savingsDST,UErrorCode & status)108 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID,
109 int8_t savingsStartMonth, int8_t savingsStartDay,
110 int8_t savingsStartDayOfWeek, int32_t savingsStartTime,
111 int8_t savingsEndMonth, int8_t savingsEndDay,
112 int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
113 int32_t savingsDST, UErrorCode& status)
114 : BasicTimeZone(ID)
115 {
116 clearTransitionRules();
117 construct(rawOffsetGMT,
118 savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
119 savingsStartTime, WALL_TIME,
120 savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
121 savingsEndTime, WALL_TIME,
122 savingsDST, status);
123 }
124
125 // -------------------------------------
126
SimpleTimeZone(int32_t rawOffsetGMT,const UnicodeString & ID,int8_t savingsStartMonth,int8_t savingsStartDay,int8_t savingsStartDayOfWeek,int32_t savingsStartTime,TimeMode savingsStartTimeMode,int8_t savingsEndMonth,int8_t savingsEndDay,int8_t savingsEndDayOfWeek,int32_t savingsEndTime,TimeMode savingsEndTimeMode,int32_t savingsDST,UErrorCode & status)127 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID,
128 int8_t savingsStartMonth, int8_t savingsStartDay,
129 int8_t savingsStartDayOfWeek, int32_t savingsStartTime,
130 TimeMode savingsStartTimeMode,
131 int8_t savingsEndMonth, int8_t savingsEndDay,
132 int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
133 TimeMode savingsEndTimeMode,
134 int32_t savingsDST, UErrorCode& status)
135 : BasicTimeZone(ID)
136 {
137 clearTransitionRules();
138 construct(rawOffsetGMT,
139 savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
140 savingsStartTime, savingsStartTimeMode,
141 savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
142 savingsEndTime, savingsEndTimeMode,
143 savingsDST, status);
144 }
145
146 /**
147 * Internal construction method.
148 */
construct(int32_t rawOffsetGMT,int8_t savingsStartMonth,int8_t savingsStartDay,int8_t savingsStartDayOfWeek,int32_t savingsStartTime,TimeMode savingsStartTimeMode,int8_t savingsEndMonth,int8_t savingsEndDay,int8_t savingsEndDayOfWeek,int32_t savingsEndTime,TimeMode savingsEndTimeMode,int32_t savingsDST,UErrorCode & status)149 void SimpleTimeZone::construct(int32_t rawOffsetGMT,
150 int8_t savingsStartMonth,
151 int8_t savingsStartDay,
152 int8_t savingsStartDayOfWeek,
153 int32_t savingsStartTime,
154 TimeMode savingsStartTimeMode,
155 int8_t savingsEndMonth,
156 int8_t savingsEndDay,
157 int8_t savingsEndDayOfWeek,
158 int32_t savingsEndTime,
159 TimeMode savingsEndTimeMode,
160 int32_t savingsDST,
161 UErrorCode& status)
162 {
163 this->rawOffset = rawOffsetGMT;
164 this->startMonth = savingsStartMonth;
165 this->startDay = savingsStartDay;
166 this->startDayOfWeek = savingsStartDayOfWeek;
167 this->startTime = savingsStartTime;
168 this->startTimeMode = savingsStartTimeMode;
169 this->endMonth = savingsEndMonth;
170 this->endDay = savingsEndDay;
171 this->endDayOfWeek = savingsEndDayOfWeek;
172 this->endTime = savingsEndTime;
173 this->endTimeMode = savingsEndTimeMode;
174 this->dstSavings = savingsDST;
175 this->startYear = 0;
176 this->startMode = DOM_MODE;
177 this->endMode = DOM_MODE;
178
179 decodeRules(status);
180
181 if (savingsDST == 0) {
182 status = U_ILLEGAL_ARGUMENT_ERROR;
183 }
184 }
185
186 // -------------------------------------
187
~SimpleTimeZone()188 SimpleTimeZone::~SimpleTimeZone()
189 {
190 deleteTransitionRules();
191 }
192
193 // -------------------------------------
194
195 // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
SimpleTimeZone(const SimpleTimeZone & source)196 SimpleTimeZone::SimpleTimeZone(const SimpleTimeZone &source)
197 : BasicTimeZone(source)
198 {
199 *this = source;
200 }
201
202 // -------------------------------------
203
204 // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
205 SimpleTimeZone &
operator =(const SimpleTimeZone & right)206 SimpleTimeZone::operator=(const SimpleTimeZone &right)
207 {
208 if (this != &right)
209 {
210 TimeZone::operator=(right);
211 rawOffset = right.rawOffset;
212 startMonth = right.startMonth;
213 startDay = right.startDay;
214 startDayOfWeek = right.startDayOfWeek;
215 startTime = right.startTime;
216 startTimeMode = right.startTimeMode;
217 startMode = right.startMode;
218 endMonth = right.endMonth;
219 endDay = right.endDay;
220 endDayOfWeek = right.endDayOfWeek;
221 endTime = right.endTime;
222 endTimeMode = right.endTimeMode;
223 endMode = right.endMode;
224 startYear = right.startYear;
225 dstSavings = right.dstSavings;
226 useDaylight = right.useDaylight;
227 clearTransitionRules();
228 }
229 return *this;
230 }
231
232 // -------------------------------------
233
234 bool
operator ==(const TimeZone & that) const235 SimpleTimeZone::operator==(const TimeZone& that) const
236 {
237 return ((this == &that) ||
238 (typeid(*this) == typeid(that) &&
239 TimeZone::operator==(that) &&
240 hasSameRules(that)));
241 }
242
243 // -------------------------------------
244
245 // Called by TimeZone::createDefault() inside a Mutex - be careful.
246 SimpleTimeZone*
clone() const247 SimpleTimeZone::clone() const
248 {
249 return new SimpleTimeZone(*this);
250 }
251
252 // -------------------------------------
253
254 /**
255 * Sets the daylight savings starting year, that is, the year this time zone began
256 * observing its specified daylight savings time rules. The time zone is considered
257 * not to observe daylight savings time prior to that year; SimpleTimeZone doesn't
258 * support historical daylight-savings-time rules.
259 * @param year the daylight savings starting year.
260 */
261 void
setStartYear(int32_t year)262 SimpleTimeZone::setStartYear(int32_t year)
263 {
264 startYear = year;
265 transitionRulesInitialized = false;
266 }
267
268 // -------------------------------------
269
270 /**
271 * Sets the daylight savings starting rule. For example, in the U.S., Daylight Savings
272 * Time starts at the first Sunday in April, at 2 AM in standard time.
273 * Therefore, you can set the start rule by calling:
274 * setStartRule(TimeFields.APRIL, 1, TimeFields.SUNDAY, 2*60*60*1000);
275 * The dayOfWeekInMonth and dayOfWeek parameters together specify how to calculate
276 * the exact starting date. Their exact meaning depend on their respective signs,
277 * allowing various types of rules to be constructed, as follows:<ul>
278 * <li>If both dayOfWeekInMonth and dayOfWeek are positive, they specify the
279 * day of week in the month (e.g., (2, WEDNESDAY) is the second Wednesday
280 * of the month).
281 * <li>If dayOfWeek is positive and dayOfWeekInMonth is negative, they specify
282 * the day of week in the month counting backward from the end of the month.
283 * (e.g., (-1, MONDAY) is the last Monday in the month)
284 * <li>If dayOfWeek is zero and dayOfWeekInMonth is positive, dayOfWeekInMonth
285 * specifies the day of the month, regardless of what day of the week it is.
286 * (e.g., (10, 0) is the tenth day of the month)
287 * <li>If dayOfWeek is zero and dayOfWeekInMonth is negative, dayOfWeekInMonth
288 * specifies the day of the month counting backward from the end of the
289 * month, regardless of what day of the week it is (e.g., (-2, 0) is the
290 * next-to-last day of the month).
291 * <li>If dayOfWeek is negative and dayOfWeekInMonth is positive, they specify the
292 * first specified day of the week on or after the specified day of the month.
293 * (e.g., (15, -SUNDAY) is the first Sunday after the 15th of the month
294 * [or the 15th itself if the 15th is a Sunday].)
295 * <li>If dayOfWeek and DayOfWeekInMonth are both negative, they specify the
296 * last specified day of the week on or before the specified day of the month.
297 * (e.g., (-20, -TUESDAY) is the last Tuesday before the 20th of the month
298 * [or the 20th itself if the 20th is a Tuesday].)</ul>
299 * @param month the daylight savings starting month. Month is 0-based.
300 * eg, 0 for January.
301 * @param dayOfWeekInMonth the daylight savings starting
302 * day-of-week-in-month. Please see the member description for an example.
303 * @param dayOfWeek the daylight savings starting day-of-week. Please see
304 * the member description for an example.
305 * @param time the daylight savings starting time. Please see the member
306 * description for an example.
307 */
308
309 void
setStartRule(int32_t month,int32_t dayOfWeekInMonth,int32_t dayOfWeek,int32_t time,TimeMode mode,UErrorCode & status)310 SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek,
311 int32_t time, TimeMode mode, UErrorCode& status)
312 {
313 startMonth = static_cast<int8_t>(month);
314 startDay = static_cast<int8_t>(dayOfWeekInMonth);
315 startDayOfWeek = static_cast<int8_t>(dayOfWeek);
316 startTime = time;
317 startTimeMode = mode;
318 decodeStartRule(status);
319 transitionRulesInitialized = false;
320 }
321
322 // -------------------------------------
323
324 void
setStartRule(int32_t month,int32_t dayOfMonth,int32_t time,TimeMode mode,UErrorCode & status)325 SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth,
326 int32_t time, TimeMode mode, UErrorCode& status)
327 {
328 setStartRule(month, dayOfMonth, 0, time, mode, status);
329 }
330
331 // -------------------------------------
332
333 void
setStartRule(int32_t month,int32_t dayOfMonth,int32_t dayOfWeek,int32_t time,TimeMode mode,UBool after,UErrorCode & status)334 SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
335 int32_t time, TimeMode mode, UBool after, UErrorCode& status)
336 {
337 setStartRule(month, after ? dayOfMonth : -dayOfMonth,
338 -dayOfWeek, time, mode, status);
339 }
340
341 // -------------------------------------
342
343 /**
344 * Sets the daylight savings ending rule. For example, in the U.S., Daylight
345 * Savings Time ends at the last (-1) Sunday in October, at 2 AM in standard time.
346 * Therefore, you can set the end rule by calling:
347 * setEndRule(TimeFields.OCTOBER, -1, TimeFields.SUNDAY, 2*60*60*1000);
348 * Various other types of rules can be specified by manipulating the dayOfWeek
349 * and dayOfWeekInMonth parameters. For complete details, see the documentation
350 * for setStartRule().
351 * @param month the daylight savings ending month. Month is 0-based.
352 * eg, 0 for January.
353 * @param dayOfWeekInMonth the daylight savings ending
354 * day-of-week-in-month. See setStartRule() for a complete explanation.
355 * @param dayOfWeek the daylight savings ending day-of-week. See setStartRule()
356 * for a complete explanation.
357 * @param time the daylight savings ending time. Please see the member
358 * description for an example.
359 */
360
361 void
setEndRule(int32_t month,int32_t dayOfWeekInMonth,int32_t dayOfWeek,int32_t time,TimeMode mode,UErrorCode & status)362 SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek,
363 int32_t time, TimeMode mode, UErrorCode& status)
364 {
365 endMonth = static_cast<int8_t>(month);
366 endDay = static_cast<int8_t>(dayOfWeekInMonth);
367 endDayOfWeek = static_cast<int8_t>(dayOfWeek);
368 endTime = time;
369 endTimeMode = mode;
370 decodeEndRule(status);
371 transitionRulesInitialized = false;
372 }
373
374 // -------------------------------------
375
376 void
setEndRule(int32_t month,int32_t dayOfMonth,int32_t time,TimeMode mode,UErrorCode & status)377 SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth,
378 int32_t time, TimeMode mode, UErrorCode& status)
379 {
380 setEndRule(month, dayOfMonth, 0, time, mode, status);
381 }
382
383 // -------------------------------------
384
385 void
setEndRule(int32_t month,int32_t dayOfMonth,int32_t dayOfWeek,int32_t time,TimeMode mode,UBool after,UErrorCode & status)386 SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
387 int32_t time, TimeMode mode, UBool after, UErrorCode& status)
388 {
389 setEndRule(month, after ? dayOfMonth : -dayOfMonth,
390 -dayOfWeek, time, mode, status);
391 }
392
393 // -------------------------------------
394
395 int32_t
getOffset(uint8_t era,int32_t year,int32_t month,int32_t day,uint8_t dayOfWeek,int32_t millis,UErrorCode & status) const396 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
397 uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const
398 {
399 // Check the month before calling Grego::monthLength(). This
400 // duplicates the test that occurs in the 7-argument getOffset(),
401 // however, this is unavoidable. We don't mind because this method, in
402 // fact, should not be called; internal code should always call the
403 // 7-argument getOffset(), and outside code should use Calendar.get(int
404 // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
405 // this method because it's public API. - liu 8/10/98
406 if(month < UCAL_JANUARY || month > UCAL_DECEMBER) {
407 status = U_ILLEGAL_ARGUMENT_ERROR;
408 return 0;
409 }
410
411 return getOffset(era, year, month, day, dayOfWeek, millis, Grego::monthLength(year, month), status);
412 }
413
414 int32_t
getOffset(uint8_t era,int32_t year,int32_t month,int32_t day,uint8_t dayOfWeek,int32_t millis,int32_t,UErrorCode & status) const415 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
416 uint8_t dayOfWeek, int32_t millis,
417 int32_t /*monthLength*/, UErrorCode& status) const
418 {
419 // Check the month before calling Grego::monthLength(). This
420 // duplicates a test that occurs in the 9-argument getOffset(),
421 // however, this is unavoidable. We don't mind because this method, in
422 // fact, should not be called; internal code should always call the
423 // 9-argument getOffset(), and outside code should use Calendar.get(int
424 // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
425 // this method because it's public API. - liu 8/10/98
426 if (month < UCAL_JANUARY
427 || month > UCAL_DECEMBER) {
428 status = U_ILLEGAL_ARGUMENT_ERROR;
429 return -1;
430 }
431
432 // We ignore monthLength because it can be derived from year and month.
433 // This is so that February in leap years is calculated correctly.
434 // We keep this argument in this function for backwards compatibility.
435 return getOffset(era, year, month, day, dayOfWeek, millis,
436 Grego::monthLength(year, month),
437 Grego::previousMonthLength(year, month),
438 status);
439 }
440
441 int32_t
getOffset(uint8_t era,int32_t year,int32_t month,int32_t day,uint8_t dayOfWeek,int32_t millis,int32_t monthLength,int32_t prevMonthLength,UErrorCode & status) const442 SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
443 uint8_t dayOfWeek, int32_t millis,
444 int32_t monthLength, int32_t prevMonthLength,
445 UErrorCode& status) const
446 {
447 if(U_FAILURE(status)) return 0;
448
449 if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC)
450 || month < UCAL_JANUARY
451 || month > UCAL_DECEMBER
452 || day < 1
453 || day > monthLength
454 || dayOfWeek < UCAL_SUNDAY
455 || dayOfWeek > UCAL_SATURDAY
456 || millis < 0
457 || millis >= U_MILLIS_PER_DAY
458 || monthLength < 28
459 || monthLength > 31
460 || prevMonthLength < 28
461 || prevMonthLength > 31) {
462 status = U_ILLEGAL_ARGUMENT_ERROR;
463 return -1;
464 }
465
466 int32_t result = rawOffset;
467
468 // Bail out if we are before the onset of daylight savings time
469 if(!useDaylight || year < startYear || era != GregorianCalendar::AD)
470 return result;
471
472 // Check for southern hemisphere. We assume that the start and end
473 // month are different.
474 UBool southern = (startMonth > endMonth);
475
476 // Compare the date to the starting and ending rules.+1 = date>rule, -1
477 // = date<rule, 0 = date==rule.
478 int32_t startCompare = compareToRule(static_cast<int8_t>(month), static_cast<int8_t>(monthLength), static_cast<int8_t>(prevMonthLength),
479 static_cast<int8_t>(day), static_cast<int8_t>(dayOfWeek), millis,
480 startTimeMode == UTC_TIME ? -rawOffset : 0,
481 startMode, startMonth, startDayOfWeek,
482 startDay, startTime);
483 int32_t endCompare = 0;
484
485 /* We don't always have to compute endCompare. For many instances,
486 * startCompare is enough to determine if we are in DST or not. In the
487 * northern hemisphere, if we are before the start rule, we can't have
488 * DST. In the southern hemisphere, if we are after the start rule, we
489 * must have DST. This is reflected in the way the next if statement
490 * (not the one immediately following) short circuits. */
491 if(southern != (startCompare >= 0)) {
492 endCompare = compareToRule(static_cast<int8_t>(month), static_cast<int8_t>(monthLength), static_cast<int8_t>(prevMonthLength),
493 static_cast<int8_t>(day), static_cast<int8_t>(dayOfWeek), millis,
494 endTimeMode == WALL_TIME ? dstSavings :
495 (endTimeMode == UTC_TIME ? -rawOffset : 0),
496 endMode, endMonth, endDayOfWeek,
497 endDay, endTime);
498 }
499
500 // Check for both the northern and southern hemisphere cases. We
501 // assume that in the northern hemisphere, the start rule is before the
502 // end rule within the calendar year, and vice versa for the southern
503 // hemisphere.
504 if ((!southern && (startCompare >= 0 && endCompare < 0)) ||
505 (southern && (startCompare >= 0 || endCompare < 0)))
506 result += dstSavings;
507
508 return result;
509 }
510
511 void
getOffsetFromLocal(UDate date,UTimeZoneLocalOption nonExistingTimeOpt,UTimeZoneLocalOption duplicatedTimeOpt,int32_t & rawOffsetGMT,int32_t & savingsDST,UErrorCode & status) const512 SimpleTimeZone::getOffsetFromLocal(UDate date, UTimeZoneLocalOption nonExistingTimeOpt,
513 UTimeZoneLocalOption duplicatedTimeOpt, int32_t& rawOffsetGMT,
514 int32_t& savingsDST, UErrorCode& status) const
515 {
516 if (U_FAILURE(status)) {
517 return;
518 }
519
520 rawOffsetGMT = getRawOffset();
521 int32_t year, month, dom, dow, millis;
522 double dday = ClockMath::floorDivide(date, U_MILLIS_PER_DAY, &millis);
523 if (dday > INT32_MAX || dday < INT32_MIN) {
524 status = U_ILLEGAL_ARGUMENT_ERROR;
525 return;
526 }
527 int32_t day = dday;
528
529 Grego::dayToFields(day, year, month, dom, dow, status);
530 if (U_FAILURE(status)) return;
531
532 savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
533 static_cast<uint8_t>(dow), millis,
534 Grego::monthLength(year, month),
535 status) - rawOffsetGMT;
536 if (U_FAILURE(status)) {
537 return;
538 }
539
540 UBool recalc = false;
541
542 // Now we need some adjustment
543 if (savingsDST > 0) {
544 if ((nonExistingTimeOpt & kStdDstMask) == kStandard
545 || ((nonExistingTimeOpt & kStdDstMask) != kDaylight && (nonExistingTimeOpt & kFormerLatterMask) != kLatter)) {
546 date -= getDSTSavings();
547 recalc = true;
548 }
549 } else {
550 if ((duplicatedTimeOpt & kStdDstMask) == kDaylight
551 || ((duplicatedTimeOpt & kStdDstMask) != kStandard && (duplicatedTimeOpt & kFormerLatterMask) == kFormer)) {
552 date -= getDSTSavings();
553 recalc = true;
554 }
555 }
556 if (recalc) {
557 day = ClockMath::floorDivide(date, U_MILLIS_PER_DAY, &millis);
558 Grego::dayToFields(day, year, month, dom, dow, status);
559 if (U_FAILURE(status)) return;
560 savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
561 static_cast<uint8_t>(dow), millis,
562 Grego::monthLength(year, month),
563 status) - rawOffsetGMT;
564 }
565 }
566
567 // -------------------------------------
568
569 /**
570 * Compare a given date in the year to a rule. Return 1, 0, or -1, depending
571 * on whether the date is after, equal to, or before the rule date. The
572 * millis are compared directly against the ruleMillis, so any
573 * standard-daylight adjustments must be handled by the caller.
574 *
575 * @return 1 if the date is after the rule date, -1 if the date is before
576 * the rule date, or 0 if the date is equal to the rule date.
577 */
578 int32_t
compareToRule(int8_t month,int8_t monthLen,int8_t prevMonthLen,int8_t dayOfMonth,int8_t dayOfWeek,int32_t millis,int32_t millisDelta,EMode ruleMode,int8_t ruleMonth,int8_t ruleDayOfWeek,int8_t ruleDay,int32_t ruleMillis)579 SimpleTimeZone::compareToRule(int8_t month, int8_t monthLen, int8_t prevMonthLen,
580 int8_t dayOfMonth,
581 int8_t dayOfWeek, int32_t millis, int32_t millisDelta,
582 EMode ruleMode, int8_t ruleMonth, int8_t ruleDayOfWeek,
583 int8_t ruleDay, int32_t ruleMillis)
584 {
585 // Make adjustments for startTimeMode and endTimeMode
586 millis += millisDelta;
587 while (millis >= U_MILLIS_PER_DAY) {
588 millis -= U_MILLIS_PER_DAY;
589 ++dayOfMonth;
590 dayOfWeek = static_cast<int8_t>(1 + (dayOfWeek % 7)); // dayOfWeek is one-based
591 if (dayOfMonth > monthLen) {
592 dayOfMonth = 1;
593 /* When incrementing the month, it is desirable to overflow
594 * from DECEMBER to DECEMBER+1, since we use the result to
595 * compare against a real month. Wraparound of the value
596 * leads to bug 4173604. */
597 ++month;
598 }
599 }
600 while (millis < 0) {
601 millis += U_MILLIS_PER_DAY;
602 --dayOfMonth;
603 dayOfWeek = static_cast<int8_t>(1 + ((dayOfWeek + 5) % 7)); // dayOfWeek is one-based
604 if (dayOfMonth < 1) {
605 dayOfMonth = prevMonthLen;
606 --month;
607 }
608 }
609
610 // first compare months. If they're different, we don't have to worry about days
611 // and times
612 if (month < ruleMonth) return -1;
613 else if (month > ruleMonth) return 1;
614
615 // calculate the actual day of month for the rule
616 int32_t ruleDayOfMonth = 0;
617
618 // Adjust the ruleDay to the monthLen, for non-leap year February 29 rule days.
619 if (ruleDay > monthLen) {
620 ruleDay = monthLen;
621 }
622
623 switch (ruleMode)
624 {
625 // if the mode is day-of-month, the day of month is given
626 case DOM_MODE:
627 ruleDayOfMonth = ruleDay;
628 break;
629
630 // if the mode is day-of-week-in-month, calculate the day-of-month from it
631 case DOW_IN_MONTH_MODE:
632 // In this case ruleDay is the day-of-week-in-month (this code is using
633 // the dayOfWeek and dayOfMonth parameters to figure out the day-of-week
634 // of the first day of the month, so it's trusting that they're really
635 // consistent with each other)
636 if (ruleDay > 0)
637 ruleDayOfMonth = 1 + (ruleDay - 1) * 7 +
638 (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7;
639
640 // if ruleDay is negative (we assume it's not zero here), we have to do
641 // the same calculation figuring backward from the last day of the month.
642 else
643 {
644 // (again, this code is trusting that dayOfWeek and dayOfMonth are
645 // consistent with each other here, since we're using them to figure
646 // the day of week of the first of the month)
647 ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 -
648 (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7;
649 }
650 break;
651
652 case DOW_GE_DOM_MODE:
653 ruleDayOfMonth = ruleDay +
654 (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7;
655 break;
656
657 case DOW_LE_DOM_MODE:
658 ruleDayOfMonth = ruleDay -
659 (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7;
660 // Note at this point ruleDayOfMonth may be <1, although it will
661 // be >=1 for well-formed rules.
662 break;
663 }
664
665 // now that we have a real day-in-month for the rule, we can compare days...
666 if (dayOfMonth < ruleDayOfMonth) return -1;
667 else if (dayOfMonth > ruleDayOfMonth) return 1;
668
669 // ...and if they're equal, we compare times
670 if (millis < ruleMillis) return -1;
671 else if (millis > ruleMillis) return 1;
672 else return 0;
673 }
674
675 // -------------------------------------
676
677 int32_t
getRawOffset() const678 SimpleTimeZone::getRawOffset() const
679 {
680 return rawOffset;
681 }
682
683 // -------------------------------------
684
685 void
setRawOffset(int32_t offsetMillis)686 SimpleTimeZone::setRawOffset(int32_t offsetMillis)
687 {
688 rawOffset = offsetMillis;
689 transitionRulesInitialized = false;
690 }
691
692 // -------------------------------------
693
694 void
setDSTSavings(int32_t millisSavedDuringDST,UErrorCode & status)695 SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST, UErrorCode& status)
696 {
697 if (millisSavedDuringDST == 0) {
698 status = U_ILLEGAL_ARGUMENT_ERROR;
699 }
700 else {
701 dstSavings = millisSavedDuringDST;
702 }
703 transitionRulesInitialized = false;
704 }
705
706 // -------------------------------------
707
708 int32_t
getDSTSavings() const709 SimpleTimeZone::getDSTSavings() const
710 {
711 return dstSavings;
712 }
713
714 // -------------------------------------
715
716 UBool
useDaylightTime() const717 SimpleTimeZone::useDaylightTime() const
718 {
719 return useDaylight;
720 }
721
722 // -------------------------------------
723
724 /**
725 * Overrides TimeZone
726 * Queries if the given date is in Daylight Savings Time.
727 */
inDaylightTime(UDate date,UErrorCode & status) const728 UBool SimpleTimeZone::inDaylightTime(UDate date, UErrorCode& status) const
729 {
730 // This method is wasteful since it creates a new GregorianCalendar and
731 // deletes it each time it is called. However, this is a deprecated method
732 // and provided only for Java compatibility as of 8/6/97 [LIU].
733 if (U_FAILURE(status)) return false;
734 GregorianCalendar *gc = new GregorianCalendar(*this, status);
735 /* test for nullptr */
736 if (gc == nullptr) {
737 status = U_MEMORY_ALLOCATION_ERROR;
738 return false;
739 }
740 gc->setTime(date, status);
741 UBool result = gc->inDaylightTime(status);
742 delete gc;
743 return result;
744 }
745
746 // -------------------------------------
747
748 /**
749 * Return true if this zone has the same rules and offset as another zone.
750 * @param other the TimeZone object to be compared with
751 * @return true if the given zone has the same rules and offset as this one
752 */
753 UBool
hasSameRules(const TimeZone & other) const754 SimpleTimeZone::hasSameRules(const TimeZone& other) const
755 {
756 if (this == &other) return true;
757 if (typeid(*this) != typeid(other)) return false;
758 SimpleTimeZone *that = (SimpleTimeZone*)&other;
759 return rawOffset == that->rawOffset &&
760 useDaylight == that->useDaylight &&
761 (!useDaylight
762 // Only check rules if using DST
763 || (dstSavings == that->dstSavings &&
764 startMode == that->startMode &&
765 startMonth == that->startMonth &&
766 startDay == that->startDay &&
767 startDayOfWeek == that->startDayOfWeek &&
768 startTime == that->startTime &&
769 startTimeMode == that->startTimeMode &&
770 endMode == that->endMode &&
771 endMonth == that->endMonth &&
772 endDay == that->endDay &&
773 endDayOfWeek == that->endDayOfWeek &&
774 endTime == that->endTime &&
775 endTimeMode == that->endTimeMode &&
776 startYear == that->startYear));
777 }
778
779 // -------------------------------------
780
781 //----------------------------------------------------------------------
782 // Rule representation
783 //
784 // We represent the following flavors of rules:
785 // 5 the fifth of the month
786 // lastSun the last Sunday in the month
787 // lastMon the last Monday in the month
788 // Sun>=8 first Sunday on or after the eighth
789 // Sun<=25 last Sunday on or before the 25th
790 // This is further complicated by the fact that we need to remain
791 // backward compatible with the 1.1 FCS. Finally, we need to minimize
792 // API changes. In order to satisfy these requirements, we support
793 // three representation systems, and we translate between them.
794 //
795 // INTERNAL REPRESENTATION
796 // This is the format SimpleTimeZone objects take after construction or
797 // streaming in is complete. Rules are represented directly, using an
798 // unencoded format. We will discuss the start rule only below; the end
799 // rule is analogous.
800 // startMode Takes on enumerated values DAY_OF_MONTH,
801 // DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
802 // startDay The day of the month, or for DOW_IN_MONTH mode, a
803 // value indicating which DOW, such as +1 for first,
804 // +2 for second, -1 for last, etc.
805 // startDayOfWeek The day of the week. Ignored for DAY_OF_MONTH.
806 //
807 // ENCODED REPRESENTATION
808 // This is the format accepted by the constructor and by setStartRule()
809 // and setEndRule(). It uses various combinations of positive, negative,
810 // and zero values to encode the different rules. This representation
811 // allows us to specify all the different rule flavors without altering
812 // the API.
813 // MODE startMonth startDay startDayOfWeek
814 // DOW_IN_MONTH_MODE >=0 !=0 >0
815 // DOM_MODE >=0 >0 ==0
816 // DOW_GE_DOM_MODE >=0 >0 <0
817 // DOW_LE_DOM_MODE >=0 <0 <0
818 // (no DST) don't care ==0 don't care
819 //
820 // STREAMED REPRESENTATION
821 // We must retain binary compatibility with the 1.1 FCS. The 1.1 code only
822 // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
823 // flag useDaylight. When we stream an object out, we translate into an
824 // approximate DOW_IN_MONTH_MODE representation so the object can be parsed
825 // and used by 1.1 code. Following that, we write out the full
826 // representation separately so that contemporary code can recognize and
827 // parse it. The full representation is written in a "packed" format,
828 // consisting of a version number, a length, and an array of bytes. Future
829 // versions of this class may specify different versions. If they wish to
830 // include additional data, they should do so by storing them after the
831 // packed representation below.
832 //----------------------------------------------------------------------
833
834 /**
835 * Given a set of encoded rules in startDay and startDayOfMonth, decode
836 * them and set the startMode appropriately. Do the same for endDay and
837 * endDayOfMonth. Upon entry, the day of week variables may be zero or
838 * negative, in order to indicate special modes. The day of month
839 * variables may also be negative. Upon exit, the mode variables will be
840 * set, and the day of week and day of month variables will be positive.
841 * This method also recognizes a startDay or endDay of zero as indicating
842 * no DST.
843 */
844 void
decodeRules(UErrorCode & status)845 SimpleTimeZone::decodeRules(UErrorCode& status)
846 {
847 decodeStartRule(status);
848 decodeEndRule(status);
849 }
850
851 /**
852 * Decode the start rule and validate the parameters. The parameters are
853 * expected to be in encoded form, which represents the various rule modes
854 * by negating or zeroing certain values. Representation formats are:
855 * <p>
856 * <pre>
857 * DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST
858 * ------------ ----- -------- -------- ----------
859 * month 0..11 same same same don't care
860 * day -5..5 1..31 1..31 -1..-31 0
861 * dayOfWeek 1..7 0 -1..-7 -1..-7 don't care
862 * time 0..ONEDAY same same same don't care
863 * </pre>
864 * The range for month does not include UNDECIMBER since this class is
865 * really specific to GregorianCalendar, which does not use that month.
866 * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
867 * end rule is an exclusive limit point. That is, the range of times that
868 * are in DST include those >= the start and < the end. For this reason,
869 * it should be possible to specify an end of ONEDAY in order to include the
870 * entire day. Although this is equivalent to time 0 of the following day,
871 * it's not always possible to specify that, for example, on December 31.
872 * While arguably the start range should still be 0..ONEDAY-1, we keep
873 * the start and end ranges the same for consistency.
874 */
875 void
decodeStartRule(UErrorCode & status)876 SimpleTimeZone::decodeStartRule(UErrorCode& status)
877 {
878 if(U_FAILURE(status)) return;
879
880 useDaylight = static_cast<UBool>(startDay != 0 && endDay != 0);
881 if (useDaylight && dstSavings == 0) {
882 dstSavings = U_MILLIS_PER_HOUR;
883 }
884 if (startDay != 0) {
885 if (startMonth < UCAL_JANUARY || startMonth > UCAL_DECEMBER) {
886 status = U_ILLEGAL_ARGUMENT_ERROR;
887 return;
888 }
889 if (startTime < 0 || startTime > U_MILLIS_PER_DAY ||
890 startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) {
891 status = U_ILLEGAL_ARGUMENT_ERROR;
892 return;
893 }
894 if (startDayOfWeek == 0) {
895 startMode = DOM_MODE;
896 } else {
897 if (startDayOfWeek > 0) {
898 startMode = DOW_IN_MONTH_MODE;
899 } else {
900 startDayOfWeek = static_cast<int8_t>(-startDayOfWeek);
901 if (startDay > 0) {
902 startMode = DOW_GE_DOM_MODE;
903 } else {
904 startDay = static_cast<int8_t>(-startDay);
905 startMode = DOW_LE_DOM_MODE;
906 }
907 }
908 if (startDayOfWeek > UCAL_SATURDAY) {
909 status = U_ILLEGAL_ARGUMENT_ERROR;
910 return;
911 }
912 }
913 if (startMode == DOW_IN_MONTH_MODE) {
914 if (startDay < -5 || startDay > 5) {
915 status = U_ILLEGAL_ARGUMENT_ERROR;
916 return;
917 }
918 } else if (startDay<1 || startDay > STATICMONTHLENGTH[startMonth]) {
919 status = U_ILLEGAL_ARGUMENT_ERROR;
920 return;
921 }
922 }
923 }
924
925 /**
926 * Decode the end rule and validate the parameters. This method is exactly
927 * analogous to decodeStartRule().
928 * @see decodeStartRule
929 */
930 void
decodeEndRule(UErrorCode & status)931 SimpleTimeZone::decodeEndRule(UErrorCode& status)
932 {
933 if(U_FAILURE(status)) return;
934
935 useDaylight = static_cast<UBool>(startDay != 0 && endDay != 0);
936 if (useDaylight && dstSavings == 0) {
937 dstSavings = U_MILLIS_PER_HOUR;
938 }
939 if (endDay != 0) {
940 if (endMonth < UCAL_JANUARY || endMonth > UCAL_DECEMBER) {
941 status = U_ILLEGAL_ARGUMENT_ERROR;
942 return;
943 }
944 if (endTime < 0 || endTime > U_MILLIS_PER_DAY ||
945 endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) {
946 status = U_ILLEGAL_ARGUMENT_ERROR;
947 return;
948 }
949 if (endDayOfWeek == 0) {
950 endMode = DOM_MODE;
951 } else {
952 if (endDayOfWeek > 0) {
953 endMode = DOW_IN_MONTH_MODE;
954 } else {
955 endDayOfWeek = static_cast<int8_t>(-endDayOfWeek);
956 if (endDay > 0) {
957 endMode = DOW_GE_DOM_MODE;
958 } else {
959 endDay = static_cast<int8_t>(-endDay);
960 endMode = DOW_LE_DOM_MODE;
961 }
962 }
963 if (endDayOfWeek > UCAL_SATURDAY) {
964 status = U_ILLEGAL_ARGUMENT_ERROR;
965 return;
966 }
967 }
968 if (endMode == DOW_IN_MONTH_MODE) {
969 if (endDay < -5 || endDay > 5) {
970 status = U_ILLEGAL_ARGUMENT_ERROR;
971 return;
972 }
973 } else if (endDay<1 || endDay > STATICMONTHLENGTH[endMonth]) {
974 status = U_ILLEGAL_ARGUMENT_ERROR;
975 return;
976 }
977 }
978 }
979
980 UBool
getNextTransition(UDate base,UBool inclusive,TimeZoneTransition & result) const981 SimpleTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
982 if (!useDaylight) {
983 return false;
984 }
985
986 UErrorCode status = U_ZERO_ERROR;
987 checkTransitionRules(status);
988 if (U_FAILURE(status)) {
989 return false;
990 }
991
992 UDate firstTransitionTime = firstTransition->getTime();
993 if (base < firstTransitionTime || (inclusive && base == firstTransitionTime)) {
994 result = *firstTransition;
995 }
996 UDate stdDate, dstDate;
997 UBool stdAvail = stdRule->getNextStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
998 UBool dstAvail = dstRule->getNextStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
999 if (stdAvail && (!dstAvail || stdDate < dstDate)) {
1000 result.setTime(stdDate);
1001 result.setFrom(*dstRule);
1002 result.setTo(*stdRule);
1003 return true;
1004 }
1005 if (dstAvail && (!stdAvail || dstDate < stdDate)) {
1006 result.setTime(dstDate);
1007 result.setFrom(*stdRule);
1008 result.setTo(*dstRule);
1009 return true;
1010 }
1011 return false;
1012 }
1013
1014 UBool
getPreviousTransition(UDate base,UBool inclusive,TimeZoneTransition & result) const1015 SimpleTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
1016 if (!useDaylight) {
1017 return false;
1018 }
1019
1020 UErrorCode status = U_ZERO_ERROR;
1021 checkTransitionRules(status);
1022 if (U_FAILURE(status)) {
1023 return false;
1024 }
1025
1026 UDate firstTransitionTime = firstTransition->getTime();
1027 if (base < firstTransitionTime || (!inclusive && base == firstTransitionTime)) {
1028 return false;
1029 }
1030 UDate stdDate, dstDate;
1031 UBool stdAvail = stdRule->getPreviousStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
1032 UBool dstAvail = dstRule->getPreviousStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
1033 if (stdAvail && (!dstAvail || stdDate > dstDate)) {
1034 result.setTime(stdDate);
1035 result.setFrom(*dstRule);
1036 result.setTo(*stdRule);
1037 return true;
1038 }
1039 if (dstAvail && (!stdAvail || dstDate > stdDate)) {
1040 result.setTime(dstDate);
1041 result.setFrom(*stdRule);
1042 result.setTo(*dstRule);
1043 return true;
1044 }
1045 return false;
1046 }
1047
1048 void
clearTransitionRules()1049 SimpleTimeZone::clearTransitionRules() {
1050 initialRule = nullptr;
1051 firstTransition = nullptr;
1052 stdRule = nullptr;
1053 dstRule = nullptr;
1054 transitionRulesInitialized = false;
1055 }
1056
1057 void
deleteTransitionRules()1058 SimpleTimeZone::deleteTransitionRules() {
1059 delete initialRule;
1060 delete firstTransition;
1061 delete stdRule;
1062 delete dstRule;
1063 clearTransitionRules();
1064 }
1065
1066 /*
1067 * Lazy transition rules initializer
1068 *
1069 * Note On the removal of UMTX_CHECK from checkTransitionRules():
1070 *
1071 * It would be faster to have a UInitOnce as part of a SimpleTimeZone object,
1072 * which would avoid needing to lock a mutex to check the initialization state.
1073 * But we can't easily because simpletz.h is a public header, and including
1074 * a UInitOnce as a member of SimpleTimeZone would publicly expose internal ICU headers.
1075 *
1076 * Alternatively we could have a pointer to a UInitOnce in the SimpleTimeZone object,
1077 * allocate it in the constructors. This would be a more intrusive change, but doable
1078 * if performance turns out to be an issue.
1079 */
1080
1081 void
checkTransitionRules(UErrorCode & status) const1082 SimpleTimeZone::checkTransitionRules(UErrorCode& status) const {
1083 if (U_FAILURE(status)) {
1084 return;
1085 }
1086 static UMutex gLock;
1087 umtx_lock(&gLock);
1088 if (!transitionRulesInitialized) {
1089 SimpleTimeZone *ncThis = const_cast<SimpleTimeZone*>(this);
1090 ncThis->initTransitionRules(status);
1091 }
1092 umtx_unlock(&gLock);
1093 }
1094
1095 void
initTransitionRules(UErrorCode & status)1096 SimpleTimeZone::initTransitionRules(UErrorCode& status) {
1097 if (U_FAILURE(status)) {
1098 return;
1099 }
1100 if (transitionRulesInitialized) {
1101 return;
1102 }
1103 deleteTransitionRules();
1104 UnicodeString tzid;
1105 getID(tzid);
1106
1107 if (useDaylight) {
1108 DateTimeRule* dtRule;
1109 DateTimeRule::TimeRuleType timeRuleType;
1110 UDate firstStdStart, firstDstStart;
1111
1112 // Create a TimeZoneRule for daylight saving time
1113 timeRuleType = (startTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME :
1114 ((startTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME);
1115 switch (startMode) {
1116 case DOM_MODE:
1117 dtRule = new DateTimeRule(startMonth, startDay, startTime, timeRuleType);
1118 break;
1119 case DOW_IN_MONTH_MODE:
1120 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, startTime, timeRuleType);
1121 break;
1122 case DOW_GE_DOM_MODE:
1123 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, true, startTime, timeRuleType);
1124 break;
1125 case DOW_LE_DOM_MODE:
1126 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, false, startTime, timeRuleType);
1127 break;
1128 default:
1129 status = U_INVALID_STATE_ERROR;
1130 return;
1131 }
1132 // Check for Null pointer
1133 if (dtRule == nullptr) {
1134 status = U_MEMORY_ALLOCATION_ERROR;
1135 return;
1136 }
1137 // For now, use ID + "(DST)" as the name
1138 dstRule = new AnnualTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), getDSTSavings(),
1139 dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR);
1140
1141 // Check for Null pointer
1142 if (dstRule == nullptr) {
1143 status = U_MEMORY_ALLOCATION_ERROR;
1144 deleteTransitionRules();
1145 return;
1146 }
1147
1148 // Calculate the first DST start time
1149 dstRule->getFirstStart(getRawOffset(), 0, firstDstStart);
1150
1151 // Create a TimeZoneRule for standard time
1152 timeRuleType = (endTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME :
1153 ((endTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME);
1154 switch (endMode) {
1155 case DOM_MODE:
1156 dtRule = new DateTimeRule(endMonth, endDay, endTime, timeRuleType);
1157 break;
1158 case DOW_IN_MONTH_MODE:
1159 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, endTime, timeRuleType);
1160 break;
1161 case DOW_GE_DOM_MODE:
1162 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, true, endTime, timeRuleType);
1163 break;
1164 case DOW_LE_DOM_MODE:
1165 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, false, endTime, timeRuleType);
1166 break;
1167 }
1168
1169 // Check for Null pointer
1170 if (dtRule == nullptr) {
1171 status = U_MEMORY_ALLOCATION_ERROR;
1172 deleteTransitionRules();
1173 return;
1174 }
1175 // For now, use ID + "(STD)" as the name
1176 stdRule = new AnnualTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0,
1177 dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR);
1178
1179 //Check for Null pointer
1180 if (stdRule == nullptr) {
1181 status = U_MEMORY_ALLOCATION_ERROR;
1182 deleteTransitionRules();
1183 return;
1184 }
1185
1186 // Calculate the first STD start time
1187 stdRule->getFirstStart(getRawOffset(), dstRule->getDSTSavings(), firstStdStart);
1188
1189 // Create a TimeZoneRule for initial time
1190 if (firstStdStart < firstDstStart) {
1191 initialRule = new InitialTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), dstRule->getDSTSavings());
1192 if (initialRule == nullptr) {
1193 status = U_MEMORY_ALLOCATION_ERROR;
1194 deleteTransitionRules();
1195 return;
1196 }
1197 firstTransition = new TimeZoneTransition(firstStdStart, *initialRule, *stdRule);
1198 } else {
1199 initialRule = new InitialTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0);
1200 if (initialRule == nullptr) {
1201 status = U_MEMORY_ALLOCATION_ERROR;
1202 deleteTransitionRules();
1203 return;
1204 }
1205 firstTransition = new TimeZoneTransition(firstDstStart, *initialRule, *dstRule);
1206 }
1207 if (firstTransition == nullptr) {
1208 status = U_MEMORY_ALLOCATION_ERROR;
1209 deleteTransitionRules();
1210 return;
1211 }
1212
1213 } else {
1214 // Create a TimeZoneRule for initial time
1215 initialRule = new InitialTimeZoneRule(tzid, getRawOffset(), 0);
1216 // Check for null pointer.
1217 if (initialRule == nullptr) {
1218 status = U_MEMORY_ALLOCATION_ERROR;
1219 deleteTransitionRules();
1220 return;
1221 }
1222 }
1223
1224 transitionRulesInitialized = true;
1225 }
1226
1227 int32_t
countTransitionRules(UErrorCode &) const1228 SimpleTimeZone::countTransitionRules(UErrorCode& /*status*/) const {
1229 return (useDaylight) ? 2 : 0;
1230 }
1231
1232 void
getTimeZoneRules(const InitialTimeZoneRule * & initial,const TimeZoneRule * trsrules[],int32_t & trscount,UErrorCode & status) const1233 SimpleTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
1234 const TimeZoneRule* trsrules[],
1235 int32_t& trscount,
1236 UErrorCode& status) const {
1237 if (U_FAILURE(status)) {
1238 return;
1239 }
1240 checkTransitionRules(status);
1241 if (U_FAILURE(status)) {
1242 return;
1243 }
1244 initial = initialRule;
1245 int32_t cnt = 0;
1246 if (stdRule != nullptr) {
1247 if (cnt < trscount) {
1248 trsrules[cnt++] = stdRule;
1249 }
1250 if (cnt < trscount) {
1251 trsrules[cnt++] = dstRule;
1252 }
1253 }
1254 trscount = cnt;
1255 }
1256
1257
1258 U_NAMESPACE_END
1259
1260 #endif /* #if !UCONFIG_NO_FORMATTING */
1261
1262 //eof
1263