]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/simpletz.cpp
ICU-6.2.21.tar.gz
[apple/icu.git] / icuSources / i18n / simpletz.cpp
CommitLineData
b75a7d8f
A
1/*
2*******************************************************************************
3* Copyright (C) 1997-2003, International Business Machines Corporation and *
4* others. All Rights Reserved. *
5*******************************************************************************
6*
7* File SIMPLETZ.H
8*
9* Modification History:
10*
11* Date Name Description
12* 12/05/96 clhuang Creation.
13* 04/21/97 aliu Fixed miscellaneous bugs found by inspection and
14* testing.
15* 07/29/97 aliu Ported source bodies back from Java version with
16* numerous feature enhancements and bug fixes.
17* 08/10/98 stephen JDK 1.2 sync.
18* 09/17/98 stephen Fixed getOffset() for last hour of year and DST
19* 12/02/99 aliu Added TimeMode and constructor and setStart/EndRule
20* methods that take TimeMode. Whitespace cleanup.
21********************************************************************************
22*/
23
24#include "unicode/utypes.h"
25
26#if !UCONFIG_NO_FORMATTING
27
28#include "unicode/simpletz.h"
29#include "unicode/gregocal.h"
b75a7d8f
A
30
31U_NAMESPACE_BEGIN
32
374ca955 33UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleTimeZone)
b75a7d8f
A
34
35// WARNING: assumes that no rule is measured from the end of February,
36// since we don't handle leap years. Could handle assuming always
37// Gregorian, since we know they didn't have daylight time when
38// Gregorian calendar started.
374ca955 39const int8_t SimpleTimeZone::STATICMONTHLENGTH[] = {31,29,31,30,31,30,31,31,30,31,30,31};
b75a7d8f
A
40
41// *****************************************************************************
42// class SimpleTimeZone
43// *****************************************************************************
44
45
46SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID)
47: TimeZone(ID),
48 startMonth(0),
49 startDay(0),
50 startDayOfWeek(0),
51 startTime(0),
52 startTimeMode(WALL_TIME),
53 endTimeMode(WALL_TIME),
54 endMonth(0),
55 endDay(0),
56 endDayOfWeek(0),
57 endTime(0),
58 startYear(0),
59 rawOffset(rawOffsetGMT),
60 useDaylight(FALSE),
61 startMode(DOM_MODE),
62 endMode(DOM_MODE),
63 dstSavings(U_MILLIS_PER_HOUR)
64{
65}
66
67// -------------------------------------
68
69SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID,
70 int8_t savingsStartMonth, int8_t savingsStartDay,
71 int8_t savingsStartDayOfWeek, int32_t savingsStartTime,
72 int8_t savingsEndMonth, int8_t savingsEndDay,
73 int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
74 UErrorCode& status)
75: TimeZone(ID)
76{
77 construct(rawOffsetGMT,
78 savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
79 savingsStartTime, WALL_TIME,
80 savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
81 savingsEndTime, WALL_TIME,
82 U_MILLIS_PER_HOUR, status);
83}
84
85// -------------------------------------
86
87SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID,
88 int8_t savingsStartMonth, int8_t savingsStartDay,
89 int8_t savingsStartDayOfWeek, int32_t savingsStartTime,
90 int8_t savingsEndMonth, int8_t savingsEndDay,
91 int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
92 int32_t savingsDST, UErrorCode& status)
93: TimeZone(ID)
94{
95 construct(rawOffsetGMT,
96 savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
97 savingsStartTime, WALL_TIME,
98 savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
99 savingsEndTime, WALL_TIME,
100 savingsDST, status);
101}
102
103// -------------------------------------
104
105SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID,
106 int8_t savingsStartMonth, int8_t savingsStartDay,
107 int8_t savingsStartDayOfWeek, int32_t savingsStartTime,
108 TimeMode savingsStartTimeMode,
109 int8_t savingsEndMonth, int8_t savingsEndDay,
110 int8_t savingsEndDayOfWeek, int32_t savingsEndTime,
111 TimeMode savingsEndTimeMode,
112 int32_t savingsDST, UErrorCode& status)
113: TimeZone(ID)
114{
115 construct(rawOffsetGMT,
116 savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
117 savingsStartTime, savingsStartTimeMode,
118 savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
119 savingsEndTime, savingsEndTimeMode,
120 savingsDST, status);
121}
122
b75a7d8f
A
123/**
124 * Internal construction method.
125 */
126void SimpleTimeZone::construct(int32_t rawOffsetGMT,
127 int8_t savingsStartMonth,
128 int8_t savingsStartDay,
129 int8_t savingsStartDayOfWeek,
130 int32_t savingsStartTime,
131 TimeMode savingsStartTimeMode,
132 int8_t savingsEndMonth,
133 int8_t savingsEndDay,
134 int8_t savingsEndDayOfWeek,
135 int32_t savingsEndTime,
136 TimeMode savingsEndTimeMode,
137 int32_t savingsDST,
138 UErrorCode& status)
139{
140 this->rawOffset = rawOffsetGMT;
141 this->startMonth = savingsStartMonth;
142 this->startDay = savingsStartDay;
143 this->startDayOfWeek = savingsStartDayOfWeek;
144 this->startTime = savingsStartTime;
145 this->startTimeMode = savingsStartTimeMode;
146 this->endMonth = savingsEndMonth;
147 this->endDay = savingsEndDay;
148 this->endDayOfWeek = savingsEndDayOfWeek;
149 this->endTime = savingsEndTime;
150 this->endTimeMode = savingsEndTimeMode;
151 this->dstSavings = savingsDST;
152 this->startYear = 0;
153 this->startMode = DOM_MODE;
154 this->endMode = DOM_MODE;
155
156 decodeRules(status);
157
158 if (savingsDST <= 0) {
159 status = U_ILLEGAL_ARGUMENT_ERROR;
160 }
161}
162
163// -------------------------------------
164
165SimpleTimeZone::~SimpleTimeZone()
166{
167}
168
169// -------------------------------------
170
171// Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
172SimpleTimeZone::SimpleTimeZone(const SimpleTimeZone &source)
173: TimeZone(source)
174{
175 *this = source;
176}
177
178// -------------------------------------
179
180// Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
181SimpleTimeZone &
182SimpleTimeZone::operator=(const SimpleTimeZone &right)
183{
184 if (this != &right)
185 {
186 TimeZone::operator=(right);
187 rawOffset = right.rawOffset;
188 startMonth = right.startMonth;
189 startDay = right.startDay;
190 startDayOfWeek = right.startDayOfWeek;
191 startTime = right.startTime;
192 startTimeMode = right.startTimeMode;
193 startMode = right.startMode;
194 endMonth = right.endMonth;
195 endDay = right.endDay;
196 endDayOfWeek = right.endDayOfWeek;
197 endTime = right.endTime;
198 endTimeMode = right.endTimeMode;
199 endMode = right.endMode;
200 startYear = right.startYear;
201 dstSavings = right.dstSavings;
202 useDaylight = right.useDaylight;
203 }
204 return *this;
205}
206
207// -------------------------------------
208
209UBool
210SimpleTimeZone::operator==(const TimeZone& that) const
211{
212 return ((this == &that) ||
213 (getDynamicClassID() == that.getDynamicClassID() &&
214 TimeZone::operator==(that) &&
215 hasSameRules(that)));
216}
217
218// -------------------------------------
219
220// Called by TimeZone::createDefault() inside a Mutex - be careful.
221TimeZone*
222SimpleTimeZone::clone() const
223{
224 return new SimpleTimeZone(*this);
225}
226
227// -------------------------------------
228
229/**
230 * Sets the daylight savings starting year, that is, the year this time zone began
231 * observing its specified daylight savings time rules. The time zone is considered
232 * not to observe daylight savings time prior to that year; SimpleTimeZone doesn't
233 * support historical daylight-savings-time rules.
234 * @param year the daylight savings starting year.
235 */
236void
237SimpleTimeZone::setStartYear(int32_t year)
238{
239 startYear = year;
240}
241
242// -------------------------------------
243
244/**
245 * Sets the daylight savings starting rule. For example, in the U.S., Daylight Savings
246 * Time starts at the first Sunday in April, at 2 AM in standard time.
247 * Therefore, you can set the start rule by calling:
248 * setStartRule(TimeFields.APRIL, 1, TimeFields.SUNDAY, 2*60*60*1000);
249 * The dayOfWeekInMonth and dayOfWeek parameters together specify how to calculate
250 * the exact starting date. Their exact meaning depend on their respective signs,
251 * allowing various types of rules to be constructed, as follows:<ul>
252 * <li>If both dayOfWeekInMonth and dayOfWeek are positive, they specify the
253 * day of week in the month (e.g., (2, WEDNESDAY) is the second Wednesday
254 * of the month).
255 * <li>If dayOfWeek is positive and dayOfWeekInMonth is negative, they specify
256 * the day of week in the month counting backward from the end of the month.
257 * (e.g., (-1, MONDAY) is the last Monday in the month)
258 * <li>If dayOfWeek is zero and dayOfWeekInMonth is positive, dayOfWeekInMonth
259 * specifies the day of the month, regardless of what day of the week it is.
260 * (e.g., (10, 0) is the tenth day of the month)
261 * <li>If dayOfWeek is zero and dayOfWeekInMonth is negative, dayOfWeekInMonth
262 * specifies the day of the month counting backward from the end of the
263 * month, regardless of what day of the week it is (e.g., (-2, 0) is the
264 * next-to-last day of the month).
265 * <li>If dayOfWeek is negative and dayOfWeekInMonth is positive, they specify the
266 * first specified day of the week on or after the specfied day of the month.
267 * (e.g., (15, -SUNDAY) is the first Sunday after the 15th of the month
268 * [or the 15th itself if the 15th is a Sunday].)
269 * <li>If dayOfWeek and DayOfWeekInMonth are both negative, they specify the
270 * last specified day of the week on or before the specified day of the month.
271 * (e.g., (-20, -TUESDAY) is the last Tuesday before the 20th of the month
272 * [or the 20th itself if the 20th is a Tuesday].)</ul>
273 * @param month the daylight savings starting month. Month is 0-based.
274 * eg, 0 for January.
275 * @param dayOfWeekInMonth the daylight savings starting
276 * day-of-week-in-month. Please see the member description for an example.
277 * @param dayOfWeek the daylight savings starting day-of-week. Please see
278 * the member description for an example.
279 * @param time the daylight savings starting time. Please see the member
280 * description for an example.
281 */
282
283void
284SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek,
285 int32_t time, TimeMode mode, UErrorCode& status)
286{
287 startMonth = (int8_t)month;
288 startDay = (int8_t)dayOfWeekInMonth;
289 startDayOfWeek = (int8_t)dayOfWeek;
290 startTime = time;
291 startTimeMode = mode;
292 decodeStartRule(status);
293}
294
295// -------------------------------------
296
297void
298SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth,
299 int32_t time, TimeMode mode, UErrorCode& status)
300{
301 setStartRule(month, dayOfMonth, 0, time, mode, status);
302}
303
304// -------------------------------------
305
306void
307SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
308 int32_t time, TimeMode mode, UBool after, UErrorCode& status)
309{
310 setStartRule(month, after ? dayOfMonth : -dayOfMonth,
311 -dayOfWeek, time, mode, status);
312}
313
314// -------------------------------------
315
316/**
317 * Sets the daylight savings ending rule. For example, in the U.S., Daylight
318 * Savings Time ends at the last (-1) Sunday in October, at 2 AM in standard time.
319 * Therefore, you can set the end rule by calling:
320 * setEndRule(TimeFields.OCTOBER, -1, TimeFields.SUNDAY, 2*60*60*1000);
321 * Various other types of rules can be specified by manipulating the dayOfWeek
322 * and dayOfWeekInMonth parameters. For complete details, see the documentation
323 * for setStartRule().
324 * @param month the daylight savings ending month. Month is 0-based.
325 * eg, 0 for January.
326 * @param dayOfWeekInMonth the daylight savings ending
327 * day-of-week-in-month. See setStartRule() for a complete explanation.
328 * @param dayOfWeek the daylight savings ending day-of-week. See setStartRule()
329 * for a complete explanation.
330 * @param time the daylight savings ending time. Please see the member
331 * description for an example.
332 */
333
334void
335SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek,
336 int32_t time, TimeMode mode, UErrorCode& status)
337{
338 endMonth = (int8_t)month;
339 endDay = (int8_t)dayOfWeekInMonth;
340 endDayOfWeek = (int8_t)dayOfWeek;
341 endTime = time;
342 endTimeMode = mode;
343 decodeEndRule(status);
344}
345
346// -------------------------------------
347
348void
349SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth,
350 int32_t time, TimeMode mode, UErrorCode& status)
351{
352 setEndRule(month, dayOfMonth, 0, time, mode, status);
353}
354
355// -------------------------------------
356
357void
358SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
359 int32_t time, TimeMode mode, UBool after, UErrorCode& status)
360{
361 setEndRule(month, after ? dayOfMonth : -dayOfMonth,
362 -dayOfWeek, time, mode, status);
363}
364
365// -------------------------------------
366
367int32_t
368SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
369 uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const
370{
374ca955 371 // Check the month before indexing into STATICMONTHLENGTH. This
b75a7d8f
A
372 // duplicates the test that occurs in the 7-argument getOffset(),
373 // however, this is unavoidable. We don't mind because this method, in
374 // fact, should not be called; internal code should always call the
375 // 7-argument getOffset(), and outside code should use Calendar.get(int
376 // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
377 // this method because it's public API. - liu 8/10/98
378 if(month < UCAL_JANUARY || month > UCAL_DECEMBER) {
379 status = U_ILLEGAL_ARGUMENT_ERROR;
380 return 0;
381 }
382
374ca955 383 return getOffset(era, year, month, day, dayOfWeek, millis, STATICMONTHLENGTH[month], status);
b75a7d8f
A
384}
385
386int32_t
387SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
388 uint8_t dayOfWeek, int32_t millis,
389 int32_t monthLength, UErrorCode& status) const {
374ca955 390 // Check the month before indexing into STATICMONTHLENGTH. This
b75a7d8f
A
391 // duplicates a test that occurs in the 9-argument getOffset(),
392 // however, this is unavoidable. We don't mind because this method, in
393 // fact, should not be called; internal code should always call the
394 // 9-argument getOffset(), and outside code should use Calendar.get(int
395 // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
396 // this method because it's public API. - liu 8/10/98
397 if (month < UCAL_JANUARY
398 || month > UCAL_DECEMBER) {
399 status = U_ILLEGAL_ARGUMENT_ERROR;
400 return -1;
401 }
402
403 // TODO FIX We don't handle leap years yet!
374ca955 404 int32_t prevMonthLength = (month >= 1) ? STATICMONTHLENGTH[month - 1] : 31;
b75a7d8f
A
405
406 return getOffset(era, year, month, day, dayOfWeek, millis,
407 monthLength, prevMonthLength, status);
408}
409
410int32_t
411SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
412 uint8_t dayOfWeek, int32_t millis,
413 int32_t monthLength, int32_t prevMonthLength,
414 UErrorCode& status) const
415{
416 if(U_FAILURE(status)) return 0;
417
418 if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC)
419 || month < UCAL_JANUARY
420 || month > UCAL_DECEMBER
421 || day < 1
422 || day > monthLength
423 || dayOfWeek < UCAL_SUNDAY
424 || dayOfWeek > UCAL_SATURDAY
425 || millis < 0
426 || millis >= U_MILLIS_PER_DAY
427 || monthLength < 28
374ca955
A
428 || monthLength > 31
429 || prevMonthLength < 28
430 || prevMonthLength > 31) {
b75a7d8f
A
431 status = U_ILLEGAL_ARGUMENT_ERROR;
432 return -1;
433 }
434
435 int32_t result = rawOffset;
436
437 // Bail out if we are before the onset of daylight savings time
438 if(!useDaylight || year < startYear || era != GregorianCalendar::AD)
439 return result;
440
441 // Check for southern hemisphere. We assume that the start and end
442 // month are different.
443 UBool southern = (startMonth > endMonth);
444
445 // Compare the date to the starting and ending rules.+1 = date>rule, -1
446 // = date<rule, 0 = date==rule.
447 int32_t startCompare = compareToRule((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength,
448 (int8_t)day, (int8_t)dayOfWeek, millis,
449 startTimeMode == UTC_TIME ? -rawOffset : 0,
450 startMode, (int8_t)startMonth, (int8_t)startDayOfWeek,
451 (int8_t)startDay, startTime);
452 int32_t endCompare = 0;
453
454 /* We don't always have to compute endCompare. For many instances,
455 * startCompare is enough to determine if we are in DST or not. In the
456 * northern hemisphere, if we are before the start rule, we can't have
457 * DST. In the southern hemisphere, if we are after the start rule, we
458 * must have DST. This is reflected in the way the next if statement
459 * (not the one immediately following) short circuits. */
460 if(southern != (startCompare >= 0)) {
461 endCompare = compareToRule((int8_t)month, (int8_t)monthLength, (int8_t)prevMonthLength,
462 (int8_t)day, (int8_t)dayOfWeek, millis,
463 endTimeMode == WALL_TIME ? dstSavings :
464 (endTimeMode == UTC_TIME ? -rawOffset : 0),
465 endMode, (int8_t)endMonth, (int8_t)endDayOfWeek,
466 (int8_t)endDay, endTime);
467 }
468
469 // Check for both the northern and southern hemisphere cases. We
470 // assume that in the northern hemisphere, the start rule is before the
471 // end rule within the calendar year, and vice versa for the southern
472 // hemisphere.
473 if ((!southern && (startCompare >= 0 && endCompare < 0)) ||
474 (southern && (startCompare >= 0 || endCompare < 0)))
475 result += dstSavings;
476
477 return result;
478}
479
480// -------------------------------------
481
482/**
483 * Compare a given date in the year to a rule. Return 1, 0, or -1, depending
484 * on whether the date is after, equal to, or before the rule date. The
485 * millis are compared directly against the ruleMillis, so any
486 * standard-daylight adjustments must be handled by the caller.
487 *
488 * @return 1 if the date is after the rule date, -1 if the date is before
489 * the rule date, or 0 if the date is equal to the rule date.
490 */
491int32_t
492SimpleTimeZone::compareToRule(int8_t month, int8_t monthLen, int8_t prevMonthLen,
493 int8_t dayOfMonth,
494 int8_t dayOfWeek, int32_t millis, int32_t millisDelta,
495 EMode ruleMode, int8_t ruleMonth, int8_t ruleDayOfWeek,
496 int8_t ruleDay, int32_t ruleMillis)
497{
498 // Make adjustments for startTimeMode and endTimeMode
499 millis += millisDelta;
500 while (millis >= U_MILLIS_PER_DAY) {
501 millis -= U_MILLIS_PER_DAY;
502 ++dayOfMonth;
503 dayOfWeek = (int8_t)(1 + (dayOfWeek % 7)); // dayOfWeek is one-based
504 if (dayOfMonth > monthLen) {
505 dayOfMonth = 1;
506 /* When incrementing the month, it is desirible to overflow
507 * from DECEMBER to DECEMBER+1, since we use the result to
508 * compare against a real month. Wraparound of the value
509 * leads to bug 4173604. */
510 ++month;
511 }
512 }
513 while (millis < 0) {
514 millis += U_MILLIS_PER_DAY;
515 --dayOfMonth;
516 dayOfWeek = (int8_t)(1 + ((dayOfWeek+5) % 7)); // dayOfWeek is one-based
517 if (dayOfMonth < 1) {
518 dayOfMonth = prevMonthLen;
519 --month;
520 }
521 }
522
523 // first compare months. If they're different, we don't have to worry about days
524 // and times
525 if (month < ruleMonth) return -1;
526 else if (month > ruleMonth) return 1;
527
528 // calculate the actual day of month for the rule
529 int32_t ruleDayOfMonth = 0;
530 switch (ruleMode)
531 {
532 // if the mode is day-of-month, the day of month is given
533 case DOM_MODE:
534 ruleDayOfMonth = ruleDay;
535 break;
536
537 // if the mode is day-of-week-in-month, calculate the day-of-month from it
538 case DOW_IN_MONTH_MODE:
539 // In this case ruleDay is the day-of-week-in-month (this code is using
540 // the dayOfWeek and dayOfMonth parameters to figure out the day-of-week
541 // of the first day of the month, so it's trusting that they're really
542 // consistent with each other)
543 if (ruleDay > 0)
544 ruleDayOfMonth = 1 + (ruleDay - 1) * 7 +
545 (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7;
546
547 // if ruleDay is negative (we assume it's not zero here), we have to do
548 // the same calculation figuring backward from the last day of the month.
374ca955 549 // (STATICMONTHLENGTH gives us that last day. We don't take leap years
b75a7d8f
A
550 // into account, so this may not work right for February.)
551 else
552 {
553 // (again, this code is trusting that dayOfMonth and dayOfMonth are
554 // consistent with each other here, since we're using them to figure
555 // the day of week of the first of the month)
556 ruleDayOfMonth = monthLen + (ruleDay + 1) * 7 -
557 (7 + (dayOfWeek + monthLen - dayOfMonth) - ruleDayOfWeek) % 7;
558 }
559 break;
560
561 case DOW_GE_DOM_MODE:
562 ruleDayOfMonth = ruleDay +
563 (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7;
564 break;
565
566 case DOW_LE_DOM_MODE:
567 ruleDayOfMonth = ruleDay -
568 (49 - ruleDayOfWeek + ruleDay + dayOfWeek - dayOfMonth) % 7;
569 // Note at this point ruleDayOfMonth may be <1, although it will
570 // be >=1 for well-formed rules.
571 break;
572 }
573
574 // now that we have a real day-in-month for the rule, we can compare days...
575 if (dayOfMonth < ruleDayOfMonth) return -1;
576 else if (dayOfMonth > ruleDayOfMonth) return 1;
577
578 // ...and if they're equal, we compare times
579 if (millis < ruleMillis) return -1;
580 else if (millis > ruleMillis) return 1;
581 else return 0;
582}
583
584// -------------------------------------
585
586int32_t
587SimpleTimeZone::getRawOffset() const
588{
589 return rawOffset;
590}
591
592// -------------------------------------
593
594void
595SimpleTimeZone::setRawOffset(int32_t offsetMillis)
596{
597 rawOffset = offsetMillis;
598}
599
600// -------------------------------------
601
602void
603SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST, UErrorCode& status)
604{
605 if (millisSavedDuringDST <= 0) {
606 status = U_ILLEGAL_ARGUMENT_ERROR;
607 }
608 else {
609 dstSavings = millisSavedDuringDST;
610 }
611}
612
613// -------------------------------------
614
615int32_t
616SimpleTimeZone::getDSTSavings() const
617{
618 return dstSavings;
619}
620
621// -------------------------------------
622
623UBool
624SimpleTimeZone::useDaylightTime() const
625{
626 return useDaylight;
627}
628
629// -------------------------------------
630
631/**
632 * Overrides TimeZone
633 * Queries if the given date is in Daylight Savings Time.
634 */
635UBool SimpleTimeZone::inDaylightTime(UDate date, UErrorCode& status) const
636{
637 // This method is wasteful since it creates a new GregorianCalendar and
638 // deletes it each time it is called. However, this is a deprecated method
639 // and provided only for Java compatibility as of 8/6/97 [LIU].
640 if (U_FAILURE(status)) return FALSE;
641 GregorianCalendar *gc = new GregorianCalendar(*this, status);
642 /* test for NULL */
643 if (gc == 0) {
644 status = U_MEMORY_ALLOCATION_ERROR;
645 return FALSE;
646 }
647 gc->setTime(date, status);
648 UBool result = gc->inDaylightTime(status);
649 delete gc;
650 return result;
651}
652
653// -------------------------------------
654
655/**
656 * Return true if this zone has the same rules and offset as another zone.
657 * @param other the TimeZone object to be compared with
658 * @return true if the given zone has the same rules and offset as this one
659 */
660UBool
661SimpleTimeZone::hasSameRules(const TimeZone& other) const
662{
663 if (this == &other) return TRUE;
664 if (other.getDynamicClassID() != SimpleTimeZone::getStaticClassID()) return FALSE;
665 SimpleTimeZone *that = (SimpleTimeZone*)&other;
666 return rawOffset == that->rawOffset &&
667 useDaylight == that->useDaylight &&
668 (!useDaylight
669 // Only check rules if using DST
670 || (dstSavings == that->dstSavings &&
671 startMode == that->startMode &&
672 startMonth == that->startMonth &&
673 startDay == that->startDay &&
674 startDayOfWeek == that->startDayOfWeek &&
675 startTime == that->startTime &&
676 startTimeMode == that->startTimeMode &&
677 endMode == that->endMode &&
678 endMonth == that->endMonth &&
679 endDay == that->endDay &&
680 endDayOfWeek == that->endDayOfWeek &&
681 endTime == that->endTime &&
682 endTimeMode == that->endTimeMode &&
683 startYear == that->startYear));
684}
685
686// -------------------------------------
687
688//----------------------------------------------------------------------
689// Rule representation
690//
691// We represent the following flavors of rules:
692// 5 the fifth of the month
693// lastSun the last Sunday in the month
694// lastMon the last Monday in the month
695// Sun>=8 first Sunday on or after the eighth
696// Sun<=25 last Sunday on or before the 25th
697// This is further complicated by the fact that we need to remain
698// backward compatible with the 1.1 FCS. Finally, we need to minimize
699// API changes. In order to satisfy these requirements, we support
700// three representation systems, and we translate between them.
701//
702// INTERNAL REPRESENTATION
703// This is the format SimpleTimeZone objects take after construction or
704// streaming in is complete. Rules are represented directly, using an
705// unencoded format. We will discuss the start rule only below; the end
706// rule is analogous.
707// startMode Takes on enumerated values DAY_OF_MONTH,
708// DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
709// startDay The day of the month, or for DOW_IN_MONTH mode, a
710// value indicating which DOW, such as +1 for first,
711// +2 for second, -1 for last, etc.
712// startDayOfWeek The day of the week. Ignored for DAY_OF_MONTH.
713//
714// ENCODED REPRESENTATION
715// This is the format accepted by the constructor and by setStartRule()
716// and setEndRule(). It uses various combinations of positive, negative,
717// and zero values to encode the different rules. This representation
718// allows us to specify all the different rule flavors without altering
719// the API.
720// MODE startMonth startDay startDayOfWeek
721// DOW_IN_MONTH_MODE >=0 !=0 >0
722// DOM_MODE >=0 >0 ==0
723// DOW_GE_DOM_MODE >=0 >0 <0
724// DOW_LE_DOM_MODE >=0 <0 <0
725// (no DST) don't care ==0 don't care
726//
727// STREAMED REPRESENTATION
728// We must retain binary compatibility with the 1.1 FCS. The 1.1 code only
729// handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
730// flag useDaylight. When we stream an object out, we translate into an
731// approximate DOW_IN_MONTH_MODE representation so the object can be parsed
732// and used by 1.1 code. Following that, we write out the full
733// representation separately so that contemporary code can recognize and
734// parse it. The full representation is written in a "packed" format,
735// consisting of a version number, a length, and an array of bytes. Future
736// versions of this class may specify different versions. If they wish to
737// include additional data, they should do so by storing them after the
738// packed representation below.
739//----------------------------------------------------------------------
740
741/**
742 * Given a set of encoded rules in startDay and startDayOfMonth, decode
743 * them and set the startMode appropriately. Do the same for endDay and
744 * endDayOfMonth. Upon entry, the day of week variables may be zero or
745 * negative, in order to indicate special modes. The day of month
746 * variables may also be negative. Upon exit, the mode variables will be
747 * set, and the day of week and day of month variables will be positive.
748 * This method also recognizes a startDay or endDay of zero as indicating
749 * no DST.
750 */
751void
752SimpleTimeZone::decodeRules(UErrorCode& status)
753{
754 decodeStartRule(status);
755 decodeEndRule(status);
756}
757
758/**
759 * Decode the start rule and validate the parameters. The parameters are
760 * expected to be in encoded form, which represents the various rule modes
761 * by negating or zeroing certain values. Representation formats are:
762 * <p>
763 * <pre>
764 * DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST
765 * ------------ ----- -------- -------- ----------
766 * month 0..11 same same same don't care
767 * day -5..5 1..31 1..31 -1..-31 0
768 * dayOfWeek 1..7 0 -1..-7 -1..-7 don't care
769 * time 0..ONEDAY same same same don't care
770 * </pre>
771 * The range for month does not include UNDECIMBER since this class is
772 * really specific to GregorianCalendar, which does not use that month.
773 * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
774 * end rule is an exclusive limit point. That is, the range of times that
775 * are in DST include those >= the start and < the end. For this reason,
776 * it should be possible to specify an end of ONEDAY in order to include the
777 * entire day. Although this is equivalent to time 0 of the following day,
778 * it's not always possible to specify that, for example, on December 31.
779 * While arguably the start range should still be 0..ONEDAY-1, we keep
780 * the start and end ranges the same for consistency.
781 */
782void
783SimpleTimeZone::decodeStartRule(UErrorCode& status)
784{
785 if(U_FAILURE(status)) return;
786
787 useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE);
788 if (useDaylight && dstSavings == 0) {
789 dstSavings = U_MILLIS_PER_HOUR;
790 }
791 if (startDay != 0) {
792 if (startMonth < UCAL_JANUARY || startMonth > UCAL_DECEMBER) {
793 status = U_ILLEGAL_ARGUMENT_ERROR;
794 return;
795 }
796 if (startTime < 0 || startTime > U_MILLIS_PER_DAY ||
797 startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) {
798 status = U_ILLEGAL_ARGUMENT_ERROR;
799 return;
800 }
801 if (startDayOfWeek == 0) {
802 startMode = DOM_MODE;
803 } else {
804 if (startDayOfWeek > 0) {
805 startMode = DOW_IN_MONTH_MODE;
806 } else {
807 startDayOfWeek = (int8_t)-startDayOfWeek;
808 if (startDay > 0) {
809 startMode = DOW_GE_DOM_MODE;
810 } else {
811 startDay = (int8_t)-startDay;
812 startMode = DOW_LE_DOM_MODE;
813 }
814 }
815 if (startDayOfWeek > UCAL_SATURDAY) {
816 status = U_ILLEGAL_ARGUMENT_ERROR;
817 return;
818 }
819 }
820 if (startMode == DOW_IN_MONTH_MODE) {
821 if (startDay < -5 || startDay > 5) {
822 status = U_ILLEGAL_ARGUMENT_ERROR;
823 return;
824 }
374ca955 825 } else if (startDay > STATICMONTHLENGTH[startMonth]) {
b75a7d8f
A
826 status = U_ILLEGAL_ARGUMENT_ERROR;
827 return;
828 }
829 }
830}
831
832/**
833 * Decode the end rule and validate the parameters. This method is exactly
834 * analogous to decodeStartRule().
835 * @see decodeStartRule
836 */
837void
838SimpleTimeZone::decodeEndRule(UErrorCode& status)
839{
840 if(U_FAILURE(status)) return;
841
842 useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE);
843 if (useDaylight && dstSavings == 0) {
844 dstSavings = U_MILLIS_PER_HOUR;
845 }
846 if (endDay != 0) {
847 if (endMonth < UCAL_JANUARY || endMonth > UCAL_DECEMBER) {
848 status = U_ILLEGAL_ARGUMENT_ERROR;
849 return;
850 }
851 if (endTime < 0 || endTime > U_MILLIS_PER_DAY ||
852 endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) {
853 status = U_ILLEGAL_ARGUMENT_ERROR;
854 return;
855 }
856 if (endDayOfWeek == 0) {
857 endMode = DOM_MODE;
858 } else {
859 if (endDayOfWeek > 0) {
860 endMode = DOW_IN_MONTH_MODE;
861 } else {
862 endDayOfWeek = (int8_t)-endDayOfWeek;
863 if (endDay > 0) {
864 endMode = DOW_GE_DOM_MODE;
865 } else {
866 endDay = (int8_t)-endDay;
867 endMode = DOW_LE_DOM_MODE;
868 }
869 }
870 if (endDayOfWeek > UCAL_SATURDAY) {
871 status = U_ILLEGAL_ARGUMENT_ERROR;
872 return;
873 }
874 }
875 if (endMode == DOW_IN_MONTH_MODE) {
876 if (endDay < -5 || endDay > 5) {
877 status = U_ILLEGAL_ARGUMENT_ERROR;
878 return;
879 }
374ca955 880 } else if (endDay > STATICMONTHLENGTH[endMonth]) {
b75a7d8f
A
881 status = U_ILLEGAL_ARGUMENT_ERROR;
882 return;
883 }
884 }
885}
886
887U_NAMESPACE_END
888
889#endif /* #if !UCONFIG_NO_FORMATTING */
890
891//eof