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;
/* * Convert date/time to RFC2445 Date-Time form #2 DATE WITH UTC TIME
*/ static UnicodeString& getUTCDateTimeString(UDate time, UnicodeString& str, UErrorCode& status) {
getDateTimeString(time, str, status);
str.append(static_cast<char16_t>(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) { // character "T" must be used for separating date and time break;
} if (length == 16) { if (str.charAt(15) != 0x005A) { // invalid format break;
}
isUTC = true;
}
// 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;
}
/* * 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 available days in dom (domCount is specifying the size of dom on input) * until time defined by UNTIL attribute or MIN_MILLIS if not available
*/ staticvoid 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;
if (attr.compare(ICAL_FREQ, -1) == 0) { // only support YEARLY frequency type if (value.compare(ICAL_YEARLY, -1) == 0) {
yearly = true;
} else { goto rruleParseError;
}
} elseif (attr.compare(ICAL_UNTIL, -1) == 0) { // ISO8601 UTC format, for example, "20060315T020000Z"
until = parseDateTimeString(value, 0, status); if (U_FAILURE(status)) { goto rruleParseError;
}
} elseif (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;
}
} elseif (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;
} elseif (value.charAt(0) == MINUS) {
sign = -1;
} elseif (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;
}
} elseif (attr.compare(ICAL_BYMONTHDAY, -1) == 0) { // Note: BYMONTHDAY may contain multiple days delimited 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 nullptr;
} if (dates == nullptr || dates->size() == 0) {
status = U_ILLEGAL_ARGUMENT_ERROR; return nullptr;
}
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 = *static_cast<UnicodeString*>(dates->elementAt(i));
UDate tmp_until;
int32_t tmp_month, tmp_dayOfWeek, tmp_nthDayOfWeek;
int32_t tmp_days[7];
int32_t tmp_daysCount = UPRV_LENGTHOF(tmp_days);
parseRRULE(rrule, tmp_month, tmp_dayOfWeek, tmp_nthDayOfWeek, tmp_days, tmp_daysCount, tmp_until, status); if (U_FAILURE(status)) { return nullptr;
} // 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;
} elseif (diff == 11 || diff == 1) { // Next month
anotherMonth = tmp_month;
} else { // The day range cannot exceed more than 2 months goto unsupportedRRule;
}
} elseif (tmp_month != month && tmp_month != anotherMonth) { // The day range cannot exceed more than 2 months goto unsupportedRRule;
}
} // If earlier 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, status); if (U_FAILURE(status)) { return nullptr;
} 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;
}
// 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);
} elseif (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);
} elseif (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 == nullptr) { goto unsupportedRRule;
} returnnew AnnualTimeZoneRule(zonename, rawOffset, dstSavings, adtr, startYear, endYear);
unsupportedRRule:
status = U_INVALID_STATE_ERROR; return nullptr;
}
/* * 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 nullptr;
}
TimeArrayTimeZoneRule *retVal = nullptr; if (dates == nullptr || 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 = static_cast<UDate*>(uprv_malloc(sizeof(UDate) * size)); if (times == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return nullptr;
} for (int32_t i = 0; i < size; i++) {
UnicodeString* datestr = static_cast<UnicodeString*>(dates->elementAt(i));
times[i] = parseDateTimeString(*datestr, fromOffset, status); if (U_FAILURE(status)) {
uprv_free(times); return nullptr;
}
}
retVal = new TimeArrayTimeZoneRule(zonename, rawOffset, dstSavings, times, size, DateTimeRule::UTC_TIME);
uprv_free(times);
} if (retVal == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
} 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()) { returnfalse;
} if (dtrule->getTimeRuleType() != DateTimeRule::WALL_TIME) { // Do not try to do more intelligent comparison for now. returnfalse;
} if (dtrule->getDateRuleType() == DateTimeRule::DOW
&& dtrule->getRuleWeekInMonth() == weekInMonth) { returntrue;
}
int32_t ruleDOM = dtrule->getRuleDayOfMonth(); if (dtrule->getDateRuleType() == DateTimeRule::DOW_GEQ_DOM) { if (ruleDOM%7 == 1 && (ruleDOM + 6)/7 == weekInMonth) { returntrue;
} if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - ruleDOM)%7 == 6
&& weekInMonth == -1*((MONTHLENGTH[month]-ruleDOM+1)/7)) { returntrue;
}
} if (dtrule->getDateRuleType() == DateTimeRule::DOW_LEQ_DOM) { if (ruleDOM%7 == 0 && ruleDOM/7 == weekInMonth) { returntrue;
} if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - ruleDOM)%7 == 0
&& weekInMonth == -1*((MONTHLENGTH[month] - ruleDOM)/7 + 1)) { returntrue;
}
} returnfalse;
}
/* * Convert the rule to its equivalent rule using WALL_TIME mode. * This function returns nullptr when the specified DateTimeRule is already * using WALL_TIME mode.
*/ static DateTimeRule *toWallTimeRule(const DateTimeRule *rule, int32_t rawOffset, int32_t dstSavings, UErrorCode &status) { if (U_FAILURE(status)) { return nullptr;
} if (rule->getTimeRuleType() == DateTimeRule::WALL_TIME) { return nullptr;
}
int32_t wallt = rule->getRuleMillisInDay(); if (rule->getTimeRuleType() == DateTimeRule::UTC_TIME) {
wallt += (rawOffset + dstSavings);
} elseif (rule->getTimeRuleType() == DateTimeRule::STANDARD_TIME) {
wallt += dstSavings;
}
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];
} elseif (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;
} elseif (dow > UCAL_SATURDAY) {
dow = UCAL_SUNDAY;
}
}
} // Create a new rule
DateTimeRule *modifiedRule = nullptr; 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);
} if (modifiedRule == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR;
} return modifiedRule;
}
/* * Minimum 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
VTimeZone::parse(UErrorCode& status) { if (U_FAILURE(status)) { return;
} if (vtzlines == nullptr || vtzlines->size() == 0) {
status = U_INVALID_STATE_ERROR; return;
}
// 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
// Set the deleter on rules to remove TimeZoneRule vectors to avoid memory leaks due to unowned TimeZoneRules.
UVector rules(uprv_deleteUObject, nullptr, status);
// list of RDATE or RRULE strings
UVector dates(uprv_deleteUObject, uhash_compareUnicodeString, status); if (U_FAILURE(status)) { return;
}
for (n = 0; n < vtzlines->size(); n++) {
UnicodeString* line = static_cast<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;
} elseif (name.compare(ICAL_TZURL, -1) == 0) {
tzurl = value;
} elseif (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)) { return;
}
} elseif (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) { return;
} // 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. return;
}
} elseif (name.compare(ICAL_END, -1) == 0) { break;
} break; case TZI: if (name.compare(ICAL_DTSTART, -1) == 0) {
dtstart = value;
} elseif (name.compare(ICAL_TZNAME, -1) == 0) {
zonename = value;
} elseif (name.compare(ICAL_TZOFFSETFROM, -1) == 0) {
from = value;
} elseif (name.compare(ICAL_TZOFFSETTO, -1) == 0) {
to = value;
} elseif (name.compare(ICAL_RDATE, -1) == 0) { // RDATE mixed with RRULE is not supported if (isRRULE) { return;
} // RDATE value may contain multiple date delimited // by comma
UBool nextDate = true;
int32_t dstart = 0;
LocalPointer<UnicodeString> dstr; while (nextDate) {
int32_t dend = value.indexOf(COMMA, dstart); if (dend == -1) {
dstr.adoptInsteadAndCheckErrorCode(new UnicodeString(value, dstart), status);
nextDate = false;
} else {
dstr.adoptInsteadAndCheckErrorCode(new UnicodeString(value, dstart, dend - dstart), status);
}
dates.adoptElement(dstr.orphan(), status); if (U_FAILURE(status)) { return;
}
dstart = dend + 1;
}
} elseif (name.compare(ICAL_RRULE, -1) == 0) { // RRULE mixed with RDATE is not supported if (!isRRULE && dates.size() != 0) { return;
}
isRRULE = true;
LocalPointer<UnicodeString> element(new UnicodeString(value), status);
dates.adoptElement(element.orphan(), status); if (U_FAILURE(status)) { return;
}
} elseif (name.compare(ICAL_END, -1) == 0) { // Mandatory properties if (dtstart.length() == 0 || from.length() == 0 || to.length() == 0) { return;
} // if zonename is not available, create one from tzid if (zonename.length() == 0) {
getDefaultTZName(tzid, dst, zonename);
}
// create a time zone rule
LocalPointer<TimeZoneRule> rule;
int32_t fromOffset = 0;
int32_t toOffset = 0;
int32_t rawOffset = 0;
int32_t dstSavings = 0;
UDate start = 0;
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)) { return;
}
// Create the rule
UDate actualStart = MAX_MILLIS; if (isRRULE) {
rule.adoptInsteadAndCheckErrorCode(
createRuleByRRULE(zonename, rawOffset, dstSavings, start, &dates, fromOffset, status), status);
} else {
rule.adoptInsteadAndCheckErrorCode(
createRuleByRDATE(zonename, rawOffset, dstSavings, start, &dates, fromOffset, status), status);
} if (U_FAILURE(status)) { return;
} 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 transition // 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.adoptElement(rule.orphan(), status); if (U_FAILURE(status)) { return;
}
state = VTZ;
} break;
}
} // Must have at least one rule if (rules.size() == 0) { return;
}
// Create a initial rule
getDefaultTZName(tzid, false, zonename);
LocalPointer<InitialTimeZoneRule> initialRule( new InitialTimeZoneRule(zonename, initialRawOffset, initialDSTSavings), status); if (U_FAILURE(status)) { return;
}
// Finally, create the RuleBasedTimeZone // C++ awkwardness on memory allocation failure: the constructor wont be run, meaning // that initialRule wont be adopted/deleted, as it normally would be.
LocalPointer<RuleBasedTimeZone> rbtz( new RuleBasedTimeZone(tzid, initialRule.getAlias()), status); if (U_SUCCESS(status)) {
initialRule.orphan();
} else { return;
}
for (n = 0; n < rules.size(); n++) {
TimeZoneRule* r = static_cast<TimeZoneRule*>(rules.elementAt(n));
AnnualTimeZoneRule *atzrule = dynamic_cast<AnnualTimeZoneRule *>(r); if (atzrule != nullptr) { if (atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) {
finalRuleCount++;
finalRuleIdx = n;
}
}
} if (finalRuleCount > 2) { // Too many final rules
status = U_ILLEGAL_ARGUMENT_ERROR; return;
}
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 = static_cast<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 = static_cast<TimeZoneRule*>(rules.elementAt(n));
UDate lastStart;
r->getFinalStart(tmpRaw, tmpDST, lastStart); if (lastStart > start) {
finalRule->getNextStart(lastStart,
r->getRawOffset(),
r->getDSTSavings(), false,
start);
}
}
LocalPointer<TimeZoneRule> newRule;
UnicodeString tznam; if (start == finalStart) { // Transform this into a single transition
newRule.adoptInsteadAndCheckErrorCode( new TimeArrayTimeZoneRule(
finalRule->getName(tznam),
finalRule->getRawOffset(),
finalRule->getDSTSavings(),
&finalStart,
1,
DateTimeRule::UTC_TIME),
status);
} else { // Update the end year
int32_t y, m, d, dow, doy, mid;
Grego::timeToFields(start, y, m, d, dow, doy, mid, status); if (U_FAILURE(status)) return;
newRule.adoptInsteadAndCheckErrorCode( new AnnualTimeZoneRule(
finalRule->getName(tznam),
finalRule->getRawOffset(),
finalRule->getDSTSavings(),
*(finalRule->getRule()),
finalRule->getStartYear(),
y),
status);
} if (U_FAILURE(status)) { return;
}
rules.removeElementAt(finalRuleIdx);
rules.adoptElement(newRule.orphan(), status); if (U_FAILURE(status)) { return;
}
}
}
while (!rules.isEmpty()) {
TimeZoneRule* tzr = static_cast<TimeZoneRule*>(rules.orphanElementAt(0));
rbtz->addTransitionRule(tzr, status); if (U_FAILURE(status)) { return;
}
}
rbtz->complete(status); if (U_FAILURE(status)) { return;
}
// Extract rules applicable to dates after the start time
getTimeZoneRulesAfter(start, initial, transitionRules, status);
LocalPointer<InitialTimeZoneRule> lpInitial(initial);
LocalPointer<UVector> lpTransitionRules(transitionRules); if (U_FAILURE(status)) { return;
}
// Create a RuleBasedTimeZone with the subset rule
getID(tzid);
RuleBasedTimeZone rbtz(tzid, lpInitial.orphan()); if (lpTransitionRules.isValid()) {
U_ASSERT(transitionRules->hasDeleter()); // Assumed for U_FAILURE early return, below. while (!lpTransitionRules->isEmpty()) {
TimeZoneRule* tr = static_cast<TimeZoneRule*>(lpTransitionRules->orphanElementAt(0));
rbtz.addTransitionRule(tr, status); if (U_FAILURE(status)) { return;
}
}
}
rbtz.complete(status); if (U_FAILURE(status)) { return;
}
if (olsonzid.length() > 0 && icutzver.length() > 0) {
UnicodeString *icutzprop = new UnicodeString(ICU_TZINFO_PROP); if (icutzprop == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return;
}
icutzprop->append(olsonzid);
icutzprop->append(static_cast<char16_t>(0x005B)/*'['*/);
icutzprop->append(icutzver);
icutzprop->append(ICU_TZINFO_PARTIAL, -1);
appendMillis(start, *icutzprop);
icutzprop->append(static_cast<char16_t>(0x005D)/*']'*/);
customProps.adoptElement(icutzprop, status); if (U_FAILURE(status)) { return;
}
}
writeZone(writer, rbtz, &customProps, status);
}
void
VTimeZone::writeSimple(UDate time, VTZWriter& writer, UErrorCode& status) const { if (U_FAILURE(status)) { return;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.