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