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 = (int8_t)month;
314 startDay = (int8_t)dayOfWeekInMonth;
315 startDayOfWeek = (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 = (int8_t)month;
366 endDay = (int8_t)dayOfWeekInMonth;
367 endDayOfWeek = (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((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength,
479 (int8_t)day, (int8_t)dayOfWeek, millis,
480 startTimeMode == UTC_TIME ? -rawOffset : 0,
481 startMode, (int8_t)startMonth, (int8_t)startDayOfWeek,
482 (int8_t)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((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength,
493 (int8_t)day, (int8_t)dayOfWeek, millis,
494 endTimeMode == WALL_TIME ? dstSavings :
495 (endTimeMode == UTC_TIME ? -rawOffset : 0),
496 endMode, (int8_t)endMonth, (int8_t)endDayOfWeek,
497 (int8_t)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);
530
531 savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
532 (uint8_t) dow, millis,
533 Grego::monthLength(year, month),
534 status) - rawOffsetGMT;
535 if (U_FAILURE(status)) {
536 return;
537 }
538
539 UBool recalc = false;
540
541 // Now we need some adjustment
542 if (savingsDST > 0) {
543 if ((nonExistingTimeOpt & kStdDstMask) == kStandard
544 || ((nonExistingTimeOpt & kStdDstMask) != kDaylight && (nonExistingTimeOpt & kFormerLatterMask) != kLatter)) {
545 date -= getDSTSavings();
546 recalc = true;
547 }
548 } else {
549 if ((duplicatedTimeOpt & kStdDstMask) == kDaylight
550 || ((duplicatedTimeOpt & kStdDstMask) != kStandard && (duplicatedTimeOpt & kFormerLatterMask) == kFormer)) {
551 date -= getDSTSavings();
552 recalc = true;
553 }
554 }
555 if (recalc) {
556 day = ClockMath::floorDivide(date, U_MILLIS_PER_DAY, &millis);
557 Grego::dayToFields(day, year, month, dom, dow);
558 savingsDST = getOffset(GregorianCalendar::AD, year, month, dom,
559 (uint8_t) dow, millis,
560 Grego::monthLength(year, month),
561 status) - rawOffsetGMT;
562 }
563 }
564
565 // -------------------------------------
566
567 /**
568 * Compare a given date in the year to a rule. Return 1, 0, or -1, depending
569 * on whether the date is after, equal to, or before the rule date. The
570 * millis are compared directly against the ruleMillis, so any
571 * standard-daylight adjustments must be handled by the caller.
572 *
573 * @return 1 if the date is after the rule date, -1 if the date is before
574 * the rule date, or 0 if the date is equal to the rule date.
575 */
576 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)577 SimpleTimeZone::compareToRule(int8_t month, int8_t monthLen, int8_t prevMonthLen,
578 int8_t dayOfMonth,
579 int8_t dayOfWeek, int32_t millis, int32_t millisDelta,
580 EMode ruleMode, int8_t ruleMonth, int8_t ruleDayOfWeek,
581 int8_t ruleDay, int32_t ruleMillis)
582 {
583 // Make adjustments for startTimeMode and endTimeMode
584 millis += millisDelta;
585 while (millis >= U_MILLIS_PER_DAY) {
586 millis -= U_MILLIS_PER_DAY;
587 ++dayOfMonth;
588 dayOfWeek = (int8_t)(1 + (dayOfWeek % 7)); // dayOfWeek is one-based
589 if (dayOfMonth > monthLen) {
590 dayOfMonth = 1;
591 /* When incrementing the month, it is desirable to overflow
592 * from DECEMBER to DECEMBER+1, since we use the result to
593 * compare against a real month. Wraparound of the value
594 * leads to bug 4173604. */
595 ++month;
596 }
597 }
598 while (millis < 0) {
599 millis += U_MILLIS_PER_DAY;
600 --dayOfMonth;
601 dayOfWeek = (int8_t)(1 + ((dayOfWeek+5) % 7)); // dayOfWeek is one-based
602 if (dayOfMonth < 1) {
603 dayOfMonth = prevMonthLen;
604 --month;
605 }
606 }
607
608 // first compare months. If they're different, we don't have to worry about days
609 // and times
610 if (month < ruleMonth) return -1;
611 else if (month > ruleMonth) return 1;
612
613 // calculate the actual day of month for the rule
614 int32_t ruleDayOfMonth = 0;
615
616 // Adjust the ruleDay to the monthLen, for non-leap year February 29 rule days.
617 if (ruleDay > monthLen) {
618 ruleDay = monthLen;
619 }
620
621 switch (ruleMode)
622 {
623 // if the mode is day-of-month, the day of month is given
624 case DOM_MODE:
625 ruleDayOfMonth = ruleDay;
626 break;
627
628 // if the mode is day-of-week-in-month, calculate the day-of-month from it
629 case DOW_IN_MONTH_MODE:
630 // In this case ruleDay is the day-of-week-in-month (this code is using
631 // the dayOfWeek and dayOfMonth parameters to figure out the day-of-week
632 // of the first day of the month, so it's trusting that they're really
633 // consistent with each other)
634 if (ruleDay > 0)
635 ruleDayOfMonth = 1 + (ruleDay - 1) * 7 +
636 (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7;
637
638 // if ruleDay is negative (we assume it's not zero here), we have to do
639 // the same calculation figuring backward from the last day of the month.
640 else
641 {
642 // (again, this code is trusting that dayOfWeek and dayOfMonth are
643 // consistent with each other here, since we're using them to figure
644 // the day of week of the first of the month)
645 ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 -
646 (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7;
647 }
648 break;
649
650 case DOW_GE_DOM_MODE:
651 ruleDayOfMonth = ruleDay +
652 (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7;
653 break;
654
655 case DOW_LE_DOM_MODE:
656 ruleDayOfMonth = ruleDay -
657 (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7;
658 // Note at this point ruleDayOfMonth may be <1, although it will
659 // be >=1 for well-formed rules.
660 break;
661 }
662
663 // now that we have a real day-in-month for the rule, we can compare days...
664 if (dayOfMonth < ruleDayOfMonth) return -1;
665 else if (dayOfMonth > ruleDayOfMonth) return 1;
666
667 // ...and if they're equal, we compare times
668 if (millis < ruleMillis) return -1;
669 else if (millis > ruleMillis) return 1;
670 else return 0;
671 }
672
673 // -------------------------------------
674
675 int32_t
getRawOffset() const676 SimpleTimeZone::getRawOffset() const
677 {
678 return rawOffset;
679 }
680
681 // -------------------------------------
682
683 void
setRawOffset(int32_t offsetMillis)684 SimpleTimeZone::setRawOffset(int32_t offsetMillis)
685 {
686 rawOffset = offsetMillis;
687 transitionRulesInitialized = false;
688 }
689
690 // -------------------------------------
691
692 void
setDSTSavings(int32_t millisSavedDuringDST,UErrorCode & status)693 SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST, UErrorCode& status)
694 {
695 if (millisSavedDuringDST == 0) {
696 status = U_ILLEGAL_ARGUMENT_ERROR;
697 }
698 else {
699 dstSavings = millisSavedDuringDST;
700 }
701 transitionRulesInitialized = false;
702 }
703
704 // -------------------------------------
705
706 int32_t
getDSTSavings() const707 SimpleTimeZone::getDSTSavings() const
708 {
709 return dstSavings;
710 }
711
712 // -------------------------------------
713
714 UBool
useDaylightTime() const715 SimpleTimeZone::useDaylightTime() const
716 {
717 return useDaylight;
718 }
719
720 // -------------------------------------
721
722 /**
723 * Overrides TimeZone
724 * Queries if the given date is in Daylight Savings Time.
725 */
inDaylightTime(UDate date,UErrorCode & status) const726 UBool SimpleTimeZone::inDaylightTime(UDate date, UErrorCode& status) const
727 {
728 // This method is wasteful since it creates a new GregorianCalendar and
729 // deletes it each time it is called. However, this is a deprecated method
730 // and provided only for Java compatibility as of 8/6/97 [LIU].
731 if (U_FAILURE(status)) return false;
732 GregorianCalendar *gc = new GregorianCalendar(*this, status);
733 /* test for nullptr */
734 if (gc == nullptr) {
735 status = U_MEMORY_ALLOCATION_ERROR;
736 return false;
737 }
738 gc->setTime(date, status);
739 UBool result = gc->inDaylightTime(status);
740 delete gc;
741 return result;
742 }
743
744 // -------------------------------------
745
746 /**
747 * Return true if this zone has the same rules and offset as another zone.
748 * @param other the TimeZone object to be compared with
749 * @return true if the given zone has the same rules and offset as this one
750 */
751 UBool
hasSameRules(const TimeZone & other) const752 SimpleTimeZone::hasSameRules(const TimeZone& other) const
753 {
754 if (this == &other) return true;
755 if (typeid(*this) != typeid(other)) return false;
756 SimpleTimeZone *that = (SimpleTimeZone*)&other;
757 return rawOffset == that->rawOffset &&
758 useDaylight == that->useDaylight &&
759 (!useDaylight
760 // Only check rules if using DST
761 || (dstSavings == that->dstSavings &&
762 startMode == that->startMode &&
763 startMonth == that->startMonth &&
764 startDay == that->startDay &&
765 startDayOfWeek == that->startDayOfWeek &&
766 startTime == that->startTime &&
767 startTimeMode == that->startTimeMode &&
768 endMode == that->endMode &&
769 endMonth == that->endMonth &&
770 endDay == that->endDay &&
771 endDayOfWeek == that->endDayOfWeek &&
772 endTime == that->endTime &&
773 endTimeMode == that->endTimeMode &&
774 startYear == that->startYear));
775 }
776
777 // -------------------------------------
778
779 //----------------------------------------------------------------------
780 // Rule representation
781 //
782 // We represent the following flavors of rules:
783 // 5 the fifth of the month
784 // lastSun the last Sunday in the month
785 // lastMon the last Monday in the month
786 // Sun>=8 first Sunday on or after the eighth
787 // Sun<=25 last Sunday on or before the 25th
788 // This is further complicated by the fact that we need to remain
789 // backward compatible with the 1.1 FCS. Finally, we need to minimize
790 // API changes. In order to satisfy these requirements, we support
791 // three representation systems, and we translate between them.
792 //
793 // INTERNAL REPRESENTATION
794 // This is the format SimpleTimeZone objects take after construction or
795 // streaming in is complete. Rules are represented directly, using an
796 // unencoded format. We will discuss the start rule only below; the end
797 // rule is analogous.
798 // startMode Takes on enumerated values DAY_OF_MONTH,
799 // DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
800 // startDay The day of the month, or for DOW_IN_MONTH mode, a
801 // value indicating which DOW, such as +1 for first,
802 // +2 for second, -1 for last, etc.
803 // startDayOfWeek The day of the week. Ignored for DAY_OF_MONTH.
804 //
805 // ENCODED REPRESENTATION
806 // This is the format accepted by the constructor and by setStartRule()
807 // and setEndRule(). It uses various combinations of positive, negative,
808 // and zero values to encode the different rules. This representation
809 // allows us to specify all the different rule flavors without altering
810 // the API.
811 // MODE startMonth startDay startDayOfWeek
812 // DOW_IN_MONTH_MODE >=0 !=0 >0
813 // DOM_MODE >=0 >0 ==0
814 // DOW_GE_DOM_MODE >=0 >0 <0
815 // DOW_LE_DOM_MODE >=0 <0 <0
816 // (no DST) don't care ==0 don't care
817 //
818 // STREAMED REPRESENTATION
819 // We must retain binary compatibility with the 1.1 FCS. The 1.1 code only
820 // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
821 // flag useDaylight. When we stream an object out, we translate into an
822 // approximate DOW_IN_MONTH_MODE representation so the object can be parsed
823 // and used by 1.1 code. Following that, we write out the full
824 // representation separately so that contemporary code can recognize and
825 // parse it. The full representation is written in a "packed" format,
826 // consisting of a version number, a length, and an array of bytes. Future
827 // versions of this class may specify different versions. If they wish to
828 // include additional data, they should do so by storing them after the
829 // packed representation below.
830 //----------------------------------------------------------------------
831
832 /**
833 * Given a set of encoded rules in startDay and startDayOfMonth, decode
834 * them and set the startMode appropriately. Do the same for endDay and
835 * endDayOfMonth. Upon entry, the day of week variables may be zero or
836 * negative, in order to indicate special modes. The day of month
837 * variables may also be negative. Upon exit, the mode variables will be
838 * set, and the day of week and day of month variables will be positive.
839 * This method also recognizes a startDay or endDay of zero as indicating
840 * no DST.
841 */
842 void
decodeRules(UErrorCode & status)843 SimpleTimeZone::decodeRules(UErrorCode& status)
844 {
845 decodeStartRule(status);
846 decodeEndRule(status);
847 }
848
849 /**
850 * Decode the start rule and validate the parameters. The parameters are
851 * expected to be in encoded form, which represents the various rule modes
852 * by negating or zeroing certain values. Representation formats are:
853 * <p>
854 * <pre>
855 * DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST
856 * ------------ ----- -------- -------- ----------
857 * month 0..11 same same same don't care
858 * day -5..5 1..31 1..31 -1..-31 0
859 * dayOfWeek 1..7 0 -1..-7 -1..-7 don't care
860 * time 0..ONEDAY same same same don't care
861 * </pre>
862 * The range for month does not include UNDECIMBER since this class is
863 * really specific to GregorianCalendar, which does not use that month.
864 * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
865 * end rule is an exclusive limit point. That is, the range of times that
866 * are in DST include those >= the start and < the end. For this reason,
867 * it should be possible to specify an end of ONEDAY in order to include the
868 * entire day. Although this is equivalent to time 0 of the following day,
869 * it's not always possible to specify that, for example, on December 31.
870 * While arguably the start range should still be 0..ONEDAY-1, we keep
871 * the start and end ranges the same for consistency.
872 */
873 void
decodeStartRule(UErrorCode & status)874 SimpleTimeZone::decodeStartRule(UErrorCode& status)
875 {
876 if(U_FAILURE(status)) return;
877
878 useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? true : false);
879 if (useDaylight && dstSavings == 0) {
880 dstSavings = U_MILLIS_PER_HOUR;
881 }
882 if (startDay != 0) {
883 if (startMonth < UCAL_JANUARY || startMonth > UCAL_DECEMBER) {
884 status = U_ILLEGAL_ARGUMENT_ERROR;
885 return;
886 }
887 if (startTime < 0 || startTime > U_MILLIS_PER_DAY ||
888 startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) {
889 status = U_ILLEGAL_ARGUMENT_ERROR;
890 return;
891 }
892 if (startDayOfWeek == 0) {
893 startMode = DOM_MODE;
894 } else {
895 if (startDayOfWeek > 0) {
896 startMode = DOW_IN_MONTH_MODE;
897 } else {
898 startDayOfWeek = (int8_t)-startDayOfWeek;
899 if (startDay > 0) {
900 startMode = DOW_GE_DOM_MODE;
901 } else {
902 startDay = (int8_t)-startDay;
903 startMode = DOW_LE_DOM_MODE;
904 }
905 }
906 if (startDayOfWeek > UCAL_SATURDAY) {
907 status = U_ILLEGAL_ARGUMENT_ERROR;
908 return;
909 }
910 }
911 if (startMode == DOW_IN_MONTH_MODE) {
912 if (startDay < -5 || startDay > 5) {
913 status = U_ILLEGAL_ARGUMENT_ERROR;
914 return;
915 }
916 } else if (startDay<1 || startDay > STATICMONTHLENGTH[startMonth]) {
917 status = U_ILLEGAL_ARGUMENT_ERROR;
918 return;
919 }
920 }
921 }
922
923 /**
924 * Decode the end rule and validate the parameters. This method is exactly
925 * analogous to decodeStartRule().
926 * @see decodeStartRule
927 */
928 void
decodeEndRule(UErrorCode & status)929 SimpleTimeZone::decodeEndRule(UErrorCode& status)
930 {
931 if(U_FAILURE(status)) return;
932
933 useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? true : false);
934 if (useDaylight && dstSavings == 0) {
935 dstSavings = U_MILLIS_PER_HOUR;
936 }
937 if (endDay != 0) {
938 if (endMonth < UCAL_JANUARY || endMonth > UCAL_DECEMBER) {
939 status = U_ILLEGAL_ARGUMENT_ERROR;
940 return;
941 }
942 if (endTime < 0 || endTime > U_MILLIS_PER_DAY ||
943 endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) {
944 status = U_ILLEGAL_ARGUMENT_ERROR;
945 return;
946 }
947 if (endDayOfWeek == 0) {
948 endMode = DOM_MODE;
949 } else {
950 if (endDayOfWeek > 0) {
951 endMode = DOW_IN_MONTH_MODE;
952 } else {
953 endDayOfWeek = (int8_t)-endDayOfWeek;
954 if (endDay > 0) {
955 endMode = DOW_GE_DOM_MODE;
956 } else {
957 endDay = (int8_t)-endDay;
958 endMode = DOW_LE_DOM_MODE;
959 }
960 }
961 if (endDayOfWeek > UCAL_SATURDAY) {
962 status = U_ILLEGAL_ARGUMENT_ERROR;
963 return;
964 }
965 }
966 if (endMode == DOW_IN_MONTH_MODE) {
967 if (endDay < -5 || endDay > 5) {
968 status = U_ILLEGAL_ARGUMENT_ERROR;
969 return;
970 }
971 } else if (endDay<1 || endDay > STATICMONTHLENGTH[endMonth]) {
972 status = U_ILLEGAL_ARGUMENT_ERROR;
973 return;
974 }
975 }
976 }
977
978 UBool
getNextTransition(UDate base,UBool inclusive,TimeZoneTransition & result) const979 SimpleTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
980 if (!useDaylight) {
981 return false;
982 }
983
984 UErrorCode status = U_ZERO_ERROR;
985 checkTransitionRules(status);
986 if (U_FAILURE(status)) {
987 return false;
988 }
989
990 UDate firstTransitionTime = firstTransition->getTime();
991 if (base < firstTransitionTime || (inclusive && base == firstTransitionTime)) {
992 result = *firstTransition;
993 }
994 UDate stdDate, dstDate;
995 UBool stdAvail = stdRule->getNextStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
996 UBool dstAvail = dstRule->getNextStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
997 if (stdAvail && (!dstAvail || stdDate < dstDate)) {
998 result.setTime(stdDate);
999 result.setFrom(*dstRule);
1000 result.setTo(*stdRule);
1001 return true;
1002 }
1003 if (dstAvail && (!stdAvail || dstDate < stdDate)) {
1004 result.setTime(dstDate);
1005 result.setFrom(*stdRule);
1006 result.setTo(*dstRule);
1007 return true;
1008 }
1009 return false;
1010 }
1011
1012 UBool
getPreviousTransition(UDate base,UBool inclusive,TimeZoneTransition & result) const1013 SimpleTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
1014 if (!useDaylight) {
1015 return false;
1016 }
1017
1018 UErrorCode status = U_ZERO_ERROR;
1019 checkTransitionRules(status);
1020 if (U_FAILURE(status)) {
1021 return false;
1022 }
1023
1024 UDate firstTransitionTime = firstTransition->getTime();
1025 if (base < firstTransitionTime || (!inclusive && base == firstTransitionTime)) {
1026 return false;
1027 }
1028 UDate stdDate, dstDate;
1029 UBool stdAvail = stdRule->getPreviousStart(base, dstRule->getRawOffset(), dstRule->getDSTSavings(), inclusive, stdDate);
1030 UBool dstAvail = dstRule->getPreviousStart(base, stdRule->getRawOffset(), stdRule->getDSTSavings(), inclusive, dstDate);
1031 if (stdAvail && (!dstAvail || stdDate > dstDate)) {
1032 result.setTime(stdDate);
1033 result.setFrom(*dstRule);
1034 result.setTo(*stdRule);
1035 return true;
1036 }
1037 if (dstAvail && (!stdAvail || dstDate > stdDate)) {
1038 result.setTime(dstDate);
1039 result.setFrom(*stdRule);
1040 result.setTo(*dstRule);
1041 return true;
1042 }
1043 return false;
1044 }
1045
1046 void
clearTransitionRules()1047 SimpleTimeZone::clearTransitionRules() {
1048 initialRule = nullptr;
1049 firstTransition = nullptr;
1050 stdRule = nullptr;
1051 dstRule = nullptr;
1052 transitionRulesInitialized = false;
1053 }
1054
1055 void
deleteTransitionRules()1056 SimpleTimeZone::deleteTransitionRules() {
1057 delete initialRule;
1058 delete firstTransition;
1059 delete stdRule;
1060 delete dstRule;
1061 clearTransitionRules();
1062 }
1063
1064 /*
1065 * Lazy transition rules initializer
1066 *
1067 * Note On the removal of UMTX_CHECK from checkTransitionRules():
1068 *
1069 * It would be faster to have a UInitOnce as part of a SimpleTimeZone object,
1070 * which would avoid needing to lock a mutex to check the initialization state.
1071 * But we can't easily because simpletz.h is a public header, and including
1072 * a UInitOnce as a member of SimpleTimeZone would publicly expose internal ICU headers.
1073 *
1074 * Alternatively we could have a pointer to a UInitOnce in the SimpleTimeZone object,
1075 * allocate it in the constructors. This would be a more intrusive change, but doable
1076 * if performance turns out to be an issue.
1077 */
1078
1079 void
checkTransitionRules(UErrorCode & status) const1080 SimpleTimeZone::checkTransitionRules(UErrorCode& status) const {
1081 if (U_FAILURE(status)) {
1082 return;
1083 }
1084 static UMutex gLock;
1085 umtx_lock(&gLock);
1086 if (!transitionRulesInitialized) {
1087 SimpleTimeZone *ncThis = const_cast<SimpleTimeZone*>(this);
1088 ncThis->initTransitionRules(status);
1089 }
1090 umtx_unlock(&gLock);
1091 }
1092
1093 void
initTransitionRules(UErrorCode & status)1094 SimpleTimeZone::initTransitionRules(UErrorCode& status) {
1095 if (U_FAILURE(status)) {
1096 return;
1097 }
1098 if (transitionRulesInitialized) {
1099 return;
1100 }
1101 deleteTransitionRules();
1102 UnicodeString tzid;
1103 getID(tzid);
1104
1105 if (useDaylight) {
1106 DateTimeRule* dtRule;
1107 DateTimeRule::TimeRuleType timeRuleType;
1108 UDate firstStdStart, firstDstStart;
1109
1110 // Create a TimeZoneRule for daylight saving time
1111 timeRuleType = (startTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME :
1112 ((startTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME);
1113 switch (startMode) {
1114 case DOM_MODE:
1115 dtRule = new DateTimeRule(startMonth, startDay, startTime, timeRuleType);
1116 break;
1117 case DOW_IN_MONTH_MODE:
1118 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, startTime, timeRuleType);
1119 break;
1120 case DOW_GE_DOM_MODE:
1121 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, true, startTime, timeRuleType);
1122 break;
1123 case DOW_LE_DOM_MODE:
1124 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, false, startTime, timeRuleType);
1125 break;
1126 default:
1127 status = U_INVALID_STATE_ERROR;
1128 return;
1129 }
1130 // Check for Null pointer
1131 if (dtRule == nullptr) {
1132 status = U_MEMORY_ALLOCATION_ERROR;
1133 return;
1134 }
1135 // For now, use ID + "(DST)" as the name
1136 dstRule = new AnnualTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), getDSTSavings(),
1137 dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR);
1138
1139 // Check for Null pointer
1140 if (dstRule == nullptr) {
1141 status = U_MEMORY_ALLOCATION_ERROR;
1142 deleteTransitionRules();
1143 return;
1144 }
1145
1146 // Calculate the first DST start time
1147 dstRule->getFirstStart(getRawOffset(), 0, firstDstStart);
1148
1149 // Create a TimeZoneRule for standard time
1150 timeRuleType = (endTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME :
1151 ((endTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME);
1152 switch (endMode) {
1153 case DOM_MODE:
1154 dtRule = new DateTimeRule(endMonth, endDay, endTime, timeRuleType);
1155 break;
1156 case DOW_IN_MONTH_MODE:
1157 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, endTime, timeRuleType);
1158 break;
1159 case DOW_GE_DOM_MODE:
1160 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, true, endTime, timeRuleType);
1161 break;
1162 case DOW_LE_DOM_MODE:
1163 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, false, endTime, timeRuleType);
1164 break;
1165 }
1166
1167 // Check for Null pointer
1168 if (dtRule == nullptr) {
1169 status = U_MEMORY_ALLOCATION_ERROR;
1170 deleteTransitionRules();
1171 return;
1172 }
1173 // For now, use ID + "(STD)" as the name
1174 stdRule = new AnnualTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0,
1175 dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR);
1176
1177 //Check for Null pointer
1178 if (stdRule == nullptr) {
1179 status = U_MEMORY_ALLOCATION_ERROR;
1180 deleteTransitionRules();
1181 return;
1182 }
1183
1184 // Calculate the first STD start time
1185 stdRule->getFirstStart(getRawOffset(), dstRule->getDSTSavings(), firstStdStart);
1186
1187 // Create a TimeZoneRule for initial time
1188 if (firstStdStart < firstDstStart) {
1189 initialRule = new InitialTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), dstRule->getDSTSavings());
1190 if (initialRule == nullptr) {
1191 status = U_MEMORY_ALLOCATION_ERROR;
1192 deleteTransitionRules();
1193 return;
1194 }
1195 firstTransition = new TimeZoneTransition(firstStdStart, *initialRule, *stdRule);
1196 } else {
1197 initialRule = new InitialTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0);
1198 if (initialRule == nullptr) {
1199 status = U_MEMORY_ALLOCATION_ERROR;
1200 deleteTransitionRules();
1201 return;
1202 }
1203 firstTransition = new TimeZoneTransition(firstDstStart, *initialRule, *dstRule);
1204 }
1205 if (firstTransition == nullptr) {
1206 status = U_MEMORY_ALLOCATION_ERROR;
1207 deleteTransitionRules();
1208 return;
1209 }
1210
1211 } else {
1212 // Create a TimeZoneRule for initial time
1213 initialRule = new InitialTimeZoneRule(tzid, getRawOffset(), 0);
1214 // Check for null pointer.
1215 if (initialRule == nullptr) {
1216 status = U_MEMORY_ALLOCATION_ERROR;
1217 deleteTransitionRules();
1218 return;
1219 }
1220 }
1221
1222 transitionRulesInitialized = true;
1223 }
1224
1225 int32_t
countTransitionRules(UErrorCode &) const1226 SimpleTimeZone::countTransitionRules(UErrorCode& /*status*/) const {
1227 return (useDaylight) ? 2 : 0;
1228 }
1229
1230 void
getTimeZoneRules(const InitialTimeZoneRule * & initial,const TimeZoneRule * trsrules[],int32_t & trscount,UErrorCode & status) const1231 SimpleTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
1232 const TimeZoneRule* trsrules[],
1233 int32_t& trscount,
1234 UErrorCode& status) const {
1235 if (U_FAILURE(status)) {
1236 return;
1237 }
1238 checkTransitionRules(status);
1239 if (U_FAILURE(status)) {
1240 return;
1241 }
1242 initial = initialRule;
1243 int32_t cnt = 0;
1244 if (stdRule != nullptr) {
1245 if (cnt < trscount) {
1246 trsrules[cnt++] = stdRule;
1247 }
1248 if (cnt < trscount) {
1249 trsrules[cnt++] = dstRule;
1250 }
1251 }
1252 trscount = cnt;
1253 }
1254
1255
1256 U_NAMESPACE_END
1257
1258 #endif /* #if !UCONFIG_NO_FORMATTING */
1259
1260 //eof
1261