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