| |
1 /* |
| |
2 ******************************************************************************* |
| |
3 * Copyright (C) 1997-2013, International Business Machines Corporation and |
| |
4 * others. All Rights Reserved. |
| |
5 ******************************************************************************* |
| |
6 * |
| |
7 * File SIMPLETZ.H |
| |
8 * |
| |
9 * Modification History: |
| |
10 * |
| |
11 * Date Name Description |
| |
12 * 12/05/96 clhuang Creation. |
| |
13 * 04/21/97 aliu Fixed miscellaneous bugs found by inspection and |
| |
14 * testing. |
| |
15 * 07/29/97 aliu Ported source bodies back from Java version with |
| |
16 * numerous feature enhancements and bug fixes. |
| |
17 * 08/10/98 stephen JDK 1.2 sync. |
| |
18 * 09/17/98 stephen Fixed getOffset() for last hour of year and DST |
| |
19 * 12/02/99 aliu Added TimeMode and constructor and setStart/EndRule |
| |
20 * methods that take TimeMode. Whitespace cleanup. |
| |
21 ******************************************************************************** |
| |
22 */ |
| |
23 |
| |
24 #include "utypeinfo.h" // for 'typeid' to work |
| |
25 |
| |
26 #include "unicode/utypes.h" |
| |
27 |
| |
28 #if !UCONFIG_NO_FORMATTING |
| |
29 |
| |
30 #include "unicode/simpletz.h" |
| |
31 #include "unicode/gregocal.h" |
| |
32 #include "unicode/smpdtfmt.h" |
| |
33 |
| |
34 #include "gregoimp.h" |
| |
35 #include "umutex.h" |
| |
36 |
| |
37 U_NAMESPACE_BEGIN |
| |
38 |
| |
39 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleTimeZone) |
| |
40 |
| |
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 |
| |
49 // Gregorian, since we know they didn't have daylight time when |
| |
50 // Gregorian calendar started. |
| |
51 const int8_t SimpleTimeZone::STATICMONTHLENGTH[] = {31,29,31,30,31,30,31,31,30,31,30,31}; |
| |
52 |
| |
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 |
| |
57 // ***************************************************************************** |
| |
58 // class SimpleTimeZone |
| |
59 // ***************************************************************************** |
| |
60 |
| |
61 |
| |
62 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID) |
| |
63 : BasicTimeZone(ID), |
| |
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 { |
| |
81 clearTransitionRules(); |
| |
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) |
| |
92 : BasicTimeZone(ID) |
| |
93 { |
| |
94 clearTransitionRules(); |
| |
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) |
| |
111 : BasicTimeZone(ID) |
| |
112 { |
| |
113 clearTransitionRules(); |
| |
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) |
| |
132 : BasicTimeZone(ID) |
| |
133 { |
| |
134 clearTransitionRules(); |
| |
135 construct(rawOffsetGMT, |
| |
136 savingsStartMonth, savingsStartDay, savingsStartDayOfWeek, |
| |
137 savingsStartTime, savingsStartTimeMode, |
| |
138 savingsEndMonth, savingsEndDay, savingsEndDayOfWeek, |
| |
139 savingsEndTime, savingsEndTimeMode, |
| |
140 savingsDST, status); |
| |
141 } |
| |
142 |
| |
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 { |
| |
187 deleteTransitionRules(); |
| |
188 } |
| |
189 |
| |
190 // ------------------------------------- |
| |
191 |
| |
192 // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful. |
| |
193 SimpleTimeZone::SimpleTimeZone(const SimpleTimeZone &source) |
| |
194 : BasicTimeZone(source) |
| |
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; |
| |
224 clearTransitionRules(); |
| |
225 } |
| |
226 return *this; |
| |
227 } |
| |
228 |
| |
229 // ------------------------------------- |
| |
230 |
| |
231 UBool |
| |
232 SimpleTimeZone::operator==(const TimeZone& that) const |
| |
233 { |
| |
234 return ((this == &that) || |
| |
235 (typeid(*this) == typeid(that) && |
| |
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; |
| |
262 transitionRulesInitialized = FALSE; |
| |
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); |
| |
316 transitionRulesInitialized = FALSE; |
| |
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); |
| |
368 transitionRulesInitialized = FALSE; |
| |
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 { |
| |
396 // Check the month before calling Grego::monthLength(). This |
| |
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 |
| |
408 return getOffset(era, year, month, day, dayOfWeek, millis, Grego::monthLength(year, month), status); |
| |
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, |
| |
414 int32_t /*monthLength*/, UErrorCode& status) const |
| |
415 { |
| |
416 // Check the month before calling Grego::monthLength(). This |
| |
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 |
| |
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. |
| |
432 return getOffset(era, year, month, day, dayOfWeek, millis, |
| |
433 Grego::monthLength(year, month), |
| |
434 Grego::previousMonthLength(year, month), |
| |
435 status); |
| |
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 |
| |
456 || monthLength > 31 |
| |
457 || prevMonthLength < 28 |
| |
458 || prevMonthLength > 31) { |
| |
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 |
| |
508 void |
| |
509 SimpleTimeZone::getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt, |
| |
510 int32_t& rawOffsetGMT, int32_t& savingsDST, UErrorCode& status) const { |
| |
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 |
| |
535 || ((nonExistingTimeOpt & kStdDstMask) != kDaylight && (nonExistingTimeOpt & kFormerLatterMask) != kLatter)) { |
| |
536 date -= getDSTSavings(); |
| |
537 recalc = TRUE; |
| |
538 } |
| |
539 } else { |
| |
540 if ((duplicatedTimeOpt & kStdDstMask) == kDaylight |
| |
541 || ((duplicatedTimeOpt & kStdDstMask) != kStandard && (duplicatedTimeOpt & kFormerLatterMask) == kFormer)) { |
| |
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 |
| |
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; |
| |
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 |
| |
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. |
| |
632 else |
| |
633 { |
| |
634 // (again, this code is trusting that dayOfWeek and dayOfMonth are |
| |
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; |
| |
679 transitionRulesInitialized = FALSE; |
| |
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 } |
| |
693 transitionRulesInitialized = FALSE; |
| |
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; |
| |
747 if (typeid(*this) != typeid(other)) return FALSE; |
| |
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 } |
| |
908 } else if (startDay<1 || startDay > STATICMONTHLENGTH[startMonth]) { |
| |
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 } |
| |
963 } else if (endDay<1 || endDay > STATICMONTHLENGTH[endMonth]) { |
| |
964 status = U_ILLEGAL_ARGUMENT_ERROR; |
| |
965 return; |
| |
966 } |
| |
967 } |
| |
968 } |
| |
969 |
| |
970 UBool |
| |
971 SimpleTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { |
| |
972 if (!useDaylight) { |
| |
973 return FALSE; |
| |
974 } |
| |
975 |
| |
976 UErrorCode status = U_ZERO_ERROR; |
| |
977 checkTransitionRules(status); |
| |
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 |
| |
1005 SimpleTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const { |
| |
1006 if (!useDaylight) { |
| |
1007 return FALSE; |
| |
1008 } |
| |
1009 |
| |
1010 UErrorCode status = U_ZERO_ERROR; |
| |
1011 checkTransitionRules(status); |
| |
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 |
| |
1064 /* |
| |
1065 * Lazy transition rules initializer |
| |
1066 * |
| |
1067 * Note On the removal of UMTX_CHECK from checkTransitionRules(): |
| |
1068 * |
| |
1069 * It would be faster to have a UInitOnce as part of a SimpleTimeZone object, |
| |
1070 * which would avoid needing to lock a mutex to check the initialization state. |
| |
1071 * But we can't easily because simpletz.h is a public header, and including |
| |
1072 * a UInitOnce as a member of SimpleTimeZone would publicly expose internal ICU headers. |
| |
1073 * |
| |
1074 * Alternatively we could have a pointer to a UInitOnce in the SimpleTimeZone object, |
| |
1075 * allocate it in the constructors. This would be a more intrusive change, but doable |
| |
1076 * if performance turns out to be an issue. |
| |
1077 */ |
| |
1078 static UMutex gLock = U_MUTEX_INITIALIZER; |
| |
1079 |
| |
1080 void |
| |
1081 SimpleTimeZone::checkTransitionRules(UErrorCode& status) const { |
| |
1082 if (U_FAILURE(status)) { |
| |
1083 return; |
| |
1084 } |
| |
1085 umtx_lock(&gLock); |
| |
1086 if (!transitionRulesInitialized) { |
| |
1087 SimpleTimeZone *ncThis = const_cast<SimpleTimeZone*>(this); |
| |
1088 ncThis->initTransitionRules(status); |
| |
1089 } |
| |
1090 umtx_unlock(&gLock); |
| |
1091 } |
| |
1092 |
| |
1093 void |
| |
1094 SimpleTimeZone::initTransitionRules(UErrorCode& status) { |
| |
1095 if (U_FAILURE(status)) { |
| |
1096 return; |
| |
1097 } |
| |
1098 if (transitionRulesInitialized) { |
| |
1099 return; |
| |
1100 } |
| |
1101 deleteTransitionRules(); |
| |
1102 UnicodeString tzid; |
| |
1103 getID(tzid); |
| |
1104 |
| |
1105 if (useDaylight) { |
| |
1106 DateTimeRule* dtRule; |
| |
1107 DateTimeRule::TimeRuleType timeRuleType; |
| |
1108 UDate firstStdStart, firstDstStart; |
| |
1109 |
| |
1110 // Create a TimeZoneRule for daylight saving time |
| |
1111 timeRuleType = (startTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME : |
| |
1112 ((startTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME); |
| |
1113 switch (startMode) { |
| |
1114 case DOM_MODE: |
| |
1115 dtRule = new DateTimeRule(startMonth, startDay, startTime, timeRuleType); |
| |
1116 break; |
| |
1117 case DOW_IN_MONTH_MODE: |
| |
1118 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, startTime, timeRuleType); |
| |
1119 break; |
| |
1120 case DOW_GE_DOM_MODE: |
| |
1121 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, true, startTime, timeRuleType); |
| |
1122 break; |
| |
1123 case DOW_LE_DOM_MODE: |
| |
1124 dtRule = new DateTimeRule(startMonth, startDay, startDayOfWeek, false, startTime, timeRuleType); |
| |
1125 break; |
| |
1126 default: |
| |
1127 status = U_INVALID_STATE_ERROR; |
| |
1128 return; |
| |
1129 } |
| |
1130 // Check for Null pointer |
| |
1131 if (dtRule == NULL) { |
| |
1132 status = U_MEMORY_ALLOCATION_ERROR; |
| |
1133 return; |
| |
1134 } |
| |
1135 // For now, use ID + "(DST)" as the name |
| |
1136 dstRule = new AnnualTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), getDSTSavings(), |
| |
1137 dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR); |
| |
1138 |
| |
1139 // Check for Null pointer |
| |
1140 if (dstRule == NULL) { |
| |
1141 status = U_MEMORY_ALLOCATION_ERROR; |
| |
1142 deleteTransitionRules(); |
| |
1143 return; |
| |
1144 } |
| |
1145 |
| |
1146 // Calculate the first DST start time |
| |
1147 dstRule->getFirstStart(getRawOffset(), 0, firstDstStart); |
| |
1148 |
| |
1149 // Create a TimeZoneRule for standard time |
| |
1150 timeRuleType = (endTimeMode == STANDARD_TIME) ? DateTimeRule::STANDARD_TIME : |
| |
1151 ((endTimeMode == UTC_TIME) ? DateTimeRule::UTC_TIME : DateTimeRule::WALL_TIME); |
| |
1152 switch (endMode) { |
| |
1153 case DOM_MODE: |
| |
1154 dtRule = new DateTimeRule(endMonth, endDay, endTime, timeRuleType); |
| |
1155 break; |
| |
1156 case DOW_IN_MONTH_MODE: |
| |
1157 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, endTime, timeRuleType); |
| |
1158 break; |
| |
1159 case DOW_GE_DOM_MODE: |
| |
1160 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, true, endTime, timeRuleType); |
| |
1161 break; |
| |
1162 case DOW_LE_DOM_MODE: |
| |
1163 dtRule = new DateTimeRule(endMonth, endDay, endDayOfWeek, false, endTime, timeRuleType); |
| |
1164 break; |
| |
1165 } |
| |
1166 |
| |
1167 // Check for Null pointer |
| |
1168 if (dtRule == NULL) { |
| |
1169 status = U_MEMORY_ALLOCATION_ERROR; |
| |
1170 deleteTransitionRules(); |
| |
1171 return; |
| |
1172 } |
| |
1173 // For now, use ID + "(STD)" as the name |
| |
1174 stdRule = new AnnualTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0, |
| |
1175 dtRule, startYear, AnnualTimeZoneRule::MAX_YEAR); |
| |
1176 |
| |
1177 //Check for Null pointer |
| |
1178 if (stdRule == NULL) { |
| |
1179 status = U_MEMORY_ALLOCATION_ERROR; |
| |
1180 deleteTransitionRules(); |
| |
1181 return; |
| |
1182 } |
| |
1183 |
| |
1184 // Calculate the first STD start time |
| |
1185 stdRule->getFirstStart(getRawOffset(), dstRule->getDSTSavings(), firstStdStart); |
| |
1186 |
| |
1187 // Create a TimeZoneRule for initial time |
| |
1188 if (firstStdStart < firstDstStart) { |
| |
1189 initialRule = new InitialTimeZoneRule(tzid+UnicodeString(DST_STR), getRawOffset(), dstRule->getDSTSavings()); |
| |
1190 firstTransition = new TimeZoneTransition(firstStdStart, *initialRule, *stdRule); |
| |
1191 } else { |
| |
1192 initialRule = new InitialTimeZoneRule(tzid+UnicodeString(STD_STR), getRawOffset(), 0); |
| |
1193 firstTransition = new TimeZoneTransition(firstDstStart, *initialRule, *dstRule); |
| |
1194 } |
| |
1195 // Check for null pointers. |
| |
1196 if (initialRule == NULL || firstTransition == NULL) { |
| |
1197 status = U_MEMORY_ALLOCATION_ERROR; |
| |
1198 deleteTransitionRules(); |
| |
1199 return; |
| |
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) { |
| |
1207 status = U_MEMORY_ALLOCATION_ERROR; |
| |
1208 deleteTransitionRules(); |
| |
1209 return; |
| |
1210 } |
| |
1211 } |
| |
1212 |
| |
1213 transitionRulesInitialized = TRUE; |
| |
1214 } |
| |
1215 |
| |
1216 int32_t |
| |
1217 SimpleTimeZone::countTransitionRules(UErrorCode& /*status*/) const { |
| |
1218 return (useDaylight) ? 2 : 0; |
| |
1219 } |
| |
1220 |
| |
1221 void |
| |
1222 SimpleTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial, |
| |
1223 const TimeZoneRule* trsrules[], |
| |
1224 int32_t& trscount, |
| |
1225 UErrorCode& status) const { |
| |
1226 if (U_FAILURE(status)) { |
| |
1227 return; |
| |
1228 } |
| |
1229 checkTransitionRules(status); |
| |
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 |
| |
1247 U_NAMESPACE_END |
| |
1248 |
| |
1249 #endif /* #if !UCONFIG_NO_FORMATTING */ |
| |
1250 |
| |
1251 //eof |