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