home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 6 / AACD06.ISO / AACD / Programming / ICU / src / icu / source / i18n / simpletz.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-27  |  29.4 KB  |  805 lines

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