U_CAPI UBool U_EXPORT2 ucal_getTimeZoneTransitionDate(const UCalendar* cal, UTimeZoneTransitionType type, UDate* transition, UErrorCode* status) { if (U_FAILURE(*status)) { return FALSE; } UDate base = ((Calendar*)cal)->getTime(*status); const TimeZone& tz = ((Calendar*)cal)->getTimeZone(); const BasicTimeZone * btz = dynamic_cast<const BasicTimeZone *>(&tz); if (btz != NULL && U_SUCCESS(*status)) { TimeZoneTransition tzt; UBool inclusive = (type == UCAL_TZ_TRANSITION_NEXT_INCLUSIVE || type == UCAL_TZ_TRANSITION_PREVIOUS_INCLUSIVE); UBool result = (type == UCAL_TZ_TRANSITION_NEXT || type == UCAL_TZ_TRANSITION_NEXT_INCLUSIVE)? btz->getNextTransition(base, inclusive, tzt): btz->getPreviousTransition(base, inclusive, tzt); if (result) { *transition = tzt.getTime(); return TRUE; } } return FALSE; }
void BasicTimeZone::getTimeZoneRulesAfter(UDate start, InitialTimeZoneRule*& initial, UVector*& transitionRules, UErrorCode& status) /*const*/ { if (U_FAILURE(status)) { return; } const InitialTimeZoneRule *orgini; const TimeZoneRule **orgtrs = NULL; TimeZoneTransition tzt; UBool avail; UVector *orgRules = NULL; int32_t ruleCount; TimeZoneRule *r = NULL; UBool *done = NULL; InitialTimeZoneRule *res_initial = NULL; UVector *filteredRules = NULL; UnicodeString name; int32_t i; UDate time, t; UDate *newTimes = NULL; UDate firstStart; UBool bFinalStd = FALSE, bFinalDst = FALSE; // Original transition rules ruleCount = countTransitionRules(status); if (U_FAILURE(status)) { return; } orgRules = new UVector(ruleCount, status); if (U_FAILURE(status)) { return; } orgtrs = (const TimeZoneRule**)uprv_malloc(sizeof(TimeZoneRule*)*ruleCount); if (orgtrs == NULL) { status = U_MEMORY_ALLOCATION_ERROR; goto error; } getTimeZoneRules(orgini, orgtrs, ruleCount, status); if (U_FAILURE(status)) { goto error; } for (i = 0; i < ruleCount; i++) { orgRules->addElement(orgtrs[i]->clone(), status); if (U_FAILURE(status)) { goto error; } } uprv_free(orgtrs); orgtrs = NULL; avail = getPreviousTransition(start, TRUE, tzt); if (!avail) { // No need to filter out rules only applicable to time before the start initial = orgini->clone(); transitionRules = orgRules; return; } done = (UBool*)uprv_malloc(sizeof(UBool)*ruleCount); if (done == NULL) { status = U_MEMORY_ALLOCATION_ERROR; goto error; } filteredRules = new UVector(status); if (U_FAILURE(status)) { goto error; } // Create initial rule tzt.getTo()->getName(name); res_initial = new InitialTimeZoneRule(name, tzt.getTo()->getRawOffset(), tzt.getTo()->getDSTSavings()); // Mark rules which does not need to be processed for (i = 0; i < ruleCount; i++) { r = (TimeZoneRule*)orgRules->elementAt(i); avail = r->getNextStart(start, res_initial->getRawOffset(), res_initial->getDSTSavings(), FALSE, time); done[i] = !avail; } time = start; while (!bFinalStd || !bFinalDst) { avail = getNextTransition(time, FALSE, tzt); if (!avail) { break; } UDate updatedTime = tzt.getTime(); if (updatedTime == time) { // Can get here if rules for start & end of daylight time have exactly // the same time. // TODO: fix getNextTransition() to prevent it? status = U_INVALID_STATE_ERROR; goto error; } time = updatedTime; const TimeZoneRule *toRule = tzt.getTo(); for (i = 0; i < ruleCount; i++) { r = (TimeZoneRule*)orgRules->elementAt(i); if (*r == *toRule) { break; } } if (i >= ruleCount) { // This case should never happen status = U_INVALID_STATE_ERROR; goto error; } if (done[i]) { continue; } const TimeArrayTimeZoneRule *tar = dynamic_cast<const TimeArrayTimeZoneRule *>(toRule); const AnnualTimeZoneRule *ar; if (tar != NULL) { // Get the previous raw offset and DST savings before the very first start time TimeZoneTransition tzt0; t = start; while (TRUE) { avail = getNextTransition(t, FALSE, tzt0); if (!avail) { break; } if (*(tzt0.getTo()) == *tar) { break; } t = tzt0.getTime(); } if (avail) { // Check if the entire start times to be added tar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart); if (firstStart > start) { // Just add the rule as is filteredRules->addElement(tar->clone(), status); if (U_FAILURE(status)) { goto error; } } else { // Colllect transitions after the start time int32_t startTimes; DateTimeRule::TimeRuleType timeType; int32_t idx; startTimes = tar->countStartTimes(); timeType = tar->getTimeType(); for (idx = 0; idx < startTimes; idx++) { tar->getStartTimeAt(idx, t); if (timeType == DateTimeRule::STANDARD_TIME) { t -= tzt.getFrom()->getRawOffset(); } if (timeType == DateTimeRule::WALL_TIME) { t -= tzt.getFrom()->getDSTSavings(); } if (t > start) { break; } } int32_t asize = startTimes - idx; if (asize > 0) { newTimes = (UDate*)uprv_malloc(sizeof(UDate) * asize); if (newTimes == NULL) { status = U_MEMORY_ALLOCATION_ERROR; goto error; } for (int32_t newidx = 0; newidx < asize; newidx++) { tar->getStartTimeAt(idx + newidx, newTimes[newidx]); if (U_FAILURE(status)) { uprv_free(newTimes); newTimes = NULL; goto error; } } tar->getName(name); TimeArrayTimeZoneRule *newTar = new TimeArrayTimeZoneRule(name, tar->getRawOffset(), tar->getDSTSavings(), newTimes, asize, timeType); uprv_free(newTimes); filteredRules->addElement(newTar, status); if (U_FAILURE(status)) { goto error; } } } } } else if ((ar = dynamic_cast<const AnnualTimeZoneRule *>(toRule)) != NULL) { ar->getFirstStart(tzt.getFrom()->getRawOffset(), tzt.getFrom()->getDSTSavings(), firstStart); if (firstStart == tzt.getTime()) { // Just add the rule as is filteredRules->addElement(ar->clone(), status); if (U_FAILURE(status)) { goto error; } } else { // Calculate the transition year int32_t year, month, dom, dow, doy, mid; Grego::timeToFields(tzt.getTime(), year, month, dom, dow, doy, mid); // Re-create the rule ar->getName(name); AnnualTimeZoneRule *newAr = new AnnualTimeZoneRule(name, ar->getRawOffset(), ar->getDSTSavings(), *(ar->getRule()), year, ar->getEndYear()); filteredRules->addElement(newAr, status); if (U_FAILURE(status)) { goto error; } } // check if this is a final rule if (ar->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) { // After bot final standard and dst rules are processed, // exit this while loop. if (ar->getDSTSavings() == 0) { bFinalStd = TRUE; } else { bFinalDst = TRUE; } } } done[i] = TRUE; } // Set the results if (orgRules != NULL) { while (!orgRules->isEmpty()) { r = (TimeZoneRule*)orgRules->orphanElementAt(0); delete r; } delete orgRules; } if (done != NULL) { uprv_free(done); } initial = res_initial; transitionRules = filteredRules; return; error: if (orgtrs != NULL) { uprv_free(orgtrs); } if (orgRules != NULL) { while (!orgRules->isEmpty()) { r = (TimeZoneRule*)orgRules->orphanElementAt(0); delete r; } delete orgRules; } if (done != NULL) { if (filteredRules != NULL) { while (!filteredRules->isEmpty()) { r = (TimeZoneRule*)filteredRules->orphanElementAt(0); delete r; } delete filteredRules; } delete res_initial; uprv_free(done); } initial = NULL; transitionRules = NULL; }
void BasicTimeZone::getSimpleRulesNear(UDate date, InitialTimeZoneRule*& initial, AnnualTimeZoneRule*& std, AnnualTimeZoneRule*& dst, UErrorCode& status) /*const*/ { initial = NULL; std = NULL; dst = NULL; if (U_FAILURE(status)) { return; } int32_t initialRaw, initialDst; UnicodeString initialName; AnnualTimeZoneRule *ar1 = NULL; AnnualTimeZoneRule *ar2 = NULL; UnicodeString name; UBool avail; TimeZoneTransition tr; // Get the next transition avail = getNextTransition(date, FALSE, tr); if (avail) { tr.getFrom()->getName(initialName); initialRaw = tr.getFrom()->getRawOffset(); initialDst = tr.getFrom()->getDSTSavings(); // Check if the next transition is either DST->STD or STD->DST and // within roughly 1 year from the specified date UDate nextTransitionTime = tr.getTime(); if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0) || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) && (date + MILLIS_PER_YEAR > nextTransitionTime)) { int32_t year, month, dom, dow, doy, mid; UDate d; // Get local wall time for the next transition time Grego::timeToFields(nextTransitionTime + initialRaw + initialDst, year, month, dom, dow, doy, mid); int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); // Create DOW rule DateTimeRule *dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); tr.getTo()->getName(name); // Note: SimpleTimeZone does not support raw offset change. // So we always use raw offset of the given time for the rule, // even raw offset is changed. This will result that the result // zone to return wrong offset after the transition. // When we encounter such case, we do not inspect next next // transition for another rule. ar1 = new AnnualTimeZoneRule(name, initialRaw, tr.getTo()->getDSTSavings(), dtr, year, AnnualTimeZoneRule::MAX_YEAR); if (tr.getTo()->getRawOffset() == initialRaw) { // Get the next next transition avail = getNextTransition(nextTransitionTime, FALSE, tr); if (avail) { // Check if the next next transition is either DST->STD or STD->DST // and within roughly 1 year from the next transition if (((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0) || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) && nextTransitionTime + MILLIS_PER_YEAR > tr.getTime()) { // Get local wall time for the next transition time Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(), year, month, dom, dow, doy, mid); weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); // Generate another DOW rule dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); tr.getTo()->getName(name); ar2 = new AnnualTimeZoneRule(name, tr.getTo()->getRawOffset(), tr.getTo()->getDSTSavings(), dtr, year - 1, AnnualTimeZoneRule::MAX_YEAR); // Make sure this rule can be applied to the specified date avail = ar2->getPreviousStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), TRUE, d); if (!avail || d > date || initialRaw != tr.getTo()->getRawOffset() || initialDst != tr.getTo()->getDSTSavings()) { // We cannot use this rule as the second transition rule delete ar2; ar2 = NULL; } } } } if (ar2 == NULL) { // Try previous transition avail = getPreviousTransition(date, TRUE, tr); if (avail) { // Check if the previous transition is either DST->STD or STD->DST. // The actual transition time does not matter here. if ((tr.getFrom()->getDSTSavings() == 0 && tr.getTo()->getDSTSavings() != 0) || (tr.getFrom()->getDSTSavings() != 0 && tr.getTo()->getDSTSavings() == 0)) { // Generate another DOW rule Grego::timeToFields(tr.getTime() + tr.getFrom()->getRawOffset() + tr.getFrom()->getDSTSavings(), year, month, dom, dow, doy, mid); weekInMonth = Grego::dayOfWeekInMonth(year, month, dom); dtr = new DateTimeRule(month, weekInMonth, dow, mid, DateTimeRule::WALL_TIME); tr.getTo()->getName(name); // second rule raw/dst offsets should match raw/dst offsets // at the given time ar2 = new AnnualTimeZoneRule(name, initialRaw, initialDst, dtr, ar1->getStartYear() - 1, AnnualTimeZoneRule::MAX_YEAR); // Check if this rule start after the first rule after the specified date avail = ar2->getNextStart(date, tr.getFrom()->getRawOffset(), tr.getFrom()->getDSTSavings(), FALSE, d); if (!avail || d <= nextTransitionTime) { // We cannot use this rule as the second transition rule delete ar2; ar2 = NULL; } } } } if (ar2 == NULL) { // Cannot find a good pair of AnnualTimeZoneRule delete ar1; ar1 = NULL; } else { // The initial rule should represent the rule before the previous transition ar1->getName(initialName); initialRaw = ar1->getRawOffset(); initialDst = ar1->getDSTSavings(); } } } else { // Try the previous one avail = getPreviousTransition(date, TRUE, tr); if (avail) { tr.getTo()->getName(initialName); initialRaw = tr.getTo()->getRawOffset(); initialDst = tr.getTo()->getDSTSavings(); } else { // No transitions in the past. Just use the current offsets getOffset(date, FALSE, initialRaw, initialDst, status); if (U_FAILURE(status)) { return; } } } // Set the initial rule initial = new InitialTimeZoneRule(initialName, initialRaw, initialDst); // Set the standard and daylight saving rules if (ar1 != NULL && ar2 != NULL) { if (ar1->getDSTSavings() != 0) { dst = ar1; std = ar2; } else { std = ar1; dst = ar2; } } }
UnicodeString& TZGNCore::formatGenericNonLocationName(const TimeZone& tz, UTimeZoneGenericNameType type, UDate date, UnicodeString& name) const { U_ASSERT(type == UTZGNM_LONG || type == UTZGNM_SHORT); name.setToBogus(); const UChar* uID = ZoneMeta::getCanonicalCLDRID(tz); if (uID == NULL) { return name; } UnicodeString tzID(TRUE, uID, -1); // Try to get a name from time zone first UTimeZoneNameType nameType = (type == UTZGNM_LONG) ? UTZNM_LONG_GENERIC : UTZNM_SHORT_GENERIC; fTimeZoneNames->getTimeZoneDisplayName(tzID, nameType, name); if (!name.isEmpty()) { return name; } // Try meta zone UChar mzIDBuf[32]; UnicodeString mzID(mzIDBuf, 0, UPRV_LENGTHOF(mzIDBuf)); fTimeZoneNames->getMetaZoneID(tzID, date, mzID); if (!mzID.isEmpty()) { UErrorCode status = U_ZERO_ERROR; UBool useStandard = FALSE; int32_t raw, sav; UChar tmpNameBuf[64]; tz.getOffset(date, FALSE, raw, sav, status); if (U_FAILURE(status)) { return name; } if (sav == 0) { useStandard = TRUE; TimeZone *tmptz = tz.clone(); // Check if the zone actually uses daylight saving time around the time BasicTimeZone *btz = NULL; if (dynamic_cast<OlsonTimeZone *>(tmptz) != NULL || dynamic_cast<SimpleTimeZone *>(tmptz) != NULL || dynamic_cast<RuleBasedTimeZone *>(tmptz) != NULL || dynamic_cast<VTimeZone *>(tmptz) != NULL) { btz = (BasicTimeZone*)tmptz; } if (btz != NULL) { TimeZoneTransition before; UBool beforTrs = btz->getPreviousTransition(date, TRUE, before); if (beforTrs && (date - before.getTime() < kDstCheckRange) && before.getFrom()->getDSTSavings() != 0) { useStandard = FALSE; } else { TimeZoneTransition after; UBool afterTrs = btz->getNextTransition(date, FALSE, after); if (afterTrs && (after.getTime() - date < kDstCheckRange) && after.getTo()->getDSTSavings() != 0) { useStandard = FALSE; } } } else { // If not BasicTimeZone... only if the instance is not an ICU's implementation. // We may get a wrong answer in edge case, but it should practically work OK. tmptz->getOffset(date - kDstCheckRange, FALSE, raw, sav, status); if (sav != 0) { useStandard = FALSE; } else { tmptz->getOffset(date + kDstCheckRange, FALSE, raw, sav, status); if (sav != 0){ useStandard = FALSE; } } if (U_FAILURE(status)) { delete tmptz; return name; } } delete tmptz; } if (useStandard) { UTimeZoneNameType stdNameType = (nameType == UTZNM_LONG_GENERIC) ? UTZNM_LONG_STANDARD : UTZNM_SHORT_STANDARD; UnicodeString stdName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf)); fTimeZoneNames->getDisplayName(tzID, stdNameType, date, stdName); if (!stdName.isEmpty()) { name.setTo(stdName); // TODO: revisit this issue later // In CLDR, a same display name is used for both generic and standard // for some meta zones in some locales. This looks like a data bugs. // For now, we check if the standard name is different from its generic // name below. UChar genNameBuf[64]; UnicodeString mzGenericName(genNameBuf, 0, UPRV_LENGTHOF(genNameBuf)); fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzGenericName); if (stdName.caseCompare(mzGenericName, 0) == 0) { name.setToBogus(); } } } if (name.isEmpty()) { // Get a name from meta zone UnicodeString mzName(tmpNameBuf, 0, UPRV_LENGTHOF(tmpNameBuf)); fTimeZoneNames->getMetaZoneDisplayName(mzID, nameType, mzName); if (!mzName.isEmpty()) { // Check if we need to use a partial location format. // This check is done by comparing offset with the meta zone's // golden zone at the given date. UChar idBuf[32]; UnicodeString goldenID(idBuf, 0, UPRV_LENGTHOF(idBuf)); fTimeZoneNames->getReferenceZoneID(mzID, fTargetRegion, goldenID); if (!goldenID.isEmpty() && goldenID != tzID) { TimeZone *goldenZone = TimeZone::createTimeZone(goldenID); int32_t raw1, sav1; // Check offset in the golden zone with wall time. // With getOffset(date, false, offsets1), // you may get incorrect results because of time overlap at DST->STD // transition. goldenZone->getOffset(date + raw + sav, TRUE, raw1, sav1, status); delete goldenZone; if (U_SUCCESS(status)) { if (raw != raw1 || sav != sav1) { // Now we need to use a partial location format getPartialLocationName(tzID, mzID, (nameType == UTZNM_LONG_GENERIC), mzName, name); } else { name.setTo(mzName); } } } else { name.setTo(mzName); } } } } return name; }