diff options
author | Peter Breitenlohner <peb@mppmu.mpg.de> | 2012-10-26 07:13:09 +0000 |
---|---|---|
committer | Peter Breitenlohner <peb@mppmu.mpg.de> | 2012-10-26 07:13:09 +0000 |
commit | 74cc05c672add53f566ac385f117008e8c4b3eda (patch) | |
tree | ce973a7d2e4814a20d52a55b1a2131d1515e9911 /Build/source/libs/icu/icu-49.1/i18n/vtzone.cpp | |
parent | 6439fb8917718c4d620170e1459a1d9e7ab3c441 (diff) |
icu 50.1 (50_rc)
git-svn-id: svn://tug.org/texlive/trunk@28087 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/libs/icu/icu-49.1/i18n/vtzone.cpp')
-rw-r--r-- | Build/source/libs/icu/icu-49.1/i18n/vtzone.cpp | 2630 |
1 files changed, 0 insertions, 2630 deletions
diff --git a/Build/source/libs/icu/icu-49.1/i18n/vtzone.cpp b/Build/source/libs/icu/icu-49.1/i18n/vtzone.cpp deleted file mode 100644 index 8e8689ffbae..00000000000 --- a/Build/source/libs/icu/icu-49.1/i18n/vtzone.cpp +++ /dev/null @@ -1,2630 +0,0 @@ -/* -******************************************************************************* -* Copyright (C) 2007-2011, International Business Machines Corporation and -* others. All Rights Reserved. -******************************************************************************* -*/ - -#include <typeinfo> // for 'typeid' to work - -#include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/vtzone.h" -#include "unicode/rbtz.h" -#include "unicode/ucal.h" -#include "unicode/ures.h" -#include "cmemory.h" -#include "uvector.h" -#include "gregoimp.h" - -U_NAMESPACE_BEGIN - -// This is the deleter that will be use to remove TimeZoneRule -U_CDECL_BEGIN -static void U_CALLCONV -deleteTimeZoneRule(void* obj) { - delete (TimeZoneRule*) obj; -} -U_CDECL_END - -// Smybol characters used by RFC2445 VTIMEZONE -static const UChar COLON = 0x3A; /* : */ -static const UChar SEMICOLON = 0x3B; /* ; */ -static const UChar EQUALS_SIGN = 0x3D; /* = */ -static const UChar COMMA = 0x2C; /* , */ -static const UChar PLUS = 0x2B; /* + */ -static const UChar MINUS = 0x2D; /* - */ - -// RFC2445 VTIMEZONE tokens -static const UChar ICAL_BEGIN_VTIMEZONE[] = {0x42, 0x45, 0x47, 0x49, 0x4E, 0x3A, 0x56, 0x54, 0x49, 0x4D, 0x45, 0x5A, 0x4F, 0x4E, 0x45, 0}; /* "BEGIN:VTIMEZONE" */ -static const UChar ICAL_END_VTIMEZONE[] = {0x45, 0x4E, 0x44, 0x3A, 0x56, 0x54, 0x49, 0x4D, 0x45, 0x5A, 0x4F, 0x4E, 0x45, 0}; /* "END:VTIMEZONE" */ -static const UChar ICAL_BEGIN[] = {0x42, 0x45, 0x47, 0x49, 0x4E, 0}; /* "BEGIN" */ -static const UChar ICAL_END[] = {0x45, 0x4E, 0x44, 0}; /* "END" */ -static const UChar ICAL_VTIMEZONE[] = {0x56, 0x54, 0x49, 0x4D, 0x45, 0x5A, 0x4F, 0x4E, 0x45, 0}; /* "VTIMEZONE" */ -static const UChar ICAL_TZID[] = {0x54, 0x5A, 0x49, 0x44, 0}; /* "TZID" */ -static const UChar ICAL_STANDARD[] = {0x53, 0x54, 0x41, 0x4E, 0x44, 0x41, 0x52, 0x44, 0}; /* "STANDARD" */ -static const UChar ICAL_DAYLIGHT[] = {0x44, 0x41, 0x59, 0x4C, 0x49, 0x47, 0x48, 0x54, 0}; /* "DAYLIGHT" */ -static const UChar ICAL_DTSTART[] = {0x44, 0x54, 0x53, 0x54, 0x41, 0x52, 0x54, 0}; /* "DTSTART" */ -static const UChar ICAL_TZOFFSETFROM[] = {0x54, 0x5A, 0x4F, 0x46, 0x46, 0x53, 0x45, 0x54, 0x46, 0x52, 0x4F, 0x4D, 0}; /* "TZOFFSETFROM" */ -static const UChar ICAL_TZOFFSETTO[] = {0x54, 0x5A, 0x4F, 0x46, 0x46, 0x53, 0x45, 0x54, 0x54, 0x4F, 0}; /* "TZOFFSETTO" */ -static const UChar ICAL_RDATE[] = {0x52, 0x44, 0x41, 0x54, 0x45, 0}; /* "RDATE" */ -static const UChar ICAL_RRULE[] = {0x52, 0x52, 0x55, 0x4C, 0x45, 0}; /* "RRULE" */ -static const UChar ICAL_TZNAME[] = {0x54, 0x5A, 0x4E, 0x41, 0x4D, 0x45, 0}; /* "TZNAME" */ -static const UChar ICAL_TZURL[] = {0x54, 0x5A, 0x55, 0x52, 0x4C, 0}; /* "TZURL" */ -static const UChar ICAL_LASTMOD[] = {0x4C, 0x41, 0x53, 0x54, 0x2D, 0x4D, 0x4F, 0x44, 0x49, 0x46, 0x49, 0x45, 0x44, 0}; /* "LAST-MODIFIED" */ - -static const UChar ICAL_FREQ[] = {0x46, 0x52, 0x45, 0x51, 0}; /* "FREQ" */ -static const UChar ICAL_UNTIL[] = {0x55, 0x4E, 0x54, 0x49, 0x4C, 0}; /* "UNTIL" */ -static const UChar ICAL_YEARLY[] = {0x59, 0x45, 0x41, 0x52, 0x4C, 0x59, 0}; /* "YEARLY" */ -static const UChar ICAL_BYMONTH[] = {0x42, 0x59, 0x4D, 0x4F, 0x4E, 0x54, 0x48, 0}; /* "BYMONTH" */ -static const UChar ICAL_BYDAY[] = {0x42, 0x59, 0x44, 0x41, 0x59, 0}; /* "BYDAY" */ -static const UChar ICAL_BYMONTHDAY[] = {0x42, 0x59, 0x4D, 0x4F, 0x4E, 0x54, 0x48, 0x44, 0x41, 0x59, 0}; /* "BYMONTHDAY" */ - -static const UChar ICAL_NEWLINE[] = {0x0D, 0x0A, 0}; /* CRLF */ - -static const UChar ICAL_DOW_NAMES[7][3] = { - {0x53, 0x55, 0}, /* "SU" */ - {0x4D, 0x4F, 0}, /* "MO" */ - {0x54, 0x55, 0}, /* "TU" */ - {0x57, 0x45, 0}, /* "WE" */ - {0x54, 0x48, 0}, /* "TH" */ - {0x46, 0x52, 0}, /* "FR" */ - {0x53, 0x41, 0} /* "SA" */}; - -// Month length for non-leap year -static const int32_t MONTHLENGTH[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - -// ICU custom property -static const UChar ICU_TZINFO_PROP[] = {0x58, 0x2D, 0x54, 0x5A, 0x49, 0x4E, 0x46, 0x4F, 0x3A, 0}; /* "X-TZINFO:" */ -static const UChar ICU_TZINFO_PARTIAL[] = {0x2F, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6C, 0x40, 0}; /* "/Partial@" */ -static const UChar ICU_TZINFO_SIMPLE[] = {0x2F, 0x53, 0x69, 0x6D, 0x70, 0x6C, 0x65, 0x40, 0}; /* "/Simple@" */ - - -/* - * Simple fixed digit ASCII number to integer converter - */ -static int32_t parseAsciiDigits(const UnicodeString& str, int32_t start, int32_t length, UErrorCode& status) { - if (U_FAILURE(status)) { - return 0; - } - if (length <= 0 || str.length() < start || (start + length) > str.length()) { - status = U_INVALID_FORMAT_ERROR; - return 0; - } - int32_t sign = 1; - if (str.charAt(start) == PLUS) { - start++; - length--; - } else if (str.charAt(start) == MINUS) { - sign = -1; - start++; - length--; - } - int32_t num = 0; - for (int32_t i = 0; i < length; i++) { - int32_t digit = str.charAt(start + i) - 0x0030; - if (digit < 0 || digit > 9) { - status = U_INVALID_FORMAT_ERROR; - return 0; - } - num = 10 * num + digit; - } - return sign * num; -} - -static UnicodeString& appendAsciiDigits(int32_t number, uint8_t length, UnicodeString& str) { - UBool negative = FALSE; - int32_t digits[10]; // max int32_t is 10 decimal digits - int32_t i; - - if (number < 0) { - negative = TRUE; - number *= -1; - } - - length = length > 10 ? 10 : length; - if (length == 0) { - // variable length - i = 0; - do { - digits[i++] = number % 10; - number /= 10; - } while (number != 0); - length = i; - } else { - // fixed digits - for (i = 0; i < length; i++) { - digits[i] = number % 10; - number /= 10; - } - } - if (negative) { - str.append(MINUS); - } - for (i = length - 1; i >= 0; i--) { - str.append((UChar)(digits[i] + 0x0030)); - } - return str; -} - -static UnicodeString& appendMillis(UDate date, UnicodeString& str) { - UBool negative = FALSE; - int32_t digits[20]; // max int64_t is 20 decimal digits - int32_t i; - int64_t number; - - if (date < MIN_MILLIS) { - number = (int64_t)MIN_MILLIS; - } else if (date > MAX_MILLIS) { - number = (int64_t)MAX_MILLIS; - } else { - number = (int64_t)date; - } - if (number < 0) { - negative = TRUE; - number *= -1; - } - i = 0; - do { - digits[i++] = (int32_t)(number % 10); - number /= 10; - } while (number != 0); - - if (negative) { - str.append(MINUS); - } - i--; - while (i >= 0) { - str.append((UChar)(digits[i--] + 0x0030)); - } - return str; -} - -/* - * Convert date/time to RFC2445 Date-Time form #1 DATE WITH LOCAL TIME - */ -static UnicodeString& getDateTimeString(UDate time, UnicodeString& str) { - int32_t year, month, dom, dow, doy, mid; - Grego::timeToFields(time, year, month, dom, dow, doy, mid); - - str.remove(); - appendAsciiDigits(year, 4, str); - appendAsciiDigits(month + 1, 2, str); - appendAsciiDigits(dom, 2, str); - str.append((UChar)0x0054 /*'T'*/); - - int32_t t = mid; - int32_t hour = t / U_MILLIS_PER_HOUR; - t %= U_MILLIS_PER_HOUR; - int32_t min = t / U_MILLIS_PER_MINUTE; - t %= U_MILLIS_PER_MINUTE; - int32_t sec = t / U_MILLIS_PER_SECOND; - - appendAsciiDigits(hour, 2, str); - appendAsciiDigits(min, 2, str); - appendAsciiDigits(sec, 2, str); - return str; -} - -/* - * Convert date/time to RFC2445 Date-Time form #2 DATE WITH UTC TIME - */ -static UnicodeString& getUTCDateTimeString(UDate time, UnicodeString& str) { - getDateTimeString(time, str); - str.append((UChar)0x005A /*'Z'*/); - return str; -} - -/* - * Parse RFC2445 Date-Time form #1 DATE WITH LOCAL TIME and - * #2 DATE WITH UTC TIME - */ -static UDate parseDateTimeString(const UnicodeString& str, int32_t offset, UErrorCode& status) { - if (U_FAILURE(status)) { - return 0.0; - } - - int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0; - UBool isUTC = FALSE; - UBool isValid = FALSE; - do { - int length = str.length(); - if (length != 15 && length != 16) { - // FORM#1 15 characters, such as "20060317T142115" - // FORM#2 16 characters, such as "20060317T142115Z" - break; - } - if (str.charAt(8) != 0x0054) { - // charcter "T" must be used for separating date and time - break; - } - if (length == 16) { - if (str.charAt(15) != 0x005A) { - // invalid format - break; - } - isUTC = TRUE; - } - - year = parseAsciiDigits(str, 0, 4, status); - month = parseAsciiDigits(str, 4, 2, status) - 1; // 0-based - day = parseAsciiDigits(str, 6, 2, status); - hour = parseAsciiDigits(str, 9, 2, status); - min = parseAsciiDigits(str, 11, 2, status); - sec = parseAsciiDigits(str, 13, 2, status); - - if (U_FAILURE(status)) { - break; - } - - // check valid range - int32_t maxDayOfMonth = Grego::monthLength(year, month); - if (year < 0 || month < 0 || month > 11 || day < 1 || day > maxDayOfMonth || - hour < 0 || hour >= 24 || min < 0 || min >= 60 || sec < 0 || sec >= 60) { - break; - } - - isValid = TRUE; - } while(false); - - if (!isValid) { - status = U_INVALID_FORMAT_ERROR; - return 0.0; - } - // Calculate the time - UDate time = Grego::fieldsToDay(year, month, day) * U_MILLIS_PER_DAY; - time += (hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE + sec * U_MILLIS_PER_SECOND); - if (!isUTC) { - time -= offset; - } - return time; -} - -/* - * Convert RFC2445 utc-offset string to milliseconds - */ -static int32_t offsetStrToMillis(const UnicodeString& str, UErrorCode& status) { - if (U_FAILURE(status)) { - return 0; - } - - UBool isValid = FALSE; - int32_t sign = 0, hour = 0, min = 0, sec = 0; - - do { - int length = str.length(); - if (length != 5 && length != 7) { - // utf-offset must be 5 or 7 characters - break; - } - // sign - UChar s = str.charAt(0); - if (s == PLUS) { - sign = 1; - } else if (s == MINUS) { - sign = -1; - } else { - // utf-offset must start with "+" or "-" - break; - } - hour = parseAsciiDigits(str, 1, 2, status); - min = parseAsciiDigits(str, 3, 2, status); - if (length == 7) { - sec = parseAsciiDigits(str, 5, 2, status); - } - if (U_FAILURE(status)) { - break; - } - isValid = true; - } while(false); - - if (!isValid) { - status = U_INVALID_FORMAT_ERROR; - return 0; - } - int32_t millis = sign * ((hour * 60 + min) * 60 + sec) * 1000; - return millis; -} - -/* - * Convert milliseconds to RFC2445 utc-offset string - */ -static void millisToOffset(int32_t millis, UnicodeString& str) { - str.remove(); - if (millis >= 0) { - str.append(PLUS); - } else { - str.append(MINUS); - millis = -millis; - } - int32_t hour, min, sec; - int32_t t = millis / 1000; - - sec = t % 60; - t = (t - sec) / 60; - min = t % 60; - hour = t / 60; - - appendAsciiDigits(hour, 2, str); - appendAsciiDigits(min, 2, str); - appendAsciiDigits(sec, 2, str); -} - -/* - * Create a default TZNAME from TZID - */ -static void getDefaultTZName(const UnicodeString tzid, UBool isDST, UnicodeString& zonename) { - zonename = tzid; - if (isDST) { - zonename += UNICODE_STRING_SIMPLE("(DST)"); - } else { - zonename += UNICODE_STRING_SIMPLE("(STD)"); - } -} - -/* - * Parse individual RRULE - * - * On return - - * - * month calculated by BYMONTH-1, or -1 when not found - * dow day of week in BYDAY, or 0 when not found - * wim day of week ordinal number in BYDAY, or 0 when not found - * dom an array of day of month - * domCount number of availble days in dom (domCount is specifying the size of dom on input) - * until time defined by UNTIL attribute or MIN_MILLIS if not available - */ -static void parseRRULE(const UnicodeString& rrule, int32_t& month, int32_t& dow, int32_t& wim, - int32_t* dom, int32_t& domCount, UDate& until, UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - int32_t numDom = 0; - - month = -1; - dow = 0; - wim = 0; - until = MIN_MILLIS; - - UBool yearly = FALSE; - //UBool parseError = FALSE; - - int32_t prop_start = 0; - int32_t prop_end; - UnicodeString prop, attr, value; - UBool nextProp = TRUE; - - while (nextProp) { - prop_end = rrule.indexOf(SEMICOLON, prop_start); - if (prop_end == -1) { - prop.setTo(rrule, prop_start); - nextProp = FALSE; - } else { - prop.setTo(rrule, prop_start, prop_end - prop_start); - prop_start = prop_end + 1; - } - int32_t eql = prop.indexOf(EQUALS_SIGN); - if (eql != -1) { - attr.setTo(prop, 0, eql); - value.setTo(prop, eql + 1); - } else { - goto rruleParseError; - } - - if (attr.compare(ICAL_FREQ, -1) == 0) { - // only support YEARLY frequency type - if (value.compare(ICAL_YEARLY, -1) == 0) { - yearly = TRUE; - } else { - goto rruleParseError; - } - } else if (attr.compare(ICAL_UNTIL, -1) == 0) { - // ISO8601 UTC format, for example, "20060315T020000Z" - until = parseDateTimeString(value, 0, status); - if (U_FAILURE(status)) { - goto rruleParseError; - } - } else if (attr.compare(ICAL_BYMONTH, -1) == 0) { - // Note: BYMONTH may contain multiple months, but only single month make sense for - // VTIMEZONE property. - if (value.length() > 2) { - goto rruleParseError; - } - month = parseAsciiDigits(value, 0, value.length(), status) - 1; - if (U_FAILURE(status) || month < 0 || month >= 12) { - goto rruleParseError; - } - } else if (attr.compare(ICAL_BYDAY, -1) == 0) { - // Note: BYDAY may contain multiple day of week separated by comma. It is unlikely used for - // VTIMEZONE property. We do not support the case. - - // 2-letter format is used just for representing a day of week, for example, "SU" for Sunday - // 3 or 4-letter format is used for represeinging Nth day of week, for example, "-1SA" for last Saturday - int32_t length = value.length(); - if (length < 2 || length > 4) { - goto rruleParseError; - } - if (length > 2) { - // Nth day of week - int32_t sign = 1; - if (value.charAt(0) == PLUS) { - sign = 1; - } else if (value.charAt(0) == MINUS) { - sign = -1; - } else if (length == 4) { - goto rruleParseError; - } - int32_t n = parseAsciiDigits(value, length - 3, 1, status); - if (U_FAILURE(status) || n == 0 || n > 4) { - goto rruleParseError; - } - wim = n * sign; - value.remove(0, length - 2); - } - int32_t wday; - for (wday = 0; wday < 7; wday++) { - if (value.compare(ICAL_DOW_NAMES[wday], 2) == 0) { - break; - } - } - if (wday < 7) { - // Sunday(1) - Saturday(7) - dow = wday + 1; - } else { - goto rruleParseError; - } - } else if (attr.compare(ICAL_BYMONTHDAY, -1) == 0) { - // Note: BYMONTHDAY may contain multiple days delimitted by comma - // - // A value of BYMONTHDAY could be negative, for example, -1 means - // the last day in a month - int32_t dom_idx = 0; - int32_t dom_start = 0; - int32_t dom_end; - UBool nextDOM = TRUE; - while (nextDOM) { - dom_end = value.indexOf(COMMA, dom_start); - if (dom_end == -1) { - dom_end = value.length(); - nextDOM = FALSE; - } - if (dom_idx < domCount) { - dom[dom_idx] = parseAsciiDigits(value, dom_start, dom_end - dom_start, status); - if (U_FAILURE(status)) { - goto rruleParseError; - } - dom_idx++; - } else { - status = U_BUFFER_OVERFLOW_ERROR; - goto rruleParseError; - } - dom_start = dom_end + 1; - } - numDom = dom_idx; - } - } - if (!yearly) { - // FREQ=YEARLY must be set - goto rruleParseError; - } - // Set actual number of parsed DOM (ICAL_BYMONTHDAY) - domCount = numDom; - return; - -rruleParseError: - if (U_SUCCESS(status)) { - // Set error status - status = U_INVALID_FORMAT_ERROR; - } -} - -static TimeZoneRule* createRuleByRRULE(const UnicodeString& zonename, int rawOffset, int dstSavings, UDate start, - UVector* dates, int fromOffset, UErrorCode& status) { - if (U_FAILURE(status)) { - return NULL; - } - if (dates == NULL || dates->size() == 0) { - status = U_ILLEGAL_ARGUMENT_ERROR; - return NULL; - } - - int32_t i, j; - DateTimeRule *adtr = NULL; - - // Parse the first rule - UnicodeString rrule = *((UnicodeString*)dates->elementAt(0)); - int32_t month, dayOfWeek, nthDayOfWeek, dayOfMonth = 0; - int32_t days[7]; - int32_t daysCount = sizeof(days)/sizeof(days[0]); - UDate until; - - parseRRULE(rrule, month, dayOfWeek, nthDayOfWeek, days, daysCount, until, status); - if (U_FAILURE(status)) { - return NULL; - } - - if (dates->size() == 1) { - // No more rules - if (daysCount > 1) { - // Multiple BYMONTHDAY values - if (daysCount != 7 || month == -1 || dayOfWeek == 0) { - // Only support the rule using 7 continuous days - // BYMONTH and BYDAY must be set at the same time - goto unsupportedRRule; - } - int32_t firstDay = 31; // max possible number of dates in a month - for (i = 0; i < 7; i++) { - // Resolve negative day numbers. A negative day number should - // not be used in February, but if we see such case, we use 28 - // as the base. - if (days[i] < 0) { - days[i] = MONTHLENGTH[month] + days[i] + 1; - } - if (days[i] < firstDay) { - firstDay = days[i]; - } - } - // Make sure days are continuous - for (i = 1; i < 7; i++) { - UBool found = FALSE; - for (j = 0; j < 7; j++) { - if (days[j] == firstDay + i) { - found = TRUE; - break; - } - } - if (!found) { - // days are not continuous - goto unsupportedRRule; - } - } - // Use DOW_GEQ_DOM rule with firstDay as the start date - dayOfMonth = firstDay; - } - } else { - // Check if BYMONTH + BYMONTHDAY + BYDAY rule with multiple RRULE lines. - // Otherwise, not supported. - if (month == -1 || dayOfWeek == 0 || daysCount == 0) { - // This is not the case - goto unsupportedRRule; - } - // Parse the rest of rules if number of rules is not exceeding 7. - // We can only support 7 continuous days starting from a day of month. - if (dates->size() > 7) { - goto unsupportedRRule; - } - - // Note: To check valid date range across multiple rule is a little - // bit complicated. For now, this code is not doing strict range - // checking across month boundary - - int32_t earliestMonth = month; - int32_t earliestDay = 31; - for (i = 0; i < daysCount; i++) { - int32_t dom = days[i]; - dom = dom > 0 ? dom : MONTHLENGTH[month] + dom + 1; - earliestDay = dom < earliestDay ? dom : earliestDay; - } - - int32_t anotherMonth = -1; - for (i = 1; i < dates->size(); i++) { - rrule = *((UnicodeString*)dates->elementAt(i)); - UDate tmp_until; - int32_t tmp_month, tmp_dayOfWeek, tmp_nthDayOfWeek; - int32_t tmp_days[7]; - int32_t tmp_daysCount = sizeof(tmp_days)/sizeof(tmp_days[0]); - parseRRULE(rrule, tmp_month, tmp_dayOfWeek, tmp_nthDayOfWeek, tmp_days, tmp_daysCount, tmp_until, status); - if (U_FAILURE(status)) { - return NULL; - } - // If UNTIL is newer than previous one, use the one - if (tmp_until > until) { - until = tmp_until; - } - - // Check if BYMONTH + BYMONTHDAY + BYDAY rule - if (tmp_month == -1 || tmp_dayOfWeek == 0 || tmp_daysCount == 0) { - goto unsupportedRRule; - } - // Count number of BYMONTHDAY - if (daysCount + tmp_daysCount > 7) { - // We cannot support BYMONTHDAY more than 7 - goto unsupportedRRule; - } - // Check if the same BYDAY is used. Otherwise, we cannot - // support the rule - if (tmp_dayOfWeek != dayOfWeek) { - goto unsupportedRRule; - } - // Check if the month is same or right next to the primary month - if (tmp_month != month) { - if (anotherMonth == -1) { - int32_t diff = tmp_month - month; - if (diff == -11 || diff == -1) { - // Previous month - anotherMonth = tmp_month; - earliestMonth = anotherMonth; - // Reset earliest day - earliestDay = 31; - } else if (diff == 11 || diff == 1) { - // Next month - anotherMonth = tmp_month; - } else { - // The day range cannot exceed more than 2 months - goto unsupportedRRule; - } - } else if (tmp_month != month && tmp_month != anotherMonth) { - // The day range cannot exceed more than 2 months - goto unsupportedRRule; - } - } - // If ealier month, go through days to find the earliest day - if (tmp_month == earliestMonth) { - for (j = 0; j < tmp_daysCount; j++) { - tmp_days[j] = tmp_days[j] > 0 ? tmp_days[j] : MONTHLENGTH[tmp_month] + tmp_days[j] + 1; - earliestDay = tmp_days[j] < earliestDay ? tmp_days[j] : earliestDay; - } - } - daysCount += tmp_daysCount; - } - if (daysCount != 7) { - // Number of BYMONTHDAY entries must be 7 - goto unsupportedRRule; - } - month = earliestMonth; - dayOfMonth = earliestDay; - } - - // Calculate start/end year and missing fields - int32_t startYear, startMonth, startDOM, startDOW, startDOY, startMID; - Grego::timeToFields(start + fromOffset, startYear, startMonth, startDOM, - startDOW, startDOY, startMID); - if (month == -1) { - // If BYMONTH is not set, use the month of DTSTART - month = startMonth; - } - if (dayOfWeek == 0 && nthDayOfWeek == 0 && dayOfMonth == 0) { - // If only YEARLY is set, use the day of DTSTART as BYMONTHDAY - dayOfMonth = startDOM; - } - - int32_t endYear; - if (until != MIN_MILLIS) { - int32_t endMonth, endDOM, endDOW, endDOY, endMID; - Grego::timeToFields(until, endYear, endMonth, endDOM, endDOW, endDOY, endMID); - } else { - endYear = AnnualTimeZoneRule::MAX_YEAR; - } - - // Create the AnnualDateTimeRule - if (dayOfWeek == 0 && nthDayOfWeek == 0 && dayOfMonth != 0) { - // Day in month rule, for example, 15th day in the month - adtr = new DateTimeRule(month, dayOfMonth, startMID, DateTimeRule::WALL_TIME); - } else if (dayOfWeek != 0 && nthDayOfWeek != 0 && dayOfMonth == 0) { - // Nth day of week rule, for example, last Sunday - adtr = new DateTimeRule(month, nthDayOfWeek, dayOfWeek, startMID, DateTimeRule::WALL_TIME); - } else if (dayOfWeek != 0 && nthDayOfWeek == 0 && dayOfMonth != 0) { - // First day of week after day of month rule, for example, - // first Sunday after 15th day in the month - adtr = new DateTimeRule(month, dayOfMonth, dayOfWeek, TRUE, startMID, DateTimeRule::WALL_TIME); - } - if (adtr == NULL) { - goto unsupportedRRule; - } - return new AnnualTimeZoneRule(zonename, rawOffset, dstSavings, adtr, startYear, endYear); - -unsupportedRRule: - status = U_INVALID_STATE_ERROR; - return NULL; -} - -/* - * Create a TimeZoneRule by the RDATE definition - */ -static TimeZoneRule* createRuleByRDATE(const UnicodeString& zonename, int32_t rawOffset, int32_t dstSavings, - UDate start, UVector* dates, int32_t fromOffset, UErrorCode& status) { - if (U_FAILURE(status)) { - return NULL; - } - TimeArrayTimeZoneRule *retVal = NULL; - if (dates == NULL || dates->size() == 0) { - // When no RDATE line is provided, use start (DTSTART) - // as the transition time - retVal = new TimeArrayTimeZoneRule(zonename, rawOffset, dstSavings, - &start, 1, DateTimeRule::UTC_TIME); - } else { - // Create an array of transition times - int32_t size = dates->size(); - UDate* times = (UDate*)uprv_malloc(sizeof(UDate) * size); - if (times == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - for (int32_t i = 0; i < size; i++) { - UnicodeString *datestr = (UnicodeString*)dates->elementAt(i); - times[i] = parseDateTimeString(*datestr, fromOffset, status); - if (U_FAILURE(status)) { - uprv_free(times); - return NULL; - } - } - retVal = new TimeArrayTimeZoneRule(zonename, rawOffset, dstSavings, - times, size, DateTimeRule::UTC_TIME); - uprv_free(times); - } - return retVal; -} - -/* - * Check if the DOW rule specified by month, weekInMonth and dayOfWeek is equivalent - * to the DateTimerule. - */ -static UBool isEquivalentDateRule(int32_t month, int32_t weekInMonth, int32_t dayOfWeek, const DateTimeRule *dtrule) { - if (month != dtrule->getRuleMonth() || dayOfWeek != dtrule->getRuleDayOfWeek()) { - return FALSE; - } - if (dtrule->getTimeRuleType() != DateTimeRule::WALL_TIME) { - // Do not try to do more intelligent comparison for now. - return FALSE; - } - if (dtrule->getDateRuleType() == DateTimeRule::DOW - && dtrule->getRuleWeekInMonth() == weekInMonth) { - return TRUE; - } - int32_t ruleDOM = dtrule->getRuleDayOfMonth(); - if (dtrule->getDateRuleType() == DateTimeRule::DOW_GEQ_DOM) { - if (ruleDOM%7 == 1 && (ruleDOM + 6)/7 == weekInMonth) { - return TRUE; - } - if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - ruleDOM)%7 == 6 - && weekInMonth == -1*((MONTHLENGTH[month]-ruleDOM+1)/7)) { - return TRUE; - } - } - if (dtrule->getDateRuleType() == DateTimeRule::DOW_LEQ_DOM) { - if (ruleDOM%7 == 0 && ruleDOM/7 == weekInMonth) { - return TRUE; - } - if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - ruleDOM)%7 == 0 - && weekInMonth == -1*((MONTHLENGTH[month] - ruleDOM)/7 + 1)) { - return TRUE; - } - } - return FALSE; -} - -/* - * Convert the rule to its equivalent rule using WALL_TIME mode. - * This function returns NULL when the specified DateTimeRule is already - * using WALL_TIME mode. - */ -static DateTimeRule* toWallTimeRule(const DateTimeRule* rule, int32_t rawOffset, int32_t dstSavings) { - if (rule->getTimeRuleType() == DateTimeRule::WALL_TIME) { - return NULL; - } - int32_t wallt = rule->getRuleMillisInDay(); - if (rule->getTimeRuleType() == DateTimeRule::UTC_TIME) { - wallt += (rawOffset + dstSavings); - } else if (rule->getTimeRuleType() == DateTimeRule::STANDARD_TIME) { - wallt += dstSavings; - } - - int32_t month = -1, dom = 0, dow = 0; - DateTimeRule::DateRuleType dtype; - int32_t dshift = 0; - if (wallt < 0) { - dshift = -1; - wallt += U_MILLIS_PER_DAY; - } else if (wallt >= U_MILLIS_PER_DAY) { - dshift = 1; - wallt -= U_MILLIS_PER_DAY; - } - - month = rule->getRuleMonth(); - dom = rule->getRuleDayOfMonth(); - dow = rule->getRuleDayOfWeek(); - dtype = rule->getDateRuleType(); - - if (dshift != 0) { - if (dtype == DateTimeRule::DOW) { - // Convert to DOW_GEW_DOM or DOW_LEQ_DOM rule first - int32_t wim = rule->getRuleWeekInMonth(); - if (wim > 0) { - dtype = DateTimeRule::DOW_GEQ_DOM; - dom = 7 * (wim - 1) + 1; - } else { - dtype = DateTimeRule::DOW_LEQ_DOM; - dom = MONTHLENGTH[month] + 7 * (wim + 1); - } - } - // Shift one day before or after - dom += dshift; - if (dom == 0) { - month--; - month = month < UCAL_JANUARY ? UCAL_DECEMBER : month; - dom = MONTHLENGTH[month]; - } else if (dom > MONTHLENGTH[month]) { - month++; - month = month > UCAL_DECEMBER ? UCAL_JANUARY : month; - dom = 1; - } - if (dtype != DateTimeRule::DOM) { - // Adjust day of week - dow += dshift; - if (dow < UCAL_SUNDAY) { - dow = UCAL_SATURDAY; - } else if (dow > UCAL_SATURDAY) { - dow = UCAL_SUNDAY; - } - } - } - // Create a new rule - DateTimeRule *modifiedRule; - if (dtype == DateTimeRule::DOM) { - modifiedRule = new DateTimeRule(month, dom, wallt, DateTimeRule::WALL_TIME); - } else { - modifiedRule = new DateTimeRule(month, dom, dow, - (dtype == DateTimeRule::DOW_GEQ_DOM), wallt, DateTimeRule::WALL_TIME); - } - return modifiedRule; -} - -/* - * Minumum implementations of stream writer/reader, writing/reading - * UnicodeString. For now, we do not want to introduce the dependency - * on the ICU I/O stream in this module. But we want to keep the code - * equivalent to the ICU4J implementation, which utilizes java.io.Writer/ - * Reader. - */ -class VTZWriter { -public: - VTZWriter(UnicodeString& out); - ~VTZWriter(); - - void write(const UnicodeString& str); - void write(UChar ch); - void write(const UChar* str); - //void write(const UChar* str, int32_t length); -private: - UnicodeString* out; -}; - -VTZWriter::VTZWriter(UnicodeString& output) { - out = &output; -} - -VTZWriter::~VTZWriter() { -} - -void -VTZWriter::write(const UnicodeString& str) { - out->append(str); -} - -void -VTZWriter::write(UChar ch) { - out->append(ch); -} - -void -VTZWriter::write(const UChar* str) { - out->append(str, -1); -} - -/* -void -VTZWriter::write(const UChar* str, int32_t length) { - out->append(str, length); -} -*/ - -class VTZReader { -public: - VTZReader(const UnicodeString& input); - ~VTZReader(); - - UChar read(void); -private: - const UnicodeString* in; - int32_t index; -}; - -VTZReader::VTZReader(const UnicodeString& input) { - in = &input; - index = 0; -} - -VTZReader::~VTZReader() { -} - -UChar -VTZReader::read(void) { - UChar ch = 0xFFFF; - if (index < in->length()) { - ch = in->charAt(index); - } - index++; - return ch; -} - - -UOBJECT_DEFINE_RTTI_IMPLEMENTATION(VTimeZone) - -VTimeZone::VTimeZone() -: BasicTimeZone(), tz(NULL), vtzlines(NULL), - lastmod(MAX_MILLIS) { -} - -VTimeZone::VTimeZone(const VTimeZone& source) -: BasicTimeZone(source), tz(NULL), vtzlines(NULL), - tzurl(source.tzurl), lastmod(source.lastmod), - olsonzid(source.olsonzid), icutzver(source.icutzver) { - if (source.tz != NULL) { - tz = (BasicTimeZone*)source.tz->clone(); - } - if (source.vtzlines != NULL) { - UErrorCode status = U_ZERO_ERROR; - int32_t size = source.vtzlines->size(); - vtzlines = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, size, status); - if (U_SUCCESS(status)) { - for (int32_t i = 0; i < size; i++) { - UnicodeString *line = (UnicodeString*)source.vtzlines->elementAt(i); - vtzlines->addElement(line->clone(), status); - if (U_FAILURE(status)) { - break; - } - } - } - if (U_FAILURE(status) && vtzlines != NULL) { - delete vtzlines; - } - } -} - -VTimeZone::~VTimeZone() { - if (tz != NULL) { - delete tz; - } - if (vtzlines != NULL) { - delete vtzlines; - } -} - -VTimeZone& -VTimeZone::operator=(const VTimeZone& right) { - if (this == &right) { - return *this; - } - if (*this != right) { - BasicTimeZone::operator=(right); - if (tz != NULL) { - delete tz; - tz = NULL; - } - if (right.tz != NULL) { - tz = (BasicTimeZone*)right.tz->clone(); - } - if (vtzlines != NULL) { - delete vtzlines; - } - if (right.vtzlines != NULL) { - UErrorCode status = U_ZERO_ERROR; - int32_t size = right.vtzlines->size(); - vtzlines = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, size, status); - if (U_SUCCESS(status)) { - for (int32_t i = 0; i < size; i++) { - UnicodeString *line = (UnicodeString*)right.vtzlines->elementAt(i); - vtzlines->addElement(line->clone(), status); - if (U_FAILURE(status)) { - break; - } - } - } - if (U_FAILURE(status) && vtzlines != NULL) { - delete vtzlines; - vtzlines = NULL; - } - } - tzurl = right.tzurl; - lastmod = right.lastmod; - olsonzid = right.olsonzid; - icutzver = right.icutzver; - } - return *this; -} - -UBool -VTimeZone::operator==(const TimeZone& that) const { - if (this == &that) { - return TRUE; - } - if (typeid(*this) != typeid(that) || !BasicTimeZone::operator==(that)) { - return FALSE; - } - VTimeZone *vtz = (VTimeZone*)&that; - if (*tz == *(vtz->tz) - && tzurl == vtz->tzurl - && lastmod == vtz->lastmod - /* && olsonzid = that.olsonzid */ - /* && icutzver = that.icutzver */) { - return TRUE; - } - return FALSE; -} - -UBool -VTimeZone::operator!=(const TimeZone& that) const { - return !operator==(that); -} - -VTimeZone* -VTimeZone::createVTimeZoneByID(const UnicodeString& ID) { - VTimeZone *vtz = new VTimeZone(); - vtz->tz = (BasicTimeZone*)TimeZone::createTimeZone(ID); - vtz->tz->getID(vtz->olsonzid); - - // Set ICU tzdata version - UErrorCode status = U_ZERO_ERROR; - UResourceBundle *bundle = NULL; - const UChar* versionStr = NULL; - int32_t len = 0; - bundle = ures_openDirect(NULL, "zoneinfo64", &status); - versionStr = ures_getStringByKey(bundle, "TZVersion", &len, &status); - if (U_SUCCESS(status)) { - vtz->icutzver.setTo(versionStr, len); - } - ures_close(bundle); - return vtz; -} - -VTimeZone* -VTimeZone::createVTimeZoneFromBasicTimeZone(const BasicTimeZone& basic_time_zone, UErrorCode &status) { - if (U_FAILURE(status)) { - return NULL; - } - VTimeZone *vtz = new VTimeZone(); - if (vtz == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - return NULL; - } - vtz->tz = (BasicTimeZone *)basic_time_zone.clone(); - if (vtz->tz == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - delete vtz; - return NULL; - } - vtz->tz->getID(vtz->olsonzid); - - // Set ICU tzdata version - UResourceBundle *bundle = NULL; - const UChar* versionStr = NULL; - int32_t len = 0; - bundle = ures_openDirect(NULL, "zoneinfo64", &status); - versionStr = ures_getStringByKey(bundle, "TZVersion", &len, &status); - if (U_SUCCESS(status)) { - vtz->icutzver.setTo(versionStr, len); - } - ures_close(bundle); - return vtz; -} - -VTimeZone* -VTimeZone::createVTimeZone(const UnicodeString& vtzdata, UErrorCode& status) { - if (U_FAILURE(status)) { - return NULL; - } - VTZReader reader(vtzdata); - VTimeZone *vtz = new VTimeZone(); - vtz->load(reader, status); - if (U_FAILURE(status)) { - delete vtz; - return NULL; - } - return vtz; -} - -UBool -VTimeZone::getTZURL(UnicodeString& url) const { - if (tzurl.length() > 0) { - url = tzurl; - return TRUE; - } - return FALSE; -} - -void -VTimeZone::setTZURL(const UnicodeString& url) { - tzurl = url; -} - -UBool -VTimeZone::getLastModified(UDate& lastModified) const { - if (lastmod != MAX_MILLIS) { - lastModified = lastmod; - return TRUE; - } - return FALSE; -} - -void -VTimeZone::setLastModified(UDate lastModified) { - lastmod = lastModified; -} - -void -VTimeZone::write(UnicodeString& result, UErrorCode& status) const { - result.remove(); - VTZWriter writer(result); - write(writer, status); -} - -void -VTimeZone::write(UDate start, UnicodeString& result, UErrorCode& status) /*const*/ { - result.remove(); - VTZWriter writer(result); - write(start, writer, status); -} - -void -VTimeZone::writeSimple(UDate time, UnicodeString& result, UErrorCode& status) /*const*/ { - result.remove(); - VTZWriter writer(result); - writeSimple(time, writer, status); -} - -TimeZone* -VTimeZone::clone(void) const { - return new VTimeZone(*this); -} - -int32_t -VTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, - uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const { - return tz->getOffset(era, year, month, day, dayOfWeek, millis, status); -} - -int32_t -VTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day, - uint8_t dayOfWeek, int32_t millis, - int32_t monthLength, UErrorCode& status) const { - return tz->getOffset(era, year, month, day, dayOfWeek, millis, monthLength, status); -} - -void -VTimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset, - int32_t& dstOffset, UErrorCode& status) const { - return tz->getOffset(date, local, rawOffset, dstOffset, status); -} - -void -VTimeZone::setRawOffset(int32_t offsetMillis) { - tz->setRawOffset(offsetMillis); -} - -int32_t -VTimeZone::getRawOffset(void) const { - return tz->getRawOffset(); -} - -UBool -VTimeZone::useDaylightTime(void) const { - return tz->useDaylightTime(); -} - -UBool -VTimeZone::inDaylightTime(UDate date, UErrorCode& status) const { - return tz->inDaylightTime(date, status); -} - -UBool -VTimeZone::hasSameRules(const TimeZone& other) const { - return tz->hasSameRules(other); -} - -UBool -VTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) /*const*/ { - return tz->getNextTransition(base, inclusive, result); -} - -UBool -VTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) /*const*/ { - return tz->getPreviousTransition(base, inclusive, result); -} - -int32_t -VTimeZone::countTransitionRules(UErrorCode& status) /*const*/ { - return tz->countTransitionRules(status); -} - -void -VTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial, - const TimeZoneRule* trsrules[], int32_t& trscount, - UErrorCode& status) /*const*/ { - tz->getTimeZoneRules(initial, trsrules, trscount, status); -} - -void -VTimeZone::load(VTZReader& reader, UErrorCode& status) { - vtzlines = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, DEFAULT_VTIMEZONE_LINES, status); - if (U_FAILURE(status)) { - return; - } - UBool eol = FALSE; - UBool start = FALSE; - UBool success = FALSE; - UnicodeString line; - - while (TRUE) { - UChar ch = reader.read(); - if (ch == 0xFFFF) { - // end of file - if (start && line.startsWith(ICAL_END_VTIMEZONE, -1)) { - vtzlines->addElement(new UnicodeString(line), status); - if (U_FAILURE(status)) { - goto cleanupVtzlines; - } - success = TRUE; - } - break; - } - if (ch == 0x000D) { - // CR, must be followed by LF according to the definition in RFC2445 - continue; - } - if (eol) { - if (ch != 0x0009 && ch != 0x0020) { - // NOT followed by TAB/SP -> new line - if (start) { - if (line.length() > 0) { - vtzlines->addElement(new UnicodeString(line), status); - if (U_FAILURE(status)) { - goto cleanupVtzlines; - } - } - } - line.remove(); - if (ch != 0x000A) { - line.append(ch); - } - } - eol = FALSE; - } else { - if (ch == 0x000A) { - // LF - eol = TRUE; - if (start) { - if (line.startsWith(ICAL_END_VTIMEZONE, -1)) { - vtzlines->addElement(new UnicodeString(line), status); - if (U_FAILURE(status)) { - goto cleanupVtzlines; - } - success = TRUE; - break; - } - } else { - if (line.startsWith(ICAL_BEGIN_VTIMEZONE, -1)) { - vtzlines->addElement(new UnicodeString(line), status); - if (U_FAILURE(status)) { - goto cleanupVtzlines; - } - line.remove(); - start = TRUE; - eol = FALSE; - } - } - } else { - line.append(ch); - } - } - } - if (!success) { - if (U_SUCCESS(status)) { - status = U_INVALID_STATE_ERROR; - } - goto cleanupVtzlines; - } - parse(status); - return; - -cleanupVtzlines: - delete vtzlines; - vtzlines = NULL; -} - -// parser state -#define INI 0 // Initial state -#define VTZ 1 // In VTIMEZONE -#define TZI 2 // In STANDARD or DAYLIGHT - -#define DEF_DSTSAVINGS (60*60*1000) -#define DEF_TZSTARTTIME (0.0) - -void -VTimeZone::parse(UErrorCode& status) { - if (U_FAILURE(status)) { - return; - } - if (vtzlines == NULL || vtzlines->size() == 0) { - status = U_INVALID_STATE_ERROR; - return; - } - InitialTimeZoneRule *initialRule = NULL; - RuleBasedTimeZone *rbtz = NULL; - - // timezone ID - UnicodeString tzid; - - int32_t state = INI; - int32_t n = 0; - UBool dst = FALSE; // current zone type - UnicodeString from; // current zone from offset - UnicodeString to; // current zone offset - UnicodeString zonename; // current zone name - UnicodeString dtstart; // current zone starts - UBool isRRULE = FALSE; // true if the rule is described by RRULE - int32_t initialRawOffset = 0; // initial offset - int32_t initialDSTSavings = 0; // initial offset - UDate firstStart = MAX_MILLIS; // the earliest rule start time - UnicodeString name; // RFC2445 prop name - UnicodeString value; // RFC2445 prop value - - UVector *dates = NULL; // list of RDATE or RRULE strings - UVector *rules = NULL; // list of TimeZoneRule instances - - int32_t finalRuleIdx = -1; - int32_t finalRuleCount = 0; - - rules = new UVector(status); - if (U_FAILURE(status)) { - goto cleanupParse; - } - // Set the deleter to remove TimeZoneRule vectors to avoid memory leaks due to unowned TimeZoneRules. - rules->setDeleter(deleteTimeZoneRule); - - dates = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status); - if (U_FAILURE(status)) { - goto cleanupParse; - } - if (rules == NULL || dates == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - goto cleanupParse; - } - - for (n = 0; n < vtzlines->size(); n++) { - UnicodeString *line = (UnicodeString*)vtzlines->elementAt(n); - int32_t valueSep = line->indexOf(COLON); - if (valueSep < 0) { - continue; - } - name.setTo(*line, 0, valueSep); - value.setTo(*line, valueSep + 1); - - switch (state) { - case INI: - if (name.compare(ICAL_BEGIN, -1) == 0 - && value.compare(ICAL_VTIMEZONE, -1) == 0) { - state = VTZ; - } - break; - - case VTZ: - if (name.compare(ICAL_TZID, -1) == 0) { - tzid = value; - } else if (name.compare(ICAL_TZURL, -1) == 0) { - tzurl = value; - } else if (name.compare(ICAL_LASTMOD, -1) == 0) { - // Always in 'Z' format, so the offset argument for the parse method - // can be any value. - lastmod = parseDateTimeString(value, 0, status); - if (U_FAILURE(status)) { - goto cleanupParse; - } - } else if (name.compare(ICAL_BEGIN, -1) == 0) { - UBool isDST = (value.compare(ICAL_DAYLIGHT, -1) == 0); - if (value.compare(ICAL_STANDARD, -1) == 0 || isDST) { - // tzid must be ready at this point - if (tzid.length() == 0) { - goto cleanupParse; - } - // initialize current zone properties - if (dates->size() != 0) { - dates->removeAllElements(); - } - isRRULE = FALSE; - from.remove(); - to.remove(); - zonename.remove(); - dst = isDST; - state = TZI; - } else { - // BEGIN property other than STANDARD/DAYLIGHT - // must not be there. - goto cleanupParse; - } - } else if (name.compare(ICAL_END, -1) == 0) { - break; - } - break; - case TZI: - if (name.compare(ICAL_DTSTART, -1) == 0) { - dtstart = value; - } else if (name.compare(ICAL_TZNAME, -1) == 0) { - zonename = value; - } else if (name.compare(ICAL_TZOFFSETFROM, -1) == 0) { - from = value; - } else if (name.compare(ICAL_TZOFFSETTO, -1) == 0) { - to = value; - } else if (name.compare(ICAL_RDATE, -1) == 0) { - // RDATE mixed with RRULE is not supported - if (isRRULE) { - goto cleanupParse; - } - // RDATE value may contain multiple date delimited - // by comma - UBool nextDate = TRUE; - int32_t dstart = 0; - UnicodeString *dstr; - while (nextDate) { - int32_t dend = value.indexOf(COMMA, dstart); - if (dend == -1) { - dstr = new UnicodeString(value, dstart); - nextDate = FALSE; - } else { - dstr = new UnicodeString(value, dstart, dend - dstart); - } - dates->addElement(dstr, status); - if (U_FAILURE(status)) { - goto cleanupParse; - } - dstart = dend + 1; - } - } else if (name.compare(ICAL_RRULE, -1) == 0) { - // RRULE mixed with RDATE is not supported - if (!isRRULE && dates->size() != 0) { - goto cleanupParse; - } - isRRULE = true; - dates->addElement(new UnicodeString(value), status); - if (U_FAILURE(status)) { - goto cleanupParse; - } - } else if (name.compare(ICAL_END, -1) == 0) { - // Mandatory properties - if (dtstart.length() == 0 || from.length() == 0 || to.length() == 0) { - goto cleanupParse; - } - // if zonename is not available, create one from tzid - if (zonename.length() == 0) { - getDefaultTZName(tzid, dst, zonename); - } - - // create a time zone rule - TimeZoneRule *rule = NULL; - int32_t fromOffset = 0; - int32_t toOffset = 0; - int32_t rawOffset = 0; - int32_t dstSavings = 0; - UDate start = 0; - - // Parse TZOFFSETFROM/TZOFFSETTO - fromOffset = offsetStrToMillis(from, status); - toOffset = offsetStrToMillis(to, status); - if (U_FAILURE(status)) { - goto cleanupParse; - } - - if (dst) { - // If daylight, use the previous offset as rawoffset if positive - if (toOffset - fromOffset > 0) { - rawOffset = fromOffset; - dstSavings = toOffset - fromOffset; - } else { - // This is rare case.. just use 1 hour DST savings - rawOffset = toOffset - DEF_DSTSAVINGS; - dstSavings = DEF_DSTSAVINGS; - } - } else { - rawOffset = toOffset; - dstSavings = 0; - } - - // start time - start = parseDateTimeString(dtstart, fromOffset, status); - if (U_FAILURE(status)) { - goto cleanupParse; - } - - // Create the rule - UDate actualStart = MAX_MILLIS; - if (isRRULE) { - rule = createRuleByRRULE(zonename, rawOffset, dstSavings, start, dates, fromOffset, status); - } else { - rule = createRuleByRDATE(zonename, rawOffset, dstSavings, start, dates, fromOffset, status); - } - if (U_FAILURE(status) || rule == NULL) { - goto cleanupParse; - } else { - UBool startAvail = rule->getFirstStart(fromOffset, 0, actualStart); - if (startAvail && actualStart < firstStart) { - // save from offset information for the earliest rule - firstStart = actualStart; - // If this is STD, assume the time before this transtion - // is DST when the difference is 1 hour. This might not be - // accurate, but VTIMEZONE data does not have such info. - if (dstSavings > 0) { - initialRawOffset = fromOffset; - initialDSTSavings = 0; - } else { - if (fromOffset - toOffset == DEF_DSTSAVINGS) { - initialRawOffset = fromOffset - DEF_DSTSAVINGS; - initialDSTSavings = DEF_DSTSAVINGS; - } else { - initialRawOffset = fromOffset; - initialDSTSavings = 0; - } - } - } - } - rules->addElement(rule, status); - if (U_FAILURE(status)) { - goto cleanupParse; - } - state = VTZ; - } - break; - } - } - // Must have at least one rule - if (rules->size() == 0) { - goto cleanupParse; - } - - // Create a initial rule - getDefaultTZName(tzid, FALSE, zonename); - initialRule = new InitialTimeZoneRule(zonename, - initialRawOffset, initialDSTSavings); - if (initialRule == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - goto cleanupParse; - } - - // Finally, create the RuleBasedTimeZone - rbtz = new RuleBasedTimeZone(tzid, initialRule); - if (rbtz == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - goto cleanupParse; - } - initialRule = NULL; // already adopted by RBTZ, no need to delete - - for (n = 0; n < rules->size(); n++) { - TimeZoneRule *r = (TimeZoneRule*)rules->elementAt(n); - AnnualTimeZoneRule *atzrule = dynamic_cast<AnnualTimeZoneRule *>(r); - if (atzrule != NULL) { - if (atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) { - finalRuleCount++; - finalRuleIdx = n; - } - } - } - if (finalRuleCount > 2) { - // Too many final rules - status = U_ILLEGAL_ARGUMENT_ERROR; - goto cleanupParse; - } - - if (finalRuleCount == 1) { - if (rules->size() == 1) { - // Only one final rule, only governs the initial rule, - // which is already initialized, thus, we do not need to - // add this transition rule - rules->removeAllElements(); - } else { - // Normalize the final rule - AnnualTimeZoneRule *finalRule = (AnnualTimeZoneRule*)rules->elementAt(finalRuleIdx); - int32_t tmpRaw = finalRule->getRawOffset(); - int32_t tmpDST = finalRule->getDSTSavings(); - - // Find the last non-final rule - UDate finalStart, start; - finalRule->getFirstStart(initialRawOffset, initialDSTSavings, finalStart); - start = finalStart; - for (n = 0; n < rules->size(); n++) { - if (finalRuleIdx == n) { - continue; - } - TimeZoneRule *r = (TimeZoneRule*)rules->elementAt(n); - UDate lastStart; - r->getFinalStart(tmpRaw, tmpDST, lastStart); - if (lastStart > start) { - finalRule->getNextStart(lastStart, - r->getRawOffset(), - r->getDSTSavings(), - FALSE, - start); - } - } - - TimeZoneRule *newRule; - UnicodeString tznam; - if (start == finalStart) { - // Transform this into a single transition - newRule = new TimeArrayTimeZoneRule( - finalRule->getName(tznam), - finalRule->getRawOffset(), - finalRule->getDSTSavings(), - &finalStart, - 1, - DateTimeRule::UTC_TIME); - } else { - // Update the end year - int32_t y, m, d, dow, doy, mid; - Grego::timeToFields(start, y, m, d, dow, doy, mid); - newRule = new AnnualTimeZoneRule( - finalRule->getName(tznam), - finalRule->getRawOffset(), - finalRule->getDSTSavings(), - *(finalRule->getRule()), - finalRule->getStartYear(), - y); - } - if (newRule == NULL) { - status = U_MEMORY_ALLOCATION_ERROR; - goto cleanupParse; - } - rules->removeElementAt(finalRuleIdx); - rules->addElement(newRule, status); - if (U_FAILURE(status)) { - delete newRule; - goto cleanupParse; - } - } - } - - while (!rules->isEmpty()) { - TimeZoneRule *tzr = (TimeZoneRule*)rules->orphanElementAt(0); - rbtz->addTransitionRule(tzr, status); - if (U_FAILURE(status)) { - goto cleanupParse; - } - } - rbtz->complete(status); - if (U_FAILURE(status)) { - goto cleanupParse; - } - delete rules; - delete dates; - - tz = rbtz; - setID(tzid); - return; - -cleanupParse: - if (rules != NULL) { - while (!rules->isEmpty()) { - TimeZoneRule *r = (TimeZoneRule*)rules->orphanElementAt(0); - delete r; - } - delete rules; - } - if (dates != NULL) { - delete dates; - } - if (initialRule != NULL) { - delete initialRule; - } - if (rbtz != NULL) { - delete rbtz; - } - return; -} - -void -VTimeZone::write(VTZWriter& writer, UErrorCode& status) const { - if (vtzlines != NULL) { - for (int32_t i = 0; i < vtzlines->size(); i++) { - UnicodeString *line = (UnicodeString*)vtzlines->elementAt(i); - if (line->startsWith(ICAL_TZURL, -1) - && line->charAt(u_strlen(ICAL_TZURL)) == COLON) { - writer.write(ICAL_TZURL); - writer.write(COLON); - writer.write(tzurl); - writer.write(ICAL_NEWLINE); - } else if (line->startsWith(ICAL_LASTMOD, -1) - && line->charAt(u_strlen(ICAL_LASTMOD)) == COLON) { - UnicodeString utcString; - writer.write(ICAL_LASTMOD); - writer.write(COLON); - writer.write(getUTCDateTimeString(lastmod, utcString)); - writer.write(ICAL_NEWLINE); - } else { - writer.write(*line); - writer.write(ICAL_NEWLINE); - } - } - } else { - UVector *customProps = NULL; - if (olsonzid.length() > 0 && icutzver.length() > 0) { - customProps = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status); - if (U_FAILURE(status)) { - return; - } - UnicodeString *icutzprop = new UnicodeString(ICU_TZINFO_PROP); - icutzprop->append(olsonzid); - icutzprop->append((UChar)0x005B/*'['*/); - icutzprop->append(icutzver); - icutzprop->append((UChar)0x005D/*']'*/); - customProps->addElement(icutzprop, status); - if (U_FAILURE(status)) { - delete icutzprop; - delete customProps; - return; - } - } - writeZone(writer, *tz, customProps, status); - delete customProps; - } -} - -void -VTimeZone::write(UDate start, VTZWriter& writer, UErrorCode& status) /*const*/ { - if (U_FAILURE(status)) { - return; - } - InitialTimeZoneRule *initial = NULL; - UVector *transitionRules = NULL; - UVector customProps(uprv_deleteUObject, uhash_compareUnicodeString, status); - UnicodeString tzid; - - // Extract rules applicable to dates after the start time - getTimeZoneRulesAfter(start, initial, transitionRules, status); - if (U_FAILURE(status)) { - return; - } - - // Create a RuleBasedTimeZone with the subset rule - getID(tzid); - RuleBasedTimeZone rbtz(tzid, initial); - if (transitionRules != NULL) { - while (!transitionRules->isEmpty()) { - TimeZoneRule *tr = (TimeZoneRule*)transitionRules->orphanElementAt(0); - rbtz.addTransitionRule(tr, status); - if (U_FAILURE(status)) { - goto cleanupWritePartial; - } - } - delete transitionRules; - transitionRules = NULL; - } - rbtz.complete(status); - if (U_FAILURE(status)) { - goto cleanupWritePartial; - } - - if (olsonzid.length() > 0 && icutzver.length() > 0) { - UnicodeString *icutzprop = new UnicodeString(ICU_TZINFO_PROP); - icutzprop->append(olsonzid); - icutzprop->append((UChar)0x005B/*'['*/); - icutzprop->append(icutzver); - icutzprop->append(ICU_TZINFO_PARTIAL, -1); - appendMillis(start, *icutzprop); - icutzprop->append((UChar)0x005D/*']'*/); - customProps.addElement(icutzprop, status); - if (U_FAILURE(status)) { - delete icutzprop; - goto cleanupWritePartial; - } - } - writeZone(writer, rbtz, &customProps, status); - return; - -cleanupWritePartial: - if (initial != NULL) { - delete initial; - } - if (transitionRules != NULL) { - while (!transitionRules->isEmpty()) { - TimeZoneRule *tr = (TimeZoneRule*)transitionRules->orphanElementAt(0); - delete tr; - } - delete transitionRules; - } -} - -void -VTimeZone::writeSimple(UDate time, VTZWriter& writer, UErrorCode& status) /*const*/ { - if (U_FAILURE(status)) { - return; - } - - UVector customProps(uprv_deleteUObject, uhash_compareUnicodeString, status); - UnicodeString tzid; - - // Extract simple rules - InitialTimeZoneRule *initial = NULL; - AnnualTimeZoneRule *std = NULL, *dst = NULL; - getSimpleRulesNear(time, initial, std, dst, status); - if (U_SUCCESS(status)) { - // Create a RuleBasedTimeZone with the subset rule - getID(tzid); - RuleBasedTimeZone rbtz(tzid, initial); - if (std != NULL && dst != NULL) { - rbtz.addTransitionRule(std, status); - rbtz.addTransitionRule(dst, status); - } - if (U_FAILURE(status)) { - goto cleanupWriteSimple; - } - - if (olsonzid.length() > 0 && icutzver.length() > 0) { - UnicodeString *icutzprop = new UnicodeString(ICU_TZINFO_PROP); - icutzprop->append(olsonzid); - icutzprop->append((UChar)0x005B/*'['*/); - icutzprop->append(icutzver); - icutzprop->append(ICU_TZINFO_SIMPLE, -1); - appendMillis(time, *icutzprop); - icutzprop->append((UChar)0x005D/*']'*/); - customProps.addElement(icutzprop, status); - if (U_FAILURE(status)) { - delete icutzprop; - goto cleanupWriteSimple; - } - } - writeZone(writer, rbtz, &customProps, status); - } - return; - -cleanupWriteSimple: - if (initial != NULL) { - delete initial; - } - if (std != NULL) { - delete std; - } - if (dst != NULL) { - delete dst; - } -} - -void -VTimeZone::writeZone(VTZWriter& w, BasicTimeZone& basictz, - UVector* customProps, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - writeHeaders(w, status); - if (U_FAILURE(status)) { - return; - } - - if (customProps != NULL) { - for (int32_t i = 0; i < customProps->size(); i++) { - UnicodeString *custprop = (UnicodeString*)customProps->elementAt(i); - w.write(*custprop); - w.write(ICAL_NEWLINE); - } - } - - UDate t = MIN_MILLIS; - UnicodeString dstName; - int32_t dstFromOffset = 0; - int32_t dstFromDSTSavings = 0; - int32_t dstToOffset = 0; - int32_t dstStartYear = 0; - int32_t dstMonth = 0; - int32_t dstDayOfWeek = 0; - int32_t dstWeekInMonth = 0; - int32_t dstMillisInDay = 0; - UDate dstStartTime = 0.0; - UDate dstUntilTime = 0.0; - int32_t dstCount = 0; - AnnualTimeZoneRule *finalDstRule = NULL; - - UnicodeString stdName; - int32_t stdFromOffset = 0; - int32_t stdFromDSTSavings = 0; - int32_t stdToOffset = 0; - int32_t stdStartYear = 0; - int32_t stdMonth = 0; - int32_t stdDayOfWeek = 0; - int32_t stdWeekInMonth = 0; - int32_t stdMillisInDay = 0; - UDate stdStartTime = 0.0; - UDate stdUntilTime = 0.0; - int32_t stdCount = 0; - AnnualTimeZoneRule *finalStdRule = NULL; - - int32_t year, month, dom, dow, doy, mid; - UBool hasTransitions = FALSE; - TimeZoneTransition tzt; - UBool tztAvail; - UnicodeString name; - UBool isDst; - - // Going through all transitions - while (TRUE) { - tztAvail = basictz.getNextTransition(t, FALSE, tzt); - if (!tztAvail) { - break; - } - hasTransitions = TRUE; - t = tzt.getTime(); - tzt.getTo()->getName(name); - isDst = (tzt.getTo()->getDSTSavings() != 0); - int32_t fromOffset = tzt.getFrom()->getRawOffset() + tzt.getFrom()->getDSTSavings(); - int32_t fromDSTSavings = tzt.getFrom()->getDSTSavings(); - int32_t toOffset = tzt.getTo()->getRawOffset() + tzt.getTo()->getDSTSavings(); - Grego::timeToFields(tzt.getTime() + fromOffset, year, month, dom, dow, doy, mid); - int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); - UBool sameRule = FALSE; - const AnnualTimeZoneRule *atzrule; - if (isDst) { - if (finalDstRule == NULL - && (atzrule = dynamic_cast<const AnnualTimeZoneRule *>(tzt.getTo())) != NULL - && atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR - ) { - finalDstRule = (AnnualTimeZoneRule*)tzt.getTo()->clone(); - } - if (dstCount > 0) { - if (year == dstStartYear + dstCount - && name.compare(dstName) == 0 - && dstFromOffset == fromOffset - && dstToOffset == toOffset - && dstMonth == month - && dstDayOfWeek == dow - && dstWeekInMonth == weekInMonth - && dstMillisInDay == mid) { - // Update until time - dstUntilTime = t; - dstCount++; - sameRule = TRUE; - } - if (!sameRule) { - if (dstCount == 1) { - writeZonePropsByTime(w, TRUE, dstName, dstFromOffset, dstToOffset, dstStartTime, - TRUE, status); - } else { - writeZonePropsByDOW(w, TRUE, dstName, dstFromOffset, dstToOffset, - dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime, status); - } - if (U_FAILURE(status)) { - goto cleanupWriteZone; - } - } - } - if (!sameRule) { - // Reset this DST information - dstName = name; - dstFromOffset = fromOffset; - dstFromDSTSavings = fromDSTSavings; - dstToOffset = toOffset; - dstStartYear = year; - dstMonth = month; - dstDayOfWeek = dow; - dstWeekInMonth = weekInMonth; - dstMillisInDay = mid; - dstStartTime = dstUntilTime = t; - dstCount = 1; - } - if (finalStdRule != NULL && finalDstRule != NULL) { - break; - } - } else { - if (finalStdRule == NULL - && (atzrule = dynamic_cast<const AnnualTimeZoneRule *>(tzt.getTo())) != NULL - && atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR - ) { - finalStdRule = (AnnualTimeZoneRule*)tzt.getTo()->clone(); - } - if (stdCount > 0) { - if (year == stdStartYear + stdCount - && name.compare(stdName) == 0 - && stdFromOffset == fromOffset - && stdToOffset == toOffset - && stdMonth == month - && stdDayOfWeek == dow - && stdWeekInMonth == weekInMonth - && stdMillisInDay == mid) { - // Update until time - stdUntilTime = t; - stdCount++; - sameRule = TRUE; - } - if (!sameRule) { - if (stdCount == 1) { - writeZonePropsByTime(w, FALSE, stdName, stdFromOffset, stdToOffset, stdStartTime, - TRUE, status); - } else { - writeZonePropsByDOW(w, FALSE, stdName, stdFromOffset, stdToOffset, - stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, stdUntilTime, status); - } - if (U_FAILURE(status)) { - goto cleanupWriteZone; - } - } - } - if (!sameRule) { - // Reset this STD information - stdName = name; - stdFromOffset = fromOffset; - stdFromDSTSavings = fromDSTSavings; - stdToOffset = toOffset; - stdStartYear = year; - stdMonth = month; - stdDayOfWeek = dow; - stdWeekInMonth = weekInMonth; - stdMillisInDay = mid; - stdStartTime = stdUntilTime = t; - stdCount = 1; - } - if (finalStdRule != NULL && finalDstRule != NULL) { - break; - } - } - } - if (!hasTransitions) { - // No transition - put a single non transition RDATE - int32_t raw, dst, offset; - basictz.getOffset(0.0/*any time*/, FALSE, raw, dst, status); - if (U_FAILURE(status)) { - goto cleanupWriteZone; - } - offset = raw + dst; - isDst = (dst != 0); - UnicodeString tzid; - basictz.getID(tzid); - getDefaultTZName(tzid, isDst, name); - writeZonePropsByTime(w, isDst, name, - offset, offset, DEF_TZSTARTTIME - offset, FALSE, status); - if (U_FAILURE(status)) { - goto cleanupWriteZone; - } - } else { - if (dstCount > 0) { - if (finalDstRule == NULL) { - if (dstCount == 1) { - writeZonePropsByTime(w, TRUE, dstName, dstFromOffset, dstToOffset, dstStartTime, - TRUE, status); - } else { - writeZonePropsByDOW(w, TRUE, dstName, dstFromOffset, dstToOffset, - dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime, status); - } - if (U_FAILURE(status)) { - goto cleanupWriteZone; - } - } else { - if (dstCount == 1) { - writeFinalRule(w, TRUE, finalDstRule, - dstFromOffset - dstFromDSTSavings, dstFromDSTSavings, dstStartTime, status); - } else { - // Use a single rule if possible - if (isEquivalentDateRule(dstMonth, dstWeekInMonth, dstDayOfWeek, finalDstRule->getRule())) { - writeZonePropsByDOW(w, TRUE, dstName, dstFromOffset, dstToOffset, - dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, MAX_MILLIS, status); - } else { - // Not equivalent rule - write out two different rules - writeZonePropsByDOW(w, TRUE, dstName, dstFromOffset, dstToOffset, - dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime, status); - if (U_FAILURE(status)) { - goto cleanupWriteZone; - } - writeFinalRule(w, TRUE, finalDstRule, - dstFromOffset - dstFromDSTSavings, dstFromDSTSavings, dstStartTime, status); - } - } - if (U_FAILURE(status)) { - goto cleanupWriteZone; - } - } - } - if (stdCount > 0) { - if (finalStdRule == NULL) { - if (stdCount == 1) { - writeZonePropsByTime(w, FALSE, stdName, stdFromOffset, stdToOffset, stdStartTime, - TRUE, status); - } else { - writeZonePropsByDOW(w, FALSE, stdName, stdFromOffset, stdToOffset, - stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, stdUntilTime, status); - } - if (U_FAILURE(status)) { - goto cleanupWriteZone; - } - } else { - if (stdCount == 1) { - writeFinalRule(w, FALSE, finalStdRule, - stdFromOffset - stdFromDSTSavings, stdFromDSTSavings, stdStartTime, status); - } else { - // Use a single rule if possible - if (isEquivalentDateRule(stdMonth, stdWeekInMonth, stdDayOfWeek, finalStdRule->getRule())) { - writeZonePropsByDOW(w, FALSE, stdName, stdFromOffset, stdToOffset, - stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, MAX_MILLIS, status); - } else { - // Not equivalent rule - write out two different rules - writeZonePropsByDOW(w, FALSE, stdName, stdFromOffset, stdToOffset, - stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, stdUntilTime, status); - if (U_FAILURE(status)) { - goto cleanupWriteZone; - } - writeFinalRule(w, FALSE, finalStdRule, - stdFromOffset - stdFromDSTSavings, stdFromDSTSavings, stdStartTime, status); - } - } - if (U_FAILURE(status)) { - goto cleanupWriteZone; - } - } - } - } - writeFooter(w, status); - -cleanupWriteZone: - - if (finalStdRule != NULL) { - delete finalStdRule; - } - if (finalDstRule != NULL) { - delete finalDstRule; - } -} - -void -VTimeZone::writeHeaders(VTZWriter& writer, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - UnicodeString tzid; - tz->getID(tzid); - - writer.write(ICAL_BEGIN); - writer.write(COLON); - writer.write(ICAL_VTIMEZONE); - writer.write(ICAL_NEWLINE); - writer.write(ICAL_TZID); - writer.write(COLON); - writer.write(tzid); - writer.write(ICAL_NEWLINE); - if (tzurl.length() != 0) { - writer.write(ICAL_TZURL); - writer.write(COLON); - writer.write(tzurl); - writer.write(ICAL_NEWLINE); - } - if (lastmod != MAX_MILLIS) { - UnicodeString lastmodStr; - writer.write(ICAL_LASTMOD); - writer.write(COLON); - writer.write(getUTCDateTimeString(lastmod, lastmodStr)); - writer.write(ICAL_NEWLINE); - } -} - -/* - * Write the closing section of the VTIMEZONE definition block - */ -void -VTimeZone::writeFooter(VTZWriter& writer, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - writer.write(ICAL_END); - writer.write(COLON); - writer.write(ICAL_VTIMEZONE); - writer.write(ICAL_NEWLINE); -} - -/* - * Write a single start time - */ -void -VTimeZone::writeZonePropsByTime(VTZWriter& writer, UBool isDst, const UnicodeString& zonename, - int32_t fromOffset, int32_t toOffset, UDate time, UBool withRDATE, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, time, status); - if (U_FAILURE(status)) { - return; - } - if (withRDATE) { - writer.write(ICAL_RDATE); - writer.write(COLON); - UnicodeString timestr; - writer.write(getDateTimeString(time + fromOffset, timestr)); - writer.write(ICAL_NEWLINE); - } - endZoneProps(writer, isDst, status); - if (U_FAILURE(status)) { - return; - } -} - -/* - * Write start times defined by a DOM rule using VTIMEZONE RRULE - */ -void -VTimeZone::writeZonePropsByDOM(VTZWriter& writer, UBool isDst, const UnicodeString& zonename, - int32_t fromOffset, int32_t toOffset, - int32_t month, int32_t dayOfMonth, UDate startTime, UDate untilTime, - UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, startTime, status); - if (U_FAILURE(status)) { - return; - } - beginRRULE(writer, month, status); - if (U_FAILURE(status)) { - return; - } - writer.write(ICAL_BYMONTHDAY); - writer.write(EQUALS_SIGN); - UnicodeString dstr; - appendAsciiDigits(dayOfMonth, 0, dstr); - writer.write(dstr); - if (untilTime != MAX_MILLIS) { - appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr), status); - if (U_FAILURE(status)) { - return; - } - } - writer.write(ICAL_NEWLINE); - endZoneProps(writer, isDst, status); -} - -/* - * Write start times defined by a DOW rule using VTIMEZONE RRULE - */ -void -VTimeZone::writeZonePropsByDOW(VTZWriter& writer, UBool isDst, const UnicodeString& zonename, - int32_t fromOffset, int32_t toOffset, - int32_t month, int32_t weekInMonth, int32_t dayOfWeek, - UDate startTime, UDate untilTime, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, startTime, status); - if (U_FAILURE(status)) { - return; - } - beginRRULE(writer, month, status); - if (U_FAILURE(status)) { - return; - } - writer.write(ICAL_BYDAY); - writer.write(EQUALS_SIGN); - UnicodeString dstr; - appendAsciiDigits(weekInMonth, 0, dstr); - writer.write(dstr); // -4, -3, -2, -1, 1, 2, 3, 4 - writer.write(ICAL_DOW_NAMES[dayOfWeek - 1]); // SU, MO, TU... - - if (untilTime != MAX_MILLIS) { - appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr), status); - if (U_FAILURE(status)) { - return; - } - } - writer.write(ICAL_NEWLINE); - endZoneProps(writer, isDst, status); -} - -/* - * Write start times defined by a DOW_GEQ_DOM rule using VTIMEZONE RRULE - */ -void -VTimeZone::writeZonePropsByDOW_GEQ_DOM(VTZWriter& writer, UBool isDst, const UnicodeString& zonename, - int32_t fromOffset, int32_t toOffset, - int32_t month, int32_t dayOfMonth, int32_t dayOfWeek, - UDate startTime, UDate untilTime, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - // Check if this rule can be converted to DOW rule - if (dayOfMonth%7 == 1) { - // Can be represented by DOW rule - writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset, - month, (dayOfMonth + 6)/7, dayOfWeek, startTime, untilTime, status); - if (U_FAILURE(status)) { - return; - } - } else if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - dayOfMonth)%7 == 6) { - // Can be represented by DOW rule with negative week number - writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset, - month, -1*((MONTHLENGTH[month] - dayOfMonth + 1)/7), dayOfWeek, startTime, untilTime, status); - if (U_FAILURE(status)) { - return; - } - } else { - // Otherwise, use BYMONTHDAY to include all possible dates - beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, startTime, status); - if (U_FAILURE(status)) { - return; - } - // Check if all days are in the same month - int32_t startDay = dayOfMonth; - int32_t currentMonthDays = 7; - - if (dayOfMonth <= 0) { - // The start day is in previous month - int32_t prevMonthDays = 1 - dayOfMonth; - currentMonthDays -= prevMonthDays; - - int32_t prevMonth = (month - 1) < 0 ? 11 : month - 1; - - // Note: When a rule is separated into two, UNTIL attribute needs to be - // calculated for each of them. For now, we skip this, because we basically use this method - // only for final rules, which does not have the UNTIL attribute - writeZonePropsByDOW_GEQ_DOM_sub(writer, prevMonth, -prevMonthDays, dayOfWeek, prevMonthDays, - MAX_MILLIS /* Do not use UNTIL */, fromOffset, status); - if (U_FAILURE(status)) { - return; - } - - // Start from 1 for the rest - startDay = 1; - } else if (dayOfMonth + 6 > MONTHLENGTH[month]) { - // Note: This code does not actually work well in February. For now, days in month in - // non-leap year. - int32_t nextMonthDays = dayOfMonth + 6 - MONTHLENGTH[month]; - currentMonthDays -= nextMonthDays; - - int32_t nextMonth = (month + 1) > 11 ? 0 : month + 1; - - writeZonePropsByDOW_GEQ_DOM_sub(writer, nextMonth, 1, dayOfWeek, nextMonthDays, - MAX_MILLIS /* Do not use UNTIL */, fromOffset, status); - if (U_FAILURE(status)) { - return; - } - } - writeZonePropsByDOW_GEQ_DOM_sub(writer, month, startDay, dayOfWeek, currentMonthDays, - untilTime, fromOffset, status); - if (U_FAILURE(status)) { - return; - } - endZoneProps(writer, isDst, status); - } -} - -/* - * Called from writeZonePropsByDOW_GEQ_DOM - */ -void -VTimeZone::writeZonePropsByDOW_GEQ_DOM_sub(VTZWriter& writer, int32_t month, int32_t dayOfMonth, - int32_t dayOfWeek, int32_t numDays, - UDate untilTime, int32_t fromOffset, UErrorCode& status) const { - - if (U_FAILURE(status)) { - return; - } - int32_t startDayNum = dayOfMonth; - UBool isFeb = (month == UCAL_FEBRUARY); - if (dayOfMonth < 0 && !isFeb) { - // Use positive number if possible - startDayNum = MONTHLENGTH[month] + dayOfMonth + 1; - } - beginRRULE(writer, month, status); - if (U_FAILURE(status)) { - return; - } - writer.write(ICAL_BYDAY); - writer.write(EQUALS_SIGN); - writer.write(ICAL_DOW_NAMES[dayOfWeek - 1]); // SU, MO, TU... - writer.write(SEMICOLON); - writer.write(ICAL_BYMONTHDAY); - writer.write(EQUALS_SIGN); - - UnicodeString dstr; - appendAsciiDigits(startDayNum, 0, dstr); - writer.write(dstr); - for (int32_t i = 1; i < numDays; i++) { - writer.write(COMMA); - dstr.remove(); - appendAsciiDigits(startDayNum + i, 0, dstr); - writer.write(dstr); - } - - if (untilTime != MAX_MILLIS) { - appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr), status); - if (U_FAILURE(status)) { - return; - } - } - writer.write(ICAL_NEWLINE); -} - -/* - * Write start times defined by a DOW_LEQ_DOM rule using VTIMEZONE RRULE - */ -void -VTimeZone::writeZonePropsByDOW_LEQ_DOM(VTZWriter& writer, UBool isDst, const UnicodeString& zonename, - int32_t fromOffset, int32_t toOffset, - int32_t month, int32_t dayOfMonth, int32_t dayOfWeek, - UDate startTime, UDate untilTime, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - // Check if this rule can be converted to DOW rule - if (dayOfMonth%7 == 0) { - // Can be represented by DOW rule - writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset, - month, dayOfMonth/7, dayOfWeek, startTime, untilTime, status); - } else if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - dayOfMonth)%7 == 0){ - // Can be represented by DOW rule with negative week number - writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset, - month, -1*((MONTHLENGTH[month] - dayOfMonth)/7 + 1), dayOfWeek, startTime, untilTime, status); - } else if (month == UCAL_FEBRUARY && dayOfMonth == 29) { - // Specical case for February - writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset, - UCAL_FEBRUARY, -1, dayOfWeek, startTime, untilTime, status); - } else { - // Otherwise, convert this to DOW_GEQ_DOM rule - writeZonePropsByDOW_GEQ_DOM(writer, isDst, zonename, fromOffset, toOffset, - month, dayOfMonth - 6, dayOfWeek, startTime, untilTime, status); - } -} - -/* - * Write the final time zone rule using RRULE, with no UNTIL attribute - */ -void -VTimeZone::writeFinalRule(VTZWriter& writer, UBool isDst, const AnnualTimeZoneRule* rule, - int32_t fromRawOffset, int32_t fromDSTSavings, - UDate startTime, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - UBool modifiedRule = TRUE; - const DateTimeRule *dtrule = toWallTimeRule(rule->getRule(), fromRawOffset, fromDSTSavings); - if (dtrule == NULL) { - modifiedRule = FALSE; - dtrule = rule->getRule(); - } - - // If the rule's mills in a day is out of range, adjust start time. - // Olson tzdata supports 24:00 of a day, but VTIMEZONE does not. - // See ticket#7008/#7518 - - int32_t timeInDay = dtrule->getRuleMillisInDay(); - if (timeInDay < 0) { - startTime = startTime + (0 - timeInDay); - } else if (timeInDay >= U_MILLIS_PER_DAY) { - startTime = startTime - (timeInDay - (U_MILLIS_PER_DAY - 1)); - } - - int32_t toOffset = rule->getRawOffset() + rule->getDSTSavings(); - UnicodeString name; - rule->getName(name); - switch (dtrule->getDateRuleType()) { - case DateTimeRule::DOM: - writeZonePropsByDOM(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset, - dtrule->getRuleMonth(), dtrule->getRuleDayOfMonth(), startTime, MAX_MILLIS, status); - break; - case DateTimeRule::DOW: - writeZonePropsByDOW(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset, - dtrule->getRuleMonth(), dtrule->getRuleWeekInMonth(), dtrule->getRuleDayOfWeek(), startTime, MAX_MILLIS, status); - break; - case DateTimeRule::DOW_GEQ_DOM: - writeZonePropsByDOW_GEQ_DOM(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset, - dtrule->getRuleMonth(), dtrule->getRuleDayOfMonth(), dtrule->getRuleDayOfWeek(), startTime, MAX_MILLIS, status); - break; - case DateTimeRule::DOW_LEQ_DOM: - writeZonePropsByDOW_LEQ_DOM(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset, - dtrule->getRuleMonth(), dtrule->getRuleDayOfMonth(), dtrule->getRuleDayOfWeek(), startTime, MAX_MILLIS, status); - break; - } - if (modifiedRule) { - delete dtrule; - } -} - -/* - * Write the opening section of zone properties - */ -void -VTimeZone::beginZoneProps(VTZWriter& writer, UBool isDst, const UnicodeString& zonename, - int32_t fromOffset, int32_t toOffset, UDate startTime, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - writer.write(ICAL_BEGIN); - writer.write(COLON); - if (isDst) { - writer.write(ICAL_DAYLIGHT); - } else { - writer.write(ICAL_STANDARD); - } - writer.write(ICAL_NEWLINE); - - UnicodeString dstr; - - // TZOFFSETTO - writer.write(ICAL_TZOFFSETTO); - writer.write(COLON); - millisToOffset(toOffset, dstr); - writer.write(dstr); - writer.write(ICAL_NEWLINE); - - // TZOFFSETFROM - writer.write(ICAL_TZOFFSETFROM); - writer.write(COLON); - millisToOffset(fromOffset, dstr); - writer.write(dstr); - writer.write(ICAL_NEWLINE); - - // TZNAME - writer.write(ICAL_TZNAME); - writer.write(COLON); - writer.write(zonename); - writer.write(ICAL_NEWLINE); - - // DTSTART - writer.write(ICAL_DTSTART); - writer.write(COLON); - writer.write(getDateTimeString(startTime + fromOffset, dstr)); - writer.write(ICAL_NEWLINE); -} - -/* - * Writes the closing section of zone properties - */ -void -VTimeZone::endZoneProps(VTZWriter& writer, UBool isDst, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - // END:STANDARD or END:DAYLIGHT - writer.write(ICAL_END); - writer.write(COLON); - if (isDst) { - writer.write(ICAL_DAYLIGHT); - } else { - writer.write(ICAL_STANDARD); - } - writer.write(ICAL_NEWLINE); -} - -/* - * Write the beggining part of RRULE line - */ -void -VTimeZone::beginRRULE(VTZWriter& writer, int32_t month, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - UnicodeString dstr; - writer.write(ICAL_RRULE); - writer.write(COLON); - writer.write(ICAL_FREQ); - writer.write(EQUALS_SIGN); - writer.write(ICAL_YEARLY); - writer.write(SEMICOLON); - writer.write(ICAL_BYMONTH); - writer.write(EQUALS_SIGN); - appendAsciiDigits(month + 1, 0, dstr); - writer.write(dstr); - writer.write(SEMICOLON); -} - -/* - * Append the UNTIL attribute after RRULE line - */ -void -VTimeZone::appendUNTIL(VTZWriter& writer, const UnicodeString& until, UErrorCode& status) const { - if (U_FAILURE(status)) { - return; - } - if (until.length() > 0) { - writer.write(SEMICOLON); - writer.write(ICAL_UNTIL); - writer.write(EQUALS_SIGN); - writer.write(until); - } -} - -U_NAMESPACE_END - -#endif /* #if !UCONFIG_NO_FORMATTING */ - -//eof |