| // © 2016 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| /* |
| ******************************************************************************* |
| * Copyright (C) 2007-2012, International Business Machines Corporation and |
| * others. All Rights Reserved. |
| ******************************************************************************* |
| */ |
| |
| #include "utypeinfo.h" // for 'typeid' to work |
| |
| #include "unicode/utypes.h" |
| |
| #if !UCONFIG_NO_FORMATTING |
| |
| #if defined(STARBOARD) |
| #include "starboard/client_porting/poem/string_poem.h" |
| #endif // defined(STARBOARD) |
| #include "unicode/tzrule.h" |
| #include "unicode/ucal.h" |
| #include "gregoimp.h" |
| #include "cmemory.h" |
| #include "uarrsort.h" |
| |
| U_CDECL_BEGIN |
| // UComparator function for sorting start times |
| static int32_t U_CALLCONV |
| compareDates(const void * /*context*/, const void *left, const void *right) { |
| UDate l = *((UDate*)left); |
| UDate r = *((UDate*)right); |
| int32_t res = l < r ? -1 : (l == r ? 0 : 1); |
| return res; |
| } |
| U_CDECL_END |
| |
| U_NAMESPACE_BEGIN |
| |
| TimeZoneRule::TimeZoneRule(const UnicodeString& name, int32_t rawOffset, int32_t dstSavings) |
| : UObject(), fName(name), fRawOffset(rawOffset), fDSTSavings(dstSavings) { |
| } |
| |
| TimeZoneRule::TimeZoneRule(const TimeZoneRule& source) |
| : UObject(source), fName(source.fName), fRawOffset(source.fRawOffset), fDSTSavings(source.fDSTSavings) { |
| } |
| |
| TimeZoneRule::~TimeZoneRule() { |
| } |
| |
| TimeZoneRule& |
| TimeZoneRule::operator=(const TimeZoneRule& right) { |
| if (this != &right) { |
| fName = right.fName; |
| fRawOffset = right.fRawOffset; |
| fDSTSavings = right.fDSTSavings; |
| } |
| return *this; |
| } |
| |
| UBool |
| TimeZoneRule::operator==(const TimeZoneRule& that) const { |
| return ((this == &that) || |
| (typeid(*this) == typeid(that) && |
| fName == that.fName && |
| fRawOffset == that.fRawOffset && |
| fDSTSavings == that.fDSTSavings)); |
| } |
| |
| UBool |
| TimeZoneRule::operator!=(const TimeZoneRule& that) const { |
| return !operator==(that); |
| } |
| |
| UnicodeString& |
| TimeZoneRule::getName(UnicodeString& name) const { |
| name = fName; |
| return name; |
| } |
| |
| int32_t |
| TimeZoneRule::getRawOffset(void) const { |
| return fRawOffset; |
| } |
| |
| int32_t |
| TimeZoneRule::getDSTSavings(void) const { |
| return fDSTSavings; |
| } |
| |
| UBool |
| TimeZoneRule::isEquivalentTo(const TimeZoneRule& other) const { |
| return ((this == &other) || |
| (typeid(*this) == typeid(other) && |
| fRawOffset == other.fRawOffset && |
| fDSTSavings == other.fDSTSavings)); |
| } |
| |
| |
| UOBJECT_DEFINE_RTTI_IMPLEMENTATION(InitialTimeZoneRule) |
| |
| InitialTimeZoneRule::InitialTimeZoneRule(const UnicodeString& name, |
| int32_t rawOffset, |
| int32_t dstSavings) |
| : TimeZoneRule(name, rawOffset, dstSavings) { |
| } |
| |
| InitialTimeZoneRule::InitialTimeZoneRule(const InitialTimeZoneRule& source) |
| : TimeZoneRule(source) { |
| } |
| |
| InitialTimeZoneRule::~InitialTimeZoneRule() { |
| } |
| |
| InitialTimeZoneRule* |
| InitialTimeZoneRule::clone() const { |
| return new InitialTimeZoneRule(*this); |
| } |
| |
| InitialTimeZoneRule& |
| InitialTimeZoneRule::operator=(const InitialTimeZoneRule& right) { |
| if (this != &right) { |
| TimeZoneRule::operator=(right); |
| } |
| return *this; |
| } |
| |
| UBool |
| InitialTimeZoneRule::operator==(const TimeZoneRule& that) const { |
| return ((this == &that) || |
| (typeid(*this) == typeid(that) && |
| TimeZoneRule::operator==(that))); |
| } |
| |
| UBool |
| InitialTimeZoneRule::operator!=(const TimeZoneRule& that) const { |
| return !operator==(that); |
| } |
| |
| UBool |
| InitialTimeZoneRule::isEquivalentTo(const TimeZoneRule& other) const { |
| if (this == &other) { |
| return TRUE; |
| } |
| if (typeid(*this) != typeid(other) || TimeZoneRule::isEquivalentTo(other) == FALSE) { |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| UBool |
| InitialTimeZoneRule::getFirstStart(int32_t /*prevRawOffset*/, |
| int32_t /*prevDSTSavings*/, |
| UDate& /*result*/) const { |
| return FALSE; |
| } |
| |
| UBool |
| InitialTimeZoneRule::getFinalStart(int32_t /*prevRawOffset*/, |
| int32_t /*prevDSTSavings*/, |
| UDate& /*result*/) const { |
| return FALSE; |
| } |
| |
| UBool |
| InitialTimeZoneRule::getNextStart(UDate /*base*/, |
| int32_t /*prevRawOffset*/, |
| int32_t /*prevDSTSavings*/, |
| UBool /*inclusive*/, |
| UDate& /*result*/) const { |
| return FALSE; |
| } |
| |
| UBool |
| InitialTimeZoneRule::getPreviousStart(UDate /*base*/, |
| int32_t /*prevRawOffset*/, |
| int32_t /*prevDSTSavings*/, |
| UBool /*inclusive*/, |
| UDate& /*result*/) const { |
| return FALSE; |
| } |
| |
| |
| UOBJECT_DEFINE_RTTI_IMPLEMENTATION(AnnualTimeZoneRule) |
| |
| const int32_t AnnualTimeZoneRule::MAX_YEAR = 0x7FFFFFFF; /* max signed int32 */ |
| |
| AnnualTimeZoneRule::AnnualTimeZoneRule(const UnicodeString& name, |
| int32_t rawOffset, |
| int32_t dstSavings, |
| const DateTimeRule& dateTimeRule, |
| int32_t startYear, |
| int32_t endYear) |
| : TimeZoneRule(name, rawOffset, dstSavings), fDateTimeRule(new DateTimeRule(dateTimeRule)), |
| fStartYear(startYear), fEndYear(endYear) { |
| } |
| |
| AnnualTimeZoneRule::AnnualTimeZoneRule(const UnicodeString& name, |
| int32_t rawOffset, |
| int32_t dstSavings, |
| DateTimeRule* dateTimeRule, |
| int32_t startYear, |
| int32_t endYear) |
| : TimeZoneRule(name, rawOffset, dstSavings), fDateTimeRule(dateTimeRule), |
| fStartYear(startYear), fEndYear(endYear) { |
| } |
| |
| AnnualTimeZoneRule::AnnualTimeZoneRule(const AnnualTimeZoneRule& source) |
| : TimeZoneRule(source), fDateTimeRule(new DateTimeRule(*(source.fDateTimeRule))), |
| fStartYear(source.fStartYear), fEndYear(source.fEndYear) { |
| } |
| |
| AnnualTimeZoneRule::~AnnualTimeZoneRule() { |
| delete fDateTimeRule; |
| } |
| |
| AnnualTimeZoneRule* |
| AnnualTimeZoneRule::clone(void) const { |
| return new AnnualTimeZoneRule(*this); |
| } |
| |
| AnnualTimeZoneRule& |
| AnnualTimeZoneRule::operator=(const AnnualTimeZoneRule& right) { |
| if (this != &right) { |
| TimeZoneRule::operator=(right); |
| delete fDateTimeRule; |
| fDateTimeRule = right.fDateTimeRule->clone(); |
| fStartYear = right.fStartYear; |
| fEndYear = right.fEndYear; |
| } |
| return *this; |
| } |
| |
| UBool |
| AnnualTimeZoneRule::operator==(const TimeZoneRule& that) const { |
| if (this == &that) { |
| return TRUE; |
| } |
| if (typeid(*this) != typeid(that)) { |
| return FALSE; |
| } |
| AnnualTimeZoneRule *atzr = (AnnualTimeZoneRule*)&that; |
| return (*fDateTimeRule == *(atzr->fDateTimeRule) && |
| fStartYear == atzr->fStartYear && |
| fEndYear == atzr->fEndYear); |
| } |
| |
| UBool |
| AnnualTimeZoneRule::operator!=(const TimeZoneRule& that) const { |
| return !operator==(that); |
| } |
| |
| const DateTimeRule* |
| AnnualTimeZoneRule::getRule() const { |
| return fDateTimeRule; |
| } |
| |
| int32_t |
| AnnualTimeZoneRule::getStartYear() const { |
| return fStartYear; |
| } |
| |
| int32_t |
| AnnualTimeZoneRule::getEndYear() const { |
| return fEndYear; |
| } |
| |
| UBool |
| AnnualTimeZoneRule::getStartInYear(int32_t year, |
| int32_t prevRawOffset, |
| int32_t prevDSTSavings, |
| UDate &result) const { |
| if (year < fStartYear || year > fEndYear) { |
| return FALSE; |
| } |
| double ruleDay; |
| DateTimeRule::DateRuleType type = fDateTimeRule->getDateRuleType(); |
| if (type == DateTimeRule::DOM) { |
| ruleDay = Grego::fieldsToDay(year, fDateTimeRule->getRuleMonth(), fDateTimeRule->getRuleDayOfMonth()); |
| } else { |
| UBool after = TRUE; |
| if (type == DateTimeRule::DOW) { |
| // Normalize DOW rule into DOW_GEQ_DOM or DOW_LEQ_DOM |
| int32_t weeks = fDateTimeRule->getRuleWeekInMonth(); |
| if (weeks > 0) { |
| ruleDay = Grego::fieldsToDay(year, fDateTimeRule->getRuleMonth(), 1); |
| ruleDay += 7 * (weeks - 1); |
| } else { |
| after = FALSE; |
| ruleDay = Grego::fieldsToDay(year, fDateTimeRule->getRuleMonth(), |
| Grego::monthLength(year, fDateTimeRule->getRuleMonth())); |
| ruleDay += 7 * (weeks + 1); |
| } |
| } else { |
| int32_t month = fDateTimeRule->getRuleMonth(); |
| int32_t dom = fDateTimeRule->getRuleDayOfMonth(); |
| if (type == DateTimeRule::DOW_LEQ_DOM) { |
| after = FALSE; |
| // Handle Feb <=29 |
| if (month == UCAL_FEBRUARY && dom == 29 && !Grego::isLeapYear(year)) { |
| dom--; |
| } |
| } |
| ruleDay = Grego::fieldsToDay(year, month, dom); |
| } |
| int32_t dow = Grego::dayOfWeek(ruleDay); |
| int32_t delta = fDateTimeRule->getRuleDayOfWeek() - dow; |
| if (after) { |
| delta = delta < 0 ? delta + 7 : delta; |
| } else { |
| delta = delta > 0 ? delta - 7 : delta; |
| } |
| ruleDay += delta; |
| } |
| |
| result = ruleDay*U_MILLIS_PER_DAY + fDateTimeRule->getRuleMillisInDay(); |
| if (fDateTimeRule->getTimeRuleType() != DateTimeRule::UTC_TIME) { |
| result -= prevRawOffset; |
| } |
| if (fDateTimeRule->getTimeRuleType() == DateTimeRule::WALL_TIME) { |
| result -= prevDSTSavings; |
| } |
| return TRUE; |
| } |
| |
| UBool |
| AnnualTimeZoneRule::isEquivalentTo(const TimeZoneRule& other) const { |
| if (this == &other) { |
| return TRUE; |
| } |
| if (typeid(*this) != typeid(other) || TimeZoneRule::isEquivalentTo(other) == FALSE) { |
| return FALSE; |
| } |
| AnnualTimeZoneRule* that = (AnnualTimeZoneRule*)&other; |
| return (*fDateTimeRule == *(that->fDateTimeRule) && |
| fStartYear == that->fStartYear && |
| fEndYear == that->fEndYear); |
| } |
| |
| UBool |
| AnnualTimeZoneRule::getFirstStart(int32_t prevRawOffset, |
| int32_t prevDSTSavings, |
| UDate& result) const { |
| return getStartInYear(fStartYear, prevRawOffset, prevDSTSavings, result); |
| } |
| |
| UBool |
| AnnualTimeZoneRule::getFinalStart(int32_t prevRawOffset, |
| int32_t prevDSTSavings, |
| UDate& result) const { |
| if (fEndYear == MAX_YEAR) { |
| return FALSE; |
| } |
| return getStartInYear(fEndYear, prevRawOffset, prevDSTSavings, result); |
| } |
| |
| UBool |
| AnnualTimeZoneRule::getNextStart(UDate base, |
| int32_t prevRawOffset, |
| int32_t prevDSTSavings, |
| UBool inclusive, |
| UDate& result) const { |
| int32_t year, month, dom, dow, doy, mid; |
| Grego::timeToFields(base, year, month, dom, dow, doy, mid); |
| if (year < fStartYear) { |
| return getFirstStart(prevRawOffset, prevDSTSavings, result); |
| } |
| UDate tmp; |
| if (getStartInYear(year, prevRawOffset, prevDSTSavings, tmp)) { |
| if (tmp < base || (!inclusive && (tmp == base))) { |
| // Return the next one |
| return getStartInYear(year + 1, prevRawOffset, prevDSTSavings, result); |
| } else { |
| result = tmp; |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } |
| |
| UBool |
| AnnualTimeZoneRule::getPreviousStart(UDate base, |
| int32_t prevRawOffset, |
| int32_t prevDSTSavings, |
| UBool inclusive, |
| UDate& result) const { |
| int32_t year, month, dom, dow, doy, mid; |
| Grego::timeToFields(base, year, month, dom, dow, doy, mid); |
| if (year > fEndYear) { |
| return getFinalStart(prevRawOffset, prevDSTSavings, result); |
| } |
| UDate tmp; |
| if (getStartInYear(year, prevRawOffset, prevDSTSavings, tmp)) { |
| if (tmp > base || (!inclusive && (tmp == base))) { |
| // Return the previous one |
| return getStartInYear(year - 1, prevRawOffset, prevDSTSavings, result); |
| } else { |
| result = tmp; |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } |
| |
| UOBJECT_DEFINE_RTTI_IMPLEMENTATION(TimeArrayTimeZoneRule) |
| |
| TimeArrayTimeZoneRule::TimeArrayTimeZoneRule(const UnicodeString& name, |
| int32_t rawOffset, |
| int32_t dstSavings, |
| const UDate* startTimes, |
| int32_t numStartTimes, |
| DateTimeRule::TimeRuleType timeRuleType) |
| : TimeZoneRule(name, rawOffset, dstSavings), fTimeRuleType(timeRuleType), |
| fStartTimes(NULL) { |
| UErrorCode status = U_ZERO_ERROR; |
| initStartTimes(startTimes, numStartTimes, status); |
| //TODO - status? |
| } |
| |
| |
| TimeArrayTimeZoneRule::TimeArrayTimeZoneRule(const TimeArrayTimeZoneRule& source) |
| : TimeZoneRule(source), fTimeRuleType(source.fTimeRuleType), fStartTimes(NULL) { |
| UErrorCode status = U_ZERO_ERROR; |
| initStartTimes(source.fStartTimes, source.fNumStartTimes, status); |
| //TODO - status? |
| } |
| |
| |
| TimeArrayTimeZoneRule::~TimeArrayTimeZoneRule() { |
| if (fStartTimes != NULL && fStartTimes != fLocalStartTimes) { |
| uprv_free(fStartTimes); |
| } |
| } |
| |
| TimeArrayTimeZoneRule* |
| TimeArrayTimeZoneRule::clone(void) const { |
| return new TimeArrayTimeZoneRule(*this); |
| } |
| |
| |
| TimeArrayTimeZoneRule& |
| TimeArrayTimeZoneRule::operator=(const TimeArrayTimeZoneRule& right) { |
| if (this != &right) { |
| TimeZoneRule::operator=(right); |
| UErrorCode status = U_ZERO_ERROR; |
| initStartTimes(right.fStartTimes, right.fNumStartTimes, status); |
| //TODO - status? |
| fTimeRuleType = right.fTimeRuleType; |
| } |
| return *this; |
| } |
| |
| UBool |
| TimeArrayTimeZoneRule::operator==(const TimeZoneRule& that) const { |
| if (this == &that) { |
| return TRUE; |
| } |
| if (typeid(*this) != typeid(that) || TimeZoneRule::operator==(that) == FALSE) { |
| return FALSE; |
| } |
| TimeArrayTimeZoneRule *tatzr = (TimeArrayTimeZoneRule*)&that; |
| if (fTimeRuleType != tatzr->fTimeRuleType || |
| fNumStartTimes != tatzr->fNumStartTimes) { |
| return FALSE; |
| } |
| // Compare start times |
| UBool res = TRUE; |
| for (int32_t i = 0; i < fNumStartTimes; i++) { |
| if (fStartTimes[i] != tatzr->fStartTimes[i]) { |
| res = FALSE; |
| break; |
| } |
| } |
| return res; |
| } |
| |
| UBool |
| TimeArrayTimeZoneRule::operator!=(const TimeZoneRule& that) const { |
| return !operator==(that); |
| } |
| |
| DateTimeRule::TimeRuleType |
| TimeArrayTimeZoneRule::getTimeType(void) const { |
| return fTimeRuleType; |
| } |
| |
| UBool |
| TimeArrayTimeZoneRule::getStartTimeAt(int32_t index, UDate& result) const { |
| if (index >= fNumStartTimes || index < 0) { |
| return FALSE; |
| } |
| result = fStartTimes[index]; |
| return TRUE; |
| } |
| |
| int32_t |
| TimeArrayTimeZoneRule::countStartTimes(void) const { |
| return fNumStartTimes; |
| } |
| |
| UBool |
| TimeArrayTimeZoneRule::isEquivalentTo(const TimeZoneRule& other) const { |
| if (this == &other) { |
| return TRUE; |
| } |
| if (typeid(*this) != typeid(other) || TimeZoneRule::isEquivalentTo(other) == FALSE) { |
| return FALSE; |
| } |
| TimeArrayTimeZoneRule* that = (TimeArrayTimeZoneRule*)&other; |
| if (fTimeRuleType != that->fTimeRuleType || |
| fNumStartTimes != that->fNumStartTimes) { |
| return FALSE; |
| } |
| // Compare start times |
| UBool res = TRUE; |
| for (int32_t i = 0; i < fNumStartTimes; i++) { |
| if (fStartTimes[i] != that->fStartTimes[i]) { |
| res = FALSE; |
| break; |
| } |
| } |
| return res; |
| } |
| |
| UBool |
| TimeArrayTimeZoneRule::getFirstStart(int32_t prevRawOffset, |
| int32_t prevDSTSavings, |
| UDate& result) const { |
| if (fNumStartTimes <= 0 || fStartTimes == NULL) { |
| return FALSE; |
| } |
| result = getUTC(fStartTimes[0], prevRawOffset, prevDSTSavings); |
| return TRUE; |
| } |
| |
| UBool |
| TimeArrayTimeZoneRule::getFinalStart(int32_t prevRawOffset, |
| int32_t prevDSTSavings, |
| UDate& result) const { |
| if (fNumStartTimes <= 0 || fStartTimes == NULL) { |
| return FALSE; |
| } |
| result = getUTC(fStartTimes[fNumStartTimes - 1], prevRawOffset, prevDSTSavings); |
| return TRUE; |
| } |
| |
| UBool |
| TimeArrayTimeZoneRule::getNextStart(UDate base, |
| int32_t prevRawOffset, |
| int32_t prevDSTSavings, |
| UBool inclusive, |
| UDate& result) const { |
| int32_t i = fNumStartTimes - 1; |
| for (; i >= 0; i--) { |
| UDate time = getUTC(fStartTimes[i], prevRawOffset, prevDSTSavings); |
| if (time < base || (!inclusive && time == base)) { |
| break; |
| } |
| result = time; |
| } |
| if (i == fNumStartTimes - 1) { |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| UBool |
| TimeArrayTimeZoneRule::getPreviousStart(UDate base, |
| int32_t prevRawOffset, |
| int32_t prevDSTSavings, |
| UBool inclusive, |
| UDate& result) const { |
| int32_t i = fNumStartTimes - 1; |
| for (; i >= 0; i--) { |
| UDate time = getUTC(fStartTimes[i], prevRawOffset, prevDSTSavings); |
| if (time < base || (inclusive && time == base)) { |
| result = time; |
| return TRUE; |
| } |
| } |
| return FALSE; |
| } |
| |
| |
| // ---- private methods ------ |
| |
| UBool |
| TimeArrayTimeZoneRule::initStartTimes(const UDate source[], int32_t size, UErrorCode& status) { |
| // Free old array |
| if (fStartTimes != NULL && fStartTimes != fLocalStartTimes) { |
| uprv_free(fStartTimes); |
| } |
| // Allocate new one if needed |
| if (size > TIMEARRAY_STACK_BUFFER_SIZE) { |
| fStartTimes = (UDate*)uprv_malloc(sizeof(UDate)*size); |
| if (fStartTimes == NULL) { |
| status = U_MEMORY_ALLOCATION_ERROR; |
| fNumStartTimes = 0; |
| return FALSE; |
| } |
| } else { |
| fStartTimes = (UDate*)fLocalStartTimes; |
| } |
| uprv_memcpy(fStartTimes, source, sizeof(UDate)*size); |
| fNumStartTimes = size; |
| // Sort dates |
| uprv_sortArray(fStartTimes, fNumStartTimes, (int32_t)sizeof(UDate), compareDates, NULL, TRUE, &status); |
| if (U_FAILURE(status)) { |
| if (fStartTimes != NULL && fStartTimes != fLocalStartTimes) { |
| uprv_free(fStartTimes); |
| } |
| fNumStartTimes = 0; |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| UDate |
| TimeArrayTimeZoneRule::getUTC(UDate time, int32_t raw, int32_t dst) const { |
| if (fTimeRuleType != DateTimeRule::UTC_TIME) { |
| time -= raw; |
| } |
| if (fTimeRuleType == DateTimeRule::WALL_TIME) { |
| time -= dst; |
| } |
| return time; |
| } |
| |
| U_NAMESPACE_END |
| |
| #endif /* #if !UCONFIG_NO_FORMATTING */ |
| |
| //eof |
| |