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