2 *******************************************************************************
3 * Copyright (C) 1997-2005, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
9 * Modification History:
11 * Date Name Description
12 * 12/05/96 clhuang Creation.
13 * 04/21/97 aliu Fixed miscellaneous bugs found by inspection and
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 ********************************************************************************
24 #include "unicode/utypes.h"
26 #if !UCONFIG_NO_FORMATTING
28 #include "unicode/simpletz.h"
29 #include "unicode/gregocal.h"
30 #include "unicode/smpdtfmt.h"
34 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleTimeZone
)
36 // WARNING: assumes that no rule is measured from the end of February,
37 // since we don't handle leap years. Could handle assuming always
38 // Gregorian, since we know they didn't have daylight time when
39 // Gregorian calendar started.
40 const int8_t SimpleTimeZone::STATICMONTHLENGTH
[] = {31,29,31,30,31,30,31,31,30,31,30,31};
42 // *****************************************************************************
43 // class SimpleTimeZone
44 // *****************************************************************************
47 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT
, const UnicodeString
& ID
)
53 startTimeMode(WALL_TIME
),
54 endTimeMode(WALL_TIME
),
60 rawOffset(rawOffsetGMT
),
64 dstSavings(U_MILLIS_PER_HOUR
)
68 // -------------------------------------
70 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT
, const UnicodeString
& ID
,
71 int8_t savingsStartMonth
, int8_t savingsStartDay
,
72 int8_t savingsStartDayOfWeek
, int32_t savingsStartTime
,
73 int8_t savingsEndMonth
, int8_t savingsEndDay
,
74 int8_t savingsEndDayOfWeek
, int32_t savingsEndTime
,
78 construct(rawOffsetGMT
,
79 savingsStartMonth
, savingsStartDay
, savingsStartDayOfWeek
,
80 savingsStartTime
, WALL_TIME
,
81 savingsEndMonth
, savingsEndDay
, savingsEndDayOfWeek
,
82 savingsEndTime
, WALL_TIME
,
83 U_MILLIS_PER_HOUR
, status
);
86 // -------------------------------------
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 int32_t savingsDST
, UErrorCode
& status
)
96 construct(rawOffsetGMT
,
97 savingsStartMonth
, savingsStartDay
, savingsStartDayOfWeek
,
98 savingsStartTime
, WALL_TIME
,
99 savingsEndMonth
, savingsEndDay
, savingsEndDayOfWeek
,
100 savingsEndTime
, WALL_TIME
,
104 // -------------------------------------
106 SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT
, const UnicodeString
& ID
,
107 int8_t savingsStartMonth
, int8_t savingsStartDay
,
108 int8_t savingsStartDayOfWeek
, int32_t savingsStartTime
,
109 TimeMode savingsStartTimeMode
,
110 int8_t savingsEndMonth
, int8_t savingsEndDay
,
111 int8_t savingsEndDayOfWeek
, int32_t savingsEndTime
,
112 TimeMode savingsEndTimeMode
,
113 int32_t savingsDST
, UErrorCode
& status
)
116 construct(rawOffsetGMT
,
117 savingsStartMonth
, savingsStartDay
, savingsStartDayOfWeek
,
118 savingsStartTime
, savingsStartTimeMode
,
119 savingsEndMonth
, savingsEndDay
, savingsEndDayOfWeek
,
120 savingsEndTime
, savingsEndTimeMode
,
125 * Internal construction method.
127 void SimpleTimeZone::construct(int32_t rawOffsetGMT
,
128 int8_t savingsStartMonth
,
129 int8_t savingsStartDay
,
130 int8_t savingsStartDayOfWeek
,
131 int32_t savingsStartTime
,
132 TimeMode savingsStartTimeMode
,
133 int8_t savingsEndMonth
,
134 int8_t savingsEndDay
,
135 int8_t savingsEndDayOfWeek
,
136 int32_t savingsEndTime
,
137 TimeMode savingsEndTimeMode
,
141 this->rawOffset
= rawOffsetGMT
;
142 this->startMonth
= savingsStartMonth
;
143 this->startDay
= savingsStartDay
;
144 this->startDayOfWeek
= savingsStartDayOfWeek
;
145 this->startTime
= savingsStartTime
;
146 this->startTimeMode
= savingsStartTimeMode
;
147 this->endMonth
= savingsEndMonth
;
148 this->endDay
= savingsEndDay
;
149 this->endDayOfWeek
= savingsEndDayOfWeek
;
150 this->endTime
= savingsEndTime
;
151 this->endTimeMode
= savingsEndTimeMode
;
152 this->dstSavings
= savingsDST
;
154 this->startMode
= DOM_MODE
;
155 this->endMode
= DOM_MODE
;
159 if (savingsDST
<= 0) {
160 status
= U_ILLEGAL_ARGUMENT_ERROR
;
164 // -------------------------------------
166 SimpleTimeZone::~SimpleTimeZone()
170 // -------------------------------------
172 // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
173 SimpleTimeZone::SimpleTimeZone(const SimpleTimeZone
&source
)
179 // -------------------------------------
181 // Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
183 SimpleTimeZone::operator=(const SimpleTimeZone
&right
)
187 TimeZone::operator=(right
);
188 rawOffset
= right
.rawOffset
;
189 startMonth
= right
.startMonth
;
190 startDay
= right
.startDay
;
191 startDayOfWeek
= right
.startDayOfWeek
;
192 startTime
= right
.startTime
;
193 startTimeMode
= right
.startTimeMode
;
194 startMode
= right
.startMode
;
195 endMonth
= right
.endMonth
;
196 endDay
= right
.endDay
;
197 endDayOfWeek
= right
.endDayOfWeek
;
198 endTime
= right
.endTime
;
199 endTimeMode
= right
.endTimeMode
;
200 endMode
= right
.endMode
;
201 startYear
= right
.startYear
;
202 dstSavings
= right
.dstSavings
;
203 useDaylight
= right
.useDaylight
;
208 // -------------------------------------
211 SimpleTimeZone::operator==(const TimeZone
& that
) const
213 return ((this == &that
) ||
214 (getDynamicClassID() == that
.getDynamicClassID() &&
215 TimeZone::operator==(that
) &&
216 hasSameRules(that
)));
219 // -------------------------------------
221 // Called by TimeZone::createDefault() inside a Mutex - be careful.
223 SimpleTimeZone::clone() const
225 return new SimpleTimeZone(*this);
228 // -------------------------------------
231 * Sets the daylight savings starting year, that is, the year this time zone began
232 * observing its specified daylight savings time rules. The time zone is considered
233 * not to observe daylight savings time prior to that year; SimpleTimeZone doesn't
234 * support historical daylight-savings-time rules.
235 * @param year the daylight savings starting year.
238 SimpleTimeZone::setStartYear(int32_t year
)
243 // -------------------------------------
246 * Sets the daylight savings starting rule. For example, in the U.S., Daylight Savings
247 * Time starts at the first Sunday in April, at 2 AM in standard time.
248 * Therefore, you can set the start rule by calling:
249 * setStartRule(TimeFields.APRIL, 1, TimeFields.SUNDAY, 2*60*60*1000);
250 * The dayOfWeekInMonth and dayOfWeek parameters together specify how to calculate
251 * the exact starting date. Their exact meaning depend on their respective signs,
252 * allowing various types of rules to be constructed, as follows:<ul>
253 * <li>If both dayOfWeekInMonth and dayOfWeek are positive, they specify the
254 * day of week in the month (e.g., (2, WEDNESDAY) is the second Wednesday
256 * <li>If dayOfWeek is positive and dayOfWeekInMonth is negative, they specify
257 * the day of week in the month counting backward from the end of the month.
258 * (e.g., (-1, MONDAY) is the last Monday in the month)
259 * <li>If dayOfWeek is zero and dayOfWeekInMonth is positive, dayOfWeekInMonth
260 * specifies the day of the month, regardless of what day of the week it is.
261 * (e.g., (10, 0) is the tenth day of the month)
262 * <li>If dayOfWeek is zero and dayOfWeekInMonth is negative, dayOfWeekInMonth
263 * specifies the day of the month counting backward from the end of the
264 * month, regardless of what day of the week it is (e.g., (-2, 0) is the
265 * next-to-last day of the month).
266 * <li>If dayOfWeek is negative and dayOfWeekInMonth is positive, they specify the
267 * first specified day of the week on or after the specfied day of the month.
268 * (e.g., (15, -SUNDAY) is the first Sunday after the 15th of the month
269 * [or the 15th itself if the 15th is a Sunday].)
270 * <li>If dayOfWeek and DayOfWeekInMonth are both negative, they specify the
271 * last specified day of the week on or before the specified day of the month.
272 * (e.g., (-20, -TUESDAY) is the last Tuesday before the 20th of the month
273 * [or the 20th itself if the 20th is a Tuesday].)</ul>
274 * @param month the daylight savings starting month. Month is 0-based.
276 * @param dayOfWeekInMonth the daylight savings starting
277 * day-of-week-in-month. Please see the member description for an example.
278 * @param dayOfWeek the daylight savings starting day-of-week. Please see
279 * the member description for an example.
280 * @param time the daylight savings starting time. Please see the member
281 * description for an example.
285 SimpleTimeZone::setStartRule(int32_t month
, int32_t dayOfWeekInMonth
, int32_t dayOfWeek
,
286 int32_t time
, TimeMode mode
, UErrorCode
& status
)
288 startMonth
= (int8_t)month
;
289 startDay
= (int8_t)dayOfWeekInMonth
;
290 startDayOfWeek
= (int8_t)dayOfWeek
;
292 startTimeMode
= mode
;
293 decodeStartRule(status
);
296 // -------------------------------------
299 SimpleTimeZone::setStartRule(int32_t month
, int32_t dayOfMonth
,
300 int32_t time
, TimeMode mode
, UErrorCode
& status
)
302 setStartRule(month
, dayOfMonth
, 0, time
, mode
, status
);
305 // -------------------------------------
308 SimpleTimeZone::setStartRule(int32_t month
, int32_t dayOfMonth
, int32_t dayOfWeek
,
309 int32_t time
, TimeMode mode
, UBool after
, UErrorCode
& status
)
311 setStartRule(month
, after
? dayOfMonth
: -dayOfMonth
,
312 -dayOfWeek
, time
, mode
, status
);
315 // -------------------------------------
318 * Sets the daylight savings ending rule. For example, in the U.S., Daylight
319 * Savings Time ends at the last (-1) Sunday in October, at 2 AM in standard time.
320 * Therefore, you can set the end rule by calling:
321 * setEndRule(TimeFields.OCTOBER, -1, TimeFields.SUNDAY, 2*60*60*1000);
322 * Various other types of rules can be specified by manipulating the dayOfWeek
323 * and dayOfWeekInMonth parameters. For complete details, see the documentation
324 * for setStartRule().
325 * @param month the daylight savings ending month. Month is 0-based.
327 * @param dayOfWeekInMonth the daylight savings ending
328 * day-of-week-in-month. See setStartRule() for a complete explanation.
329 * @param dayOfWeek the daylight savings ending day-of-week. See setStartRule()
330 * for a complete explanation.
331 * @param time the daylight savings ending time. Please see the member
332 * description for an example.
336 SimpleTimeZone::setEndRule(int32_t month
, int32_t dayOfWeekInMonth
, int32_t dayOfWeek
,
337 int32_t time
, TimeMode mode
, UErrorCode
& status
)
339 endMonth
= (int8_t)month
;
340 endDay
= (int8_t)dayOfWeekInMonth
;
341 endDayOfWeek
= (int8_t)dayOfWeek
;
344 decodeEndRule(status
);
347 // -------------------------------------
350 SimpleTimeZone::setEndRule(int32_t month
, int32_t dayOfMonth
,
351 int32_t time
, TimeMode mode
, UErrorCode
& status
)
353 setEndRule(month
, dayOfMonth
, 0, time
, mode
, status
);
356 // -------------------------------------
359 SimpleTimeZone::setEndRule(int32_t month
, int32_t dayOfMonth
, int32_t dayOfWeek
,
360 int32_t time
, TimeMode mode
, UBool after
, UErrorCode
& status
)
362 setEndRule(month
, after
? dayOfMonth
: -dayOfMonth
,
363 -dayOfWeek
, time
, mode
, status
);
366 // -------------------------------------
369 SimpleTimeZone::getOffset(uint8_t era
, int32_t year
, int32_t month
, int32_t day
,
370 uint8_t dayOfWeek
, int32_t millis
, UErrorCode
& status
) const
372 // Check the month before indexing into STATICMONTHLENGTH. This
373 // duplicates the test that occurs in the 7-argument getOffset(),
374 // however, this is unavoidable. We don't mind because this method, in
375 // fact, should not be called; internal code should always call the
376 // 7-argument getOffset(), and outside code should use Calendar.get(int
377 // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
378 // this method because it's public API. - liu 8/10/98
379 if(month
< UCAL_JANUARY
|| month
> UCAL_DECEMBER
) {
380 status
= U_ILLEGAL_ARGUMENT_ERROR
;
384 return getOffset(era
, year
, month
, day
, dayOfWeek
, millis
, STATICMONTHLENGTH
[month
], status
);
388 SimpleTimeZone::getOffset(uint8_t era
, int32_t year
, int32_t month
, int32_t day
,
389 uint8_t dayOfWeek
, int32_t millis
,
390 int32_t monthLength
, UErrorCode
& status
) const {
391 // Check the month before indexing into STATICMONTHLENGTH. This
392 // duplicates a test that occurs in the 9-argument getOffset(),
393 // however, this is unavoidable. We don't mind because this method, in
394 // fact, should not be called; internal code should always call the
395 // 9-argument getOffset(), and outside code should use Calendar.get(int
396 // field) with fields ZONE_OFFSET and DST_OFFSET. We can't get rid of
397 // this method because it's public API. - liu 8/10/98
398 if (month
< UCAL_JANUARY
399 || month
> UCAL_DECEMBER
) {
400 status
= U_ILLEGAL_ARGUMENT_ERROR
;
404 // TODO FIX We don't handle leap years yet!
405 int32_t prevMonthLength
= (month
>= 1) ? STATICMONTHLENGTH
[month
- 1] : 31;
407 return getOffset(era
, year
, month
, day
, dayOfWeek
, millis
,
408 monthLength
, prevMonthLength
, status
);
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
, int32_t prevMonthLength
,
415 UErrorCode
& status
) const
417 if(U_FAILURE(status
)) return 0;
419 if ((era
!= GregorianCalendar::AD
&& era
!= GregorianCalendar::BC
)
420 || month
< UCAL_JANUARY
421 || month
> UCAL_DECEMBER
424 || dayOfWeek
< UCAL_SUNDAY
425 || dayOfWeek
> UCAL_SATURDAY
427 || millis
>= U_MILLIS_PER_DAY
430 || prevMonthLength
< 28
431 || prevMonthLength
> 31) {
432 status
= U_ILLEGAL_ARGUMENT_ERROR
;
436 int32_t result
= rawOffset
;
438 // Bail out if we are before the onset of daylight savings time
439 if(!useDaylight
|| year
< startYear
|| era
!= GregorianCalendar::AD
)
442 // Check for southern hemisphere. We assume that the start and end
443 // month are different.
444 UBool southern
= (startMonth
> endMonth
);
446 // Compare the date to the starting and ending rules.+1 = date>rule, -1
447 // = date<rule, 0 = date==rule.
448 int32_t startCompare
= compareToRule((int8_t)month
, (int8_t)monthLength
, (int8_t)prevMonthLength
,
449 (int8_t)day
, (int8_t)dayOfWeek
, millis
,
450 startTimeMode
== UTC_TIME
? -rawOffset
: 0,
451 startMode
, (int8_t)startMonth
, (int8_t)startDayOfWeek
,
452 (int8_t)startDay
, startTime
);
453 int32_t endCompare
= 0;
455 /* We don't always have to compute endCompare. For many instances,
456 * startCompare is enough to determine if we are in DST or not. In the
457 * northern hemisphere, if we are before the start rule, we can't have
458 * DST. In the southern hemisphere, if we are after the start rule, we
459 * must have DST. This is reflected in the way the next if statement
460 * (not the one immediately following) short circuits. */
461 if(southern
!= (startCompare
>= 0)) {
462 endCompare
= compareToRule((int8_t)month
, (int8_t)monthLength
, (int8_t)prevMonthLength
,
463 (int8_t)day
, (int8_t)dayOfWeek
, millis
,
464 endTimeMode
== WALL_TIME
? dstSavings
:
465 (endTimeMode
== UTC_TIME
? -rawOffset
: 0),
466 endMode
, (int8_t)endMonth
, (int8_t)endDayOfWeek
,
467 (int8_t)endDay
, endTime
);
470 // Check for both the northern and southern hemisphere cases. We
471 // assume that in the northern hemisphere, the start rule is before the
472 // end rule within the calendar year, and vice versa for the southern
474 if ((!southern
&& (startCompare
>= 0 && endCompare
< 0)) ||
475 (southern
&& (startCompare
>= 0 || endCompare
< 0)))
476 result
+= dstSavings
;
481 // -------------------------------------
484 * Compare a given date in the year to a rule. Return 1, 0, or -1, depending
485 * on whether the date is after, equal to, or before the rule date. The
486 * millis are compared directly against the ruleMillis, so any
487 * standard-daylight adjustments must be handled by the caller.
489 * @return 1 if the date is after the rule date, -1 if the date is before
490 * the rule date, or 0 if the date is equal to the rule date.
493 SimpleTimeZone::compareToRule(int8_t month
, int8_t monthLen
, int8_t prevMonthLen
,
495 int8_t dayOfWeek
, int32_t millis
, int32_t millisDelta
,
496 EMode ruleMode
, int8_t ruleMonth
, int8_t ruleDayOfWeek
,
497 int8_t ruleDay
, int32_t ruleMillis
)
499 // Make adjustments for startTimeMode and endTimeMode
500 millis
+= millisDelta
;
501 while (millis
>= U_MILLIS_PER_DAY
) {
502 millis
-= U_MILLIS_PER_DAY
;
504 dayOfWeek
= (int8_t)(1 + (dayOfWeek
% 7)); // dayOfWeek is one-based
505 if (dayOfMonth
> monthLen
) {
507 /* When incrementing the month, it is desirible to overflow
508 * from DECEMBER to DECEMBER+1, since we use the result to
509 * compare against a real month. Wraparound of the value
510 * leads to bug 4173604. */
515 millis
+= U_MILLIS_PER_DAY
;
517 dayOfWeek
= (int8_t)(1 + ((dayOfWeek
+5) % 7)); // dayOfWeek is one-based
518 if (dayOfMonth
< 1) {
519 dayOfMonth
= prevMonthLen
;
524 // first compare months. If they're different, we don't have to worry about days
526 if (month
< ruleMonth
) return -1;
527 else if (month
> ruleMonth
) return 1;
529 // calculate the actual day of month for the rule
530 int32_t ruleDayOfMonth
= 0;
533 // if the mode is day-of-month, the day of month is given
535 ruleDayOfMonth
= ruleDay
;
538 // if the mode is day-of-week-in-month, calculate the day-of-month from it
539 case DOW_IN_MONTH_MODE
:
540 // In this case ruleDay is the day-of-week-in-month (this code is using
541 // the dayOfWeek and dayOfMonth parameters to figure out the day-of-week
542 // of the first day of the month, so it's trusting that they're really
543 // consistent with each other)
545 ruleDayOfMonth
= 1 + (ruleDay
- 1) * 7 +
546 (7 + ruleDayOfWeek
- (dayOfWeek
- dayOfMonth
+ 1)) % 7;
548 // if ruleDay is negative (we assume it's not zero here), we have to do
549 // the same calculation figuring backward from the last day of the month.
550 // (STATICMONTHLENGTH gives us that last day. We don't take leap years
551 // into account, so this may not work right for February.)
554 // (again, this code is trusting that dayOfMonth and dayOfMonth are
555 // consistent with each other here, since we're using them to figure
556 // the day of week of the first of the month)
557 ruleDayOfMonth
= monthLen
+ (ruleDay
+ 1) * 7 -
558 (7 + (dayOfWeek
+ monthLen
- dayOfMonth
) - ruleDayOfWeek
) % 7;
562 case DOW_GE_DOM_MODE
:
563 ruleDayOfMonth
= ruleDay
+
564 (49 + ruleDayOfWeek
- ruleDay
- dayOfWeek
+ dayOfMonth
) % 7;
567 case DOW_LE_DOM_MODE
:
568 ruleDayOfMonth
= ruleDay
-
569 (49 - ruleDayOfWeek
+ ruleDay
+ dayOfWeek
- dayOfMonth
) % 7;
570 // Note at this point ruleDayOfMonth may be <1, although it will
571 // be >=1 for well-formed rules.
575 // now that we have a real day-in-month for the rule, we can compare days...
576 if (dayOfMonth
< ruleDayOfMonth
) return -1;
577 else if (dayOfMonth
> ruleDayOfMonth
) return 1;
579 // ...and if they're equal, we compare times
580 if (millis
< ruleMillis
) return -1;
581 else if (millis
> ruleMillis
) return 1;
585 // -------------------------------------
588 SimpleTimeZone::getRawOffset() const
593 // -------------------------------------
596 SimpleTimeZone::setRawOffset(int32_t offsetMillis
)
598 rawOffset
= offsetMillis
;
601 // -------------------------------------
604 SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST
, UErrorCode
& status
)
606 if (millisSavedDuringDST
<= 0) {
607 status
= U_ILLEGAL_ARGUMENT_ERROR
;
610 dstSavings
= millisSavedDuringDST
;
614 // -------------------------------------
617 SimpleTimeZone::getDSTSavings() const
622 // -------------------------------------
625 SimpleTimeZone::useDaylightTime() const
630 // -------------------------------------
634 * Queries if the given date is in Daylight Savings Time.
636 UBool
SimpleTimeZone::inDaylightTime(UDate date
, UErrorCode
& status
) const
638 // This method is wasteful since it creates a new GregorianCalendar and
639 // deletes it each time it is called. However, this is a deprecated method
640 // and provided only for Java compatibility as of 8/6/97 [LIU].
641 if (U_FAILURE(status
)) return FALSE
;
642 GregorianCalendar
*gc
= new GregorianCalendar(*this, status
);
645 status
= U_MEMORY_ALLOCATION_ERROR
;
648 gc
->setTime(date
, status
);
649 UBool result
= gc
->inDaylightTime(status
);
654 // -------------------------------------
657 * Return true if this zone has the same rules and offset as another zone.
658 * @param other the TimeZone object to be compared with
659 * @return true if the given zone has the same rules and offset as this one
662 SimpleTimeZone::hasSameRules(const TimeZone
& other
) const
664 if (this == &other
) return TRUE
;
665 if (other
.getDynamicClassID() != SimpleTimeZone::getStaticClassID()) return FALSE
;
666 SimpleTimeZone
*that
= (SimpleTimeZone
*)&other
;
667 return rawOffset
== that
->rawOffset
&&
668 useDaylight
== that
->useDaylight
&&
670 // Only check rules if using DST
671 || (dstSavings
== that
->dstSavings
&&
672 startMode
== that
->startMode
&&
673 startMonth
== that
->startMonth
&&
674 startDay
== that
->startDay
&&
675 startDayOfWeek
== that
->startDayOfWeek
&&
676 startTime
== that
->startTime
&&
677 startTimeMode
== that
->startTimeMode
&&
678 endMode
== that
->endMode
&&
679 endMonth
== that
->endMonth
&&
680 endDay
== that
->endDay
&&
681 endDayOfWeek
== that
->endDayOfWeek
&&
682 endTime
== that
->endTime
&&
683 endTimeMode
== that
->endTimeMode
&&
684 startYear
== that
->startYear
));
687 // -------------------------------------
689 //----------------------------------------------------------------------
690 // Rule representation
692 // We represent the following flavors of rules:
693 // 5 the fifth of the month
694 // lastSun the last Sunday in the month
695 // lastMon the last Monday in the month
696 // Sun>=8 first Sunday on or after the eighth
697 // Sun<=25 last Sunday on or before the 25th
698 // This is further complicated by the fact that we need to remain
699 // backward compatible with the 1.1 FCS. Finally, we need to minimize
700 // API changes. In order to satisfy these requirements, we support
701 // three representation systems, and we translate between them.
703 // INTERNAL REPRESENTATION
704 // This is the format SimpleTimeZone objects take after construction or
705 // streaming in is complete. Rules are represented directly, using an
706 // unencoded format. We will discuss the start rule only below; the end
707 // rule is analogous.
708 // startMode Takes on enumerated values DAY_OF_MONTH,
709 // DOW_IN_MONTH, DOW_AFTER_DOM, or DOW_BEFORE_DOM.
710 // startDay The day of the month, or for DOW_IN_MONTH mode, a
711 // value indicating which DOW, such as +1 for first,
712 // +2 for second, -1 for last, etc.
713 // startDayOfWeek The day of the week. Ignored for DAY_OF_MONTH.
715 // ENCODED REPRESENTATION
716 // This is the format accepted by the constructor and by setStartRule()
717 // and setEndRule(). It uses various combinations of positive, negative,
718 // and zero values to encode the different rules. This representation
719 // allows us to specify all the different rule flavors without altering
721 // MODE startMonth startDay startDayOfWeek
722 // DOW_IN_MONTH_MODE >=0 !=0 >0
723 // DOM_MODE >=0 >0 ==0
724 // DOW_GE_DOM_MODE >=0 >0 <0
725 // DOW_LE_DOM_MODE >=0 <0 <0
726 // (no DST) don't care ==0 don't care
728 // STREAMED REPRESENTATION
729 // We must retain binary compatibility with the 1.1 FCS. The 1.1 code only
730 // handles DOW_IN_MONTH_MODE and non-DST mode, the latter indicated by the
731 // flag useDaylight. When we stream an object out, we translate into an
732 // approximate DOW_IN_MONTH_MODE representation so the object can be parsed
733 // and used by 1.1 code. Following that, we write out the full
734 // representation separately so that contemporary code can recognize and
735 // parse it. The full representation is written in a "packed" format,
736 // consisting of a version number, a length, and an array of bytes. Future
737 // versions of this class may specify different versions. If they wish to
738 // include additional data, they should do so by storing them after the
739 // packed representation below.
740 //----------------------------------------------------------------------
743 * Given a set of encoded rules in startDay and startDayOfMonth, decode
744 * them and set the startMode appropriately. Do the same for endDay and
745 * endDayOfMonth. Upon entry, the day of week variables may be zero or
746 * negative, in order to indicate special modes. The day of month
747 * variables may also be negative. Upon exit, the mode variables will be
748 * set, and the day of week and day of month variables will be positive.
749 * This method also recognizes a startDay or endDay of zero as indicating
753 SimpleTimeZone::decodeRules(UErrorCode
& status
)
755 decodeStartRule(status
);
756 decodeEndRule(status
);
760 * Decode the start rule and validate the parameters. The parameters are
761 * expected to be in encoded form, which represents the various rule modes
762 * by negating or zeroing certain values. Representation formats are:
765 * DOW_IN_MONTH DOM DOW>=DOM DOW<=DOM no DST
766 * ------------ ----- -------- -------- ----------
767 * month 0..11 same same same don't care
768 * day -5..5 1..31 1..31 -1..-31 0
769 * dayOfWeek 1..7 0 -1..-7 -1..-7 don't care
770 * time 0..ONEDAY same same same don't care
772 * The range for month does not include UNDECIMBER since this class is
773 * really specific to GregorianCalendar, which does not use that month.
774 * The range for time includes ONEDAY (vs. ending at ONEDAY-1) because the
775 * end rule is an exclusive limit point. That is, the range of times that
776 * are in DST include those >= the start and < the end. For this reason,
777 * it should be possible to specify an end of ONEDAY in order to include the
778 * entire day. Although this is equivalent to time 0 of the following day,
779 * it's not always possible to specify that, for example, on December 31.
780 * While arguably the start range should still be 0..ONEDAY-1, we keep
781 * the start and end ranges the same for consistency.
784 SimpleTimeZone::decodeStartRule(UErrorCode
& status
)
786 if(U_FAILURE(status
)) return;
788 useDaylight
= (UBool
)((startDay
!= 0) && (endDay
!= 0) ? TRUE
: FALSE
);
789 if (useDaylight
&& dstSavings
== 0) {
790 dstSavings
= U_MILLIS_PER_HOUR
;
793 if (startMonth
< UCAL_JANUARY
|| startMonth
> UCAL_DECEMBER
) {
794 status
= U_ILLEGAL_ARGUMENT_ERROR
;
797 if (startTime
< 0 || startTime
> U_MILLIS_PER_DAY
||
798 startTimeMode
< WALL_TIME
|| startTimeMode
> UTC_TIME
) {
799 status
= U_ILLEGAL_ARGUMENT_ERROR
;
802 if (startDayOfWeek
== 0) {
803 startMode
= DOM_MODE
;
805 if (startDayOfWeek
> 0) {
806 startMode
= DOW_IN_MONTH_MODE
;
808 startDayOfWeek
= (int8_t)-startDayOfWeek
;
810 startMode
= DOW_GE_DOM_MODE
;
812 startDay
= (int8_t)-startDay
;
813 startMode
= DOW_LE_DOM_MODE
;
816 if (startDayOfWeek
> UCAL_SATURDAY
) {
817 status
= U_ILLEGAL_ARGUMENT_ERROR
;
821 if (startMode
== DOW_IN_MONTH_MODE
) {
822 if (startDay
< -5 || startDay
> 5) {
823 status
= U_ILLEGAL_ARGUMENT_ERROR
;
826 } else if (startDay
<1 || startDay
> STATICMONTHLENGTH
[startMonth
]) {
827 status
= U_ILLEGAL_ARGUMENT_ERROR
;
834 * Decode the end rule and validate the parameters. This method is exactly
835 * analogous to decodeStartRule().
836 * @see decodeStartRule
839 SimpleTimeZone::decodeEndRule(UErrorCode
& status
)
841 if(U_FAILURE(status
)) return;
843 useDaylight
= (UBool
)((startDay
!= 0) && (endDay
!= 0) ? TRUE
: FALSE
);
844 if (useDaylight
&& dstSavings
== 0) {
845 dstSavings
= U_MILLIS_PER_HOUR
;
848 if (endMonth
< UCAL_JANUARY
|| endMonth
> UCAL_DECEMBER
) {
849 status
= U_ILLEGAL_ARGUMENT_ERROR
;
852 if (endTime
< 0 || endTime
> U_MILLIS_PER_DAY
||
853 endTimeMode
< WALL_TIME
|| endTimeMode
> UTC_TIME
) {
854 status
= U_ILLEGAL_ARGUMENT_ERROR
;
857 if (endDayOfWeek
== 0) {
860 if (endDayOfWeek
> 0) {
861 endMode
= DOW_IN_MONTH_MODE
;
863 endDayOfWeek
= (int8_t)-endDayOfWeek
;
865 endMode
= DOW_GE_DOM_MODE
;
867 endDay
= (int8_t)-endDay
;
868 endMode
= DOW_LE_DOM_MODE
;
871 if (endDayOfWeek
> UCAL_SATURDAY
) {
872 status
= U_ILLEGAL_ARGUMENT_ERROR
;
876 if (endMode
== DOW_IN_MONTH_MODE
) {
877 if (endDay
< -5 || endDay
> 5) {
878 status
= U_ILLEGAL_ARGUMENT_ERROR
;
881 } else if (endDay
<1 || endDay
> STATICMONTHLENGTH
[endMonth
]) {
882 status
= U_ILLEGAL_ARGUMENT_ERROR
;
890 #endif /* #if !UCONFIG_NO_FORMATTING */