]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/simpletz.cpp
ICU-8.11.4.tar.gz
[apple/icu.git] / icuSources / i18n / simpletz.cpp
CommitLineData
b75a7d8f
A
1/*
2*******************************************************************************
73c04bcf 3* Copyright (C) 1997-2005, International Business Machines Corporation and *
b75a7d8f
A
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"
73c04bcf 30#include "unicode/smpdtfmt.h"
b75a7d8f
A
31
32U_NAMESPACE_BEGIN
33
374ca955 34UOBJECT_DEFINE_RTTI_IMPLEMENTATION(SimpleTimeZone)
b75a7d8f
A
35
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.
374ca955 40const int8_t SimpleTimeZone::STATICMONTHLENGTH[] = {31,29,31,30,31,30,31,31,30,31,30,31};
b75a7d8f
A
41
42// *****************************************************************************
43// class SimpleTimeZone
44// *****************************************************************************
45
46
47SimpleTimeZone::SimpleTimeZone(int32_t rawOffsetGMT, const UnicodeString& ID)
48: TimeZone(ID),
49 startMonth(0),
50 startDay(0),
51 startDayOfWeek(0),
52 startTime(0),
53 startTimeMode(WALL_TIME),
54 endTimeMode(WALL_TIME),
55 endMonth(0),
56 endDay(0),
57 endDayOfWeek(0),
58 endTime(0),
59 startYear(0),
60 rawOffset(rawOffsetGMT),
61 useDaylight(FALSE),
62 startMode(DOM_MODE),
63 endMode(DOM_MODE),
64 dstSavings(U_MILLIS_PER_HOUR)
65{
66}
67
68// -------------------------------------
69
70SimpleTimeZone::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,
75 UErrorCode& status)
76: TimeZone(ID)
77{
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);
84}
85
86// -------------------------------------
87
88SimpleTimeZone::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)
94: TimeZone(ID)
95{
96 construct(rawOffsetGMT,
97 savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
98 savingsStartTime, WALL_TIME,
99 savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
100 savingsEndTime, WALL_TIME,
101 savingsDST, status);
102}
103
104// -------------------------------------
105
106SimpleTimeZone::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)
114: TimeZone(ID)
115{
116 construct(rawOffsetGMT,
117 savingsStartMonth, savingsStartDay, savingsStartDayOfWeek,
118 savingsStartTime, savingsStartTimeMode,
119 savingsEndMonth, savingsEndDay, savingsEndDayOfWeek,
120 savingsEndTime, savingsEndTimeMode,
121 savingsDST, status);
122}
123
b75a7d8f
A
124/**
125 * Internal construction method.
126 */
127void 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,
138 int32_t savingsDST,
139 UErrorCode& status)
140{
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;
153 this->startYear = 0;
154 this->startMode = DOM_MODE;
155 this->endMode = DOM_MODE;
156
157 decodeRules(status);
158
159 if (savingsDST <= 0) {
160 status = U_ILLEGAL_ARGUMENT_ERROR;
161 }
162}
163
164// -------------------------------------
165
166SimpleTimeZone::~SimpleTimeZone()
167{
168}
169
170// -------------------------------------
171
172// Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
173SimpleTimeZone::SimpleTimeZone(const SimpleTimeZone &source)
174: TimeZone(source)
175{
176 *this = source;
177}
178
179// -------------------------------------
180
181// Called by TimeZone::createDefault(), then clone() inside a Mutex - be careful.
182SimpleTimeZone &
183SimpleTimeZone::operator=(const SimpleTimeZone &right)
184{
185 if (this != &right)
186 {
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;
204 }
205 return *this;
206}
207
208// -------------------------------------
209
210UBool
211SimpleTimeZone::operator==(const TimeZone& that) const
212{
213 return ((this == &that) ||
214 (getDynamicClassID() == that.getDynamicClassID() &&
215 TimeZone::operator==(that) &&
216 hasSameRules(that)));
217}
218
219// -------------------------------------
220
221// Called by TimeZone::createDefault() inside a Mutex - be careful.
222TimeZone*
223SimpleTimeZone::clone() const
224{
225 return new SimpleTimeZone(*this);
226}
227
228// -------------------------------------
229
230/**
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.
236 */
237void
238SimpleTimeZone::setStartYear(int32_t year)
239{
240 startYear = year;
241}
242
243// -------------------------------------
244
245/**
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
255 * of the month).
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.
275 * eg, 0 for January.
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.
282 */
283
284void
285SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek,
286 int32_t time, TimeMode mode, UErrorCode& status)
287{
288 startMonth = (int8_t)month;
289 startDay = (int8_t)dayOfWeekInMonth;
290 startDayOfWeek = (int8_t)dayOfWeek;
291 startTime = time;
292 startTimeMode = mode;
293 decodeStartRule(status);
294}
295
296// -------------------------------------
297
298void
299SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth,
300 int32_t time, TimeMode mode, UErrorCode& status)
301{
302 setStartRule(month, dayOfMonth, 0, time, mode, status);
303}
304
305// -------------------------------------
306
307void
308SimpleTimeZone::setStartRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
309 int32_t time, TimeMode mode, UBool after, UErrorCode& status)
310{
311 setStartRule(month, after ? dayOfMonth : -dayOfMonth,
312 -dayOfWeek, time, mode, status);
313}
314
315// -------------------------------------
316
317/**
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.
326 * eg, 0 for January.
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.
333 */
334
335void
336SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfWeekInMonth, int32_t dayOfWeek,
337 int32_t time, TimeMode mode, UErrorCode& status)
338{
339 endMonth = (int8_t)month;
340 endDay = (int8_t)dayOfWeekInMonth;
341 endDayOfWeek = (int8_t)dayOfWeek;
342 endTime = time;
343 endTimeMode = mode;
344 decodeEndRule(status);
345}
346
347// -------------------------------------
348
349void
350SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth,
351 int32_t time, TimeMode mode, UErrorCode& status)
352{
353 setEndRule(month, dayOfMonth, 0, time, mode, status);
354}
355
356// -------------------------------------
357
358void
359SimpleTimeZone::setEndRule(int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
360 int32_t time, TimeMode mode, UBool after, UErrorCode& status)
361{
362 setEndRule(month, after ? dayOfMonth : -dayOfMonth,
363 -dayOfWeek, time, mode, status);
364}
365
366// -------------------------------------
367
368int32_t
369SimpleTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
370 uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const
371{
374ca955 372 // Check the month before indexing into STATICMONTHLENGTH. This
b75a7d8f
A
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;
381 return 0;
382 }
383
374ca955 384 return getOffset(era, year, month, day, dayOfWeek, millis, STATICMONTHLENGTH[month], status);
b75a7d8f
A
385}
386
387int32_t
388SimpleTimeZone::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 {
374ca955 391 // Check the month before indexing into STATICMONTHLENGTH. This
b75a7d8f
A
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;
401 return -1;
402 }
403
404 // TODO FIX We don't handle leap years yet!
374ca955 405 int32_t prevMonthLength = (month >= 1) ? STATICMONTHLENGTH[month - 1] : 31;
b75a7d8f
A
406
407 return getOffset(era, year, month, day, dayOfWeek, millis,
408 monthLength, prevMonthLength, status);
409}
410
411int32_t
412SimpleTimeZone::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
416{
417 if(U_FAILURE(status)) return 0;
418
419 if ((era != GregorianCalendar::AD && era != GregorianCalendar::BC)
420 || month < UCAL_JANUARY
421 || month > UCAL_DECEMBER
422 || day < 1
423 || day > monthLength
424 || dayOfWeek < UCAL_SUNDAY
425 || dayOfWeek > UCAL_SATURDAY
426 || millis < 0
427 || millis >= U_MILLIS_PER_DAY
428 || monthLength < 28
374ca955
A
429 || monthLength > 31
430 || prevMonthLength < 28
431 || prevMonthLength > 31) {
b75a7d8f
A
432 status = U_ILLEGAL_ARGUMENT_ERROR;
433 return -1;
434 }
435
436 int32_t result = rawOffset;
437
438 // Bail out if we are before the onset of daylight savings time
439 if(!useDaylight || year < startYear || era != GregorianCalendar::AD)
440 return result;
441
442 // Check for southern hemisphere. We assume that the start and end
443 // month are different.
444 UBool southern = (startMonth > endMonth);
445
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;
454
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);
468 }
469
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
473 // hemisphere.
474 if ((!southern && (startCompare >= 0 && endCompare < 0)) ||
475 (southern && (startCompare >= 0 || endCompare < 0)))
476 result += dstSavings;
477
478 return result;
479}
480
481// -------------------------------------
482
483/**
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.
488 *
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.
491 */
492int32_t
493SimpleTimeZone::compareToRule(int8_t month, int8_t monthLen, int8_t prevMonthLen,
494 int8_t dayOfMonth,
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)
498{
499 // Make adjustments for startTimeMode and endTimeMode
500 millis += millisDelta;
501 while (millis >= U_MILLIS_PER_DAY) {
502 millis -= U_MILLIS_PER_DAY;
503 ++dayOfMonth;
504 dayOfWeek = (int8_t)(1 + (dayOfWeek % 7)); // dayOfWeek is one-based
505 if (dayOfMonth > monthLen) {
506 dayOfMonth = 1;
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. */
511 ++month;
512 }
513 }
514 while (millis < 0) {
515 millis += U_MILLIS_PER_DAY;
516 --dayOfMonth;
517 dayOfWeek = (int8_t)(1 + ((dayOfWeek+5) % 7)); // dayOfWeek is one-based
518 if (dayOfMonth < 1) {
519 dayOfMonth = prevMonthLen;
520 --month;
521 }
522 }
523
524 // first compare months. If they're different, we don't have to worry about days
525 // and times
526 if (month < ruleMonth) return -1;
527 else if (month > ruleMonth) return 1;
528
529 // calculate the actual day of month for the rule
530 int32_t ruleDayOfMonth = 0;
531 switch (ruleMode)
532 {
533 // if the mode is day-of-month, the day of month is given
534 case DOM_MODE:
535 ruleDayOfMonth = ruleDay;
536 break;
537
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)
544 if (ruleDay > 0)
545 ruleDayOfMonth = 1 + (ruleDay - 1) * 7 +
546 (7 + ruleDayOfWeek - (dayOfWeek - dayOfMonth + 1)) % 7;
547
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.
374ca955 550 // (STATICMONTHLENGTH gives us that last day. We don't take leap years
b75a7d8f
A
551 // into account, so this may not work right for February.)
552 else
553 {
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;
559 }
560 break;
561
562 case DOW_GE_DOM_MODE:
563 ruleDayOfMonth = ruleDay +
564 (49 + ruleDayOfWeek - ruleDay - dayOfWeek + dayOfMonth) % 7;
565 break;
566
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.
572 break;
573 }
574
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;
578
579 // ...and if they're equal, we compare times
580 if (millis < ruleMillis) return -1;
581 else if (millis > ruleMillis) return 1;
582 else return 0;
583}
584
585// -------------------------------------
586
587int32_t
588SimpleTimeZone::getRawOffset() const
589{
590 return rawOffset;
591}
592
593// -------------------------------------
594
595void
596SimpleTimeZone::setRawOffset(int32_t offsetMillis)
597{
598 rawOffset = offsetMillis;
599}
600
601// -------------------------------------
602
603void
604SimpleTimeZone::setDSTSavings(int32_t millisSavedDuringDST, UErrorCode& status)
605{
606 if (millisSavedDuringDST <= 0) {
607 status = U_ILLEGAL_ARGUMENT_ERROR;
608 }
609 else {
610 dstSavings = millisSavedDuringDST;
611 }
612}
613
614// -------------------------------------
615
616int32_t
617SimpleTimeZone::getDSTSavings() const
618{
619 return dstSavings;
620}
621
622// -------------------------------------
623
624UBool
625SimpleTimeZone::useDaylightTime() const
626{
627 return useDaylight;
628}
629
630// -------------------------------------
631
632/**
633 * Overrides TimeZone
634 * Queries if the given date is in Daylight Savings Time.
635 */
636UBool SimpleTimeZone::inDaylightTime(UDate date, UErrorCode& status) const
637{
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);
643 /* test for NULL */
644 if (gc == 0) {
645 status = U_MEMORY_ALLOCATION_ERROR;
646 return FALSE;
647 }
648 gc->setTime(date, status);
649 UBool result = gc->inDaylightTime(status);
650 delete gc;
651 return result;
652}
653
654// -------------------------------------
655
656/**
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
660 */
661UBool
662SimpleTimeZone::hasSameRules(const TimeZone& other) const
663{
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 &&
669 (!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));
685}
686
687// -------------------------------------
688
689//----------------------------------------------------------------------
690// Rule representation
691//
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.
702//
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.
714//
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
720// the API.
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
727//
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//----------------------------------------------------------------------
741
742/**
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
750 * no DST.
751 */
752void
753SimpleTimeZone::decodeRules(UErrorCode& status)
754{
755 decodeStartRule(status);
756 decodeEndRule(status);
757}
758
759/**
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:
763 * <p>
764 * <pre>
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
771 * </pre>
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.
782 */
783void
784SimpleTimeZone::decodeStartRule(UErrorCode& status)
785{
786 if(U_FAILURE(status)) return;
787
788 useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE);
789 if (useDaylight && dstSavings == 0) {
790 dstSavings = U_MILLIS_PER_HOUR;
791 }
792 if (startDay != 0) {
793 if (startMonth < UCAL_JANUARY || startMonth > UCAL_DECEMBER) {
794 status = U_ILLEGAL_ARGUMENT_ERROR;
795 return;
796 }
797 if (startTime < 0 || startTime > U_MILLIS_PER_DAY ||
798 startTimeMode < WALL_TIME || startTimeMode > UTC_TIME) {
799 status = U_ILLEGAL_ARGUMENT_ERROR;
800 return;
801 }
802 if (startDayOfWeek == 0) {
803 startMode = DOM_MODE;
804 } else {
805 if (startDayOfWeek > 0) {
806 startMode = DOW_IN_MONTH_MODE;
807 } else {
808 startDayOfWeek = (int8_t)-startDayOfWeek;
809 if (startDay > 0) {
810 startMode = DOW_GE_DOM_MODE;
811 } else {
812 startDay = (int8_t)-startDay;
813 startMode = DOW_LE_DOM_MODE;
814 }
815 }
816 if (startDayOfWeek > UCAL_SATURDAY) {
817 status = U_ILLEGAL_ARGUMENT_ERROR;
818 return;
819 }
820 }
821 if (startMode == DOW_IN_MONTH_MODE) {
822 if (startDay < -5 || startDay > 5) {
823 status = U_ILLEGAL_ARGUMENT_ERROR;
824 return;
825 }
73c04bcf 826 } else if (startDay<1 || startDay > STATICMONTHLENGTH[startMonth]) {
b75a7d8f
A
827 status = U_ILLEGAL_ARGUMENT_ERROR;
828 return;
829 }
830 }
831}
832
833/**
834 * Decode the end rule and validate the parameters. This method is exactly
835 * analogous to decodeStartRule().
836 * @see decodeStartRule
837 */
838void
839SimpleTimeZone::decodeEndRule(UErrorCode& status)
840{
841 if(U_FAILURE(status)) return;
842
843 useDaylight = (UBool)((startDay != 0) && (endDay != 0) ? TRUE : FALSE);
844 if (useDaylight && dstSavings == 0) {
845 dstSavings = U_MILLIS_PER_HOUR;
846 }
847 if (endDay != 0) {
848 if (endMonth < UCAL_JANUARY || endMonth > UCAL_DECEMBER) {
849 status = U_ILLEGAL_ARGUMENT_ERROR;
850 return;
851 }
852 if (endTime < 0 || endTime > U_MILLIS_PER_DAY ||
853 endTimeMode < WALL_TIME || endTimeMode > UTC_TIME) {
854 status = U_ILLEGAL_ARGUMENT_ERROR;
855 return;
856 }
857 if (endDayOfWeek == 0) {
858 endMode = DOM_MODE;
859 } else {
860 if (endDayOfWeek > 0) {
861 endMode = DOW_IN_MONTH_MODE;
862 } else {
863 endDayOfWeek = (int8_t)-endDayOfWeek;
864 if (endDay > 0) {
865 endMode = DOW_GE_DOM_MODE;
866 } else {
867 endDay = (int8_t)-endDay;
868 endMode = DOW_LE_DOM_MODE;
869 }
870 }
871 if (endDayOfWeek > UCAL_SATURDAY) {
872 status = U_ILLEGAL_ARGUMENT_ERROR;
873 return;
874 }
875 }
876 if (endMode == DOW_IN_MONTH_MODE) {
877 if (endDay < -5 || endDay > 5) {
878 status = U_ILLEGAL_ARGUMENT_ERROR;
879 return;
880 }
73c04bcf 881 } else if (endDay<1 || endDay > STATICMONTHLENGTH[endMonth]) {
b75a7d8f
A
882 status = U_ILLEGAL_ARGUMENT_ERROR;
883 return;
884 }
885 }
886}
887
888U_NAMESPACE_END
889
890#endif /* #if !UCONFIG_NO_FORMATTING */
891
892//eof