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