diff --git a/headers/private/libroot/locale/ICULocaleBackend.h b/headers/private/libroot/locale/ICULocaleBackend.h index f740369028..e5d0a14300 100644 --- a/headers/private/libroot/locale/ICULocaleBackend.h +++ b/headers/private/libroot/locale/ICULocaleBackend.h @@ -45,7 +45,7 @@ public: virtual status_t Strxfrm(char* out, const char* in, size_t size, size_t& outSize); - virtual status_t TZSet(const char* timeZoneID); + virtual status_t TZSet(const char* timeZoneID, const char* tz); virtual status_t Localtime(const time_t* inTime, struct tm* tmOut); virtual status_t Gmtime(const time_t* inTime, struct tm* tmOut); diff --git a/headers/private/libroot/locale/ICUTimeConversion.h b/headers/private/libroot/locale/ICUTimeConversion.h index a614a203df..6ceb45f3b1 100644 --- a/headers/private/libroot/locale/ICUTimeConversion.h +++ b/headers/private/libroot/locale/ICUTimeConversion.h @@ -25,7 +25,7 @@ public: virtual void Initialize( TimeConversionDataBridge* dataBridge); - status_t TZSet(const char* timeZoneID); + status_t TZSet(const char* timeZoneID, const char* tz); status_t Localtime(const time_t* inTime, struct tm* tmOut); diff --git a/headers/private/libroot/locale/LocaleBackend.h b/headers/private/libroot/locale/LocaleBackend.h index 3c19319948..635b2f4ff1 100644 --- a/headers/private/libroot/locale/LocaleBackend.h +++ b/headers/private/libroot/locale/LocaleBackend.h @@ -127,7 +127,8 @@ public: virtual status_t Strxfrm(char* out, const char* in, size_t size, size_t& outSize) = 0; - virtual status_t TZSet(const char* timeZoneID) = 0; + virtual status_t TZSet(const char* timeZoneID, + const char* tz) = 0; virtual status_t Localtime(const time_t* inTime, struct tm* tmOut) = 0; virtual status_t Gmtime(const time_t* inTime, diff --git a/src/system/libroot/add-ons/icu/ICULocaleBackend.cpp b/src/system/libroot/add-ons/icu/ICULocaleBackend.cpp index c9459480e9..b4bed3f4b7 100644 --- a/src/system/libroot/add-ons/icu/ICULocaleBackend.cpp +++ b/src/system/libroot/add-ons/icu/ICULocaleBackend.cpp @@ -245,11 +245,11 @@ ICULocaleBackend::Strxfrm(char* out, const char* in, size_t size, status_t -ICULocaleBackend::TZSet(const char* timeZoneID) +ICULocaleBackend::TZSet(const char* timeZoneID, const char* tz) { ErrnoMaintainer errnoMaintainer; - return fTimeConversion.TZSet(timeZoneID); + return fTimeConversion.TZSet(timeZoneID, tz); } diff --git a/src/system/libroot/add-ons/icu/ICUTimeConversion.cpp b/src/system/libroot/add-ons/icu/ICUTimeConversion.cpp index 0238d6721e..990d08e839 100644 --- a/src/system/libroot/add-ons/icu/ICUTimeConversion.cpp +++ b/src/system/libroot/add-ons/icu/ICUTimeConversion.cpp @@ -39,13 +39,41 @@ ICUTimeConversion::Initialize(TimeConversionDataBridge* dataBridge) status_t -ICUTimeConversion::TZSet(const char* timeZoneID) +ICUTimeConversion::TZSet(const char* timeZoneID, const char* tz) { - // nothing to do if the given name matches any name of the current timezone - if (strcasecmp(fTimeZoneID, timeZoneID) == 0) - return B_OK; + // The given TZ environment variable's content overrides the default + // system timezone. + if (tz != NULL) { + // If the value given in the TZ env-var starts with a colon, that + // value is implementation specific, we expect a full timezone ID. + if (*tz == ':') { + // nothing to do if the given name matches the current timezone + if (strcasecmp(fTimeZoneID, tz + 1) == 0) + return B_OK; - strlcpy(fTimeZoneID, timeZoneID, sizeof(fTimeZoneID)); + strlcpy(fTimeZoneID, tz + 1, sizeof(fTimeZoneID)); + } else { + // We ignore anything following the timezone name, as those values + // are determined by the corresponding ICU-timezone (and glibc's + // tzset() implementation seems to do the same). + const char* tzNameEnd = tz; + while(isalpha(*tzNameEnd)) + ++tzNameEnd; + + strlcpy(fTimeZoneID, tz, + min_c((uint32)(1 + tzNameEnd - tz), sizeof(fTimeZoneID))); + + // nothing to do if the given name matches the current timezone + if (strcasecmp(fTimeZoneID, fDataBridge->addrOfTZName[0]) == 0) + return B_OK; + } + } else { + // nothing to do if the given name matches the current timezone + if (strcasecmp(fTimeZoneID, timeZoneID) == 0) + return B_OK; + + strlcpy(fTimeZoneID, timeZoneID, sizeof(fTimeZoneID)); + } ObjectDeleter icuTimeZone = TimeZone::createTimeZone(fTimeZoneID); if (icuTimeZone.Get() == NULL) @@ -71,16 +99,20 @@ ICUTimeConversion::TZSet(const char* timeZoneID) *fDataBridge->addrOfDaylight = icuTimeZone->useDaylightTime(); for (int i = 0; i < 2; ++i) { - UnicodeString icuString; - icuTimeZone->getDisplayName(i == 1, TimeZone::SHORT_COMMONLY_USED, - fTimeData.ICULocale(), icuString); - CheckedArrayByteSink byteSink(fDataBridge->addrOfTZName[i], - sizeof(fTimeZoneID)); - icuString.toUTF8(byteSink); + if (tz != NULL && *tz != ':' && i == 0) { + strcpy(fDataBridge->addrOfTZName[0], fTimeZoneID); + } else { + UnicodeString icuString; + icuTimeZone->getDisplayName(i == 1, TimeZone::SHORT_COMMONLY_USED, + fTimeData.ICULocale(), icuString); + CheckedArrayByteSink byteSink(fDataBridge->addrOfTZName[i], + sizeof(fTimeZoneID)); + icuString.toUTF8(byteSink); - // make sure to canonicalize "GMT+00:00" to just "GMT" - if (strcmp(fDataBridge->addrOfTZName[i], "GMT+00:00") == 0) - fDataBridge->addrOfTZName[i][3] = '\0'; + // make sure to canonicalize "GMT+00:00" to just "GMT" + if (strcmp(fDataBridge->addrOfTZName[i], "GMT+00:00") == 0) + fDataBridge->addrOfTZName[i][3] = '\0'; + } } return B_OK; diff --git a/src/system/libroot/posix/time/localtime.cpp b/src/system/libroot/posix/time/localtime.cpp index 527a3a52f1..d2b897f213 100644 --- a/src/system/libroot/posix/time/localtime.cpp +++ b/src/system/libroot/posix/time/localtime.cpp @@ -39,15 +39,9 @@ tzset(void) return; char timeZoneID[B_FILE_NAME_LENGTH] = { "GMT" }; + _kern_get_timezone(NULL, timeZoneID, sizeof(timeZoneID)); - const char* tz = getenv("TZ"); - if (tz != NULL) - strlcpy(timeZoneID, tz, sizeof(timeZoneID)); - else - _kern_get_timezone(NULL, timeZoneID, sizeof(timeZoneID)); - - if (gLocaleBackend != NULL) - gLocaleBackend->TZSet(timeZoneID); + gLocaleBackend->TZSet(timeZoneID, getenv("TZ")); } diff --git a/src/tests/system/libroot/posix/locale_test.cpp b/src/tests/system/libroot/posix/locale_test.cpp index 3373f8b770..cb1594896b 100644 --- a/src/tests/system/libroot/posix/locale_test.cpp +++ b/src/tests/system/libroot/posix/locale_test.cpp @@ -1819,30 +1819,42 @@ test_timeconversions() gtm.tm_wday = -1; gtm.tm_yday = -1; test_mktime("GMT", gtm, testTime, 6, 197); + tm btm = { 9, 26, 20, 17, 6, 110, 6, 197, 1, 2 * 3600, (char*)"CEST" }; - test_localtime("Europe/Berlin", testTime, btm); - test_gmtime("Europe/Berlin", testTime, gtm); + test_localtime(":Europe/Berlin", testTime, btm); + test_gmtime(":Europe/Berlin", testTime, gtm); btm.tm_wday = -1; btm.tm_yday = -1; - test_mktime("Europe/Berlin", btm, testTime, 6, 197); + test_mktime(":Europe/Berlin", btm, testTime, 6, 197); + + tm ctm = { + 9, 26, 20, 17, 6, 110, 6, 197, 1, 2 * 3600, (char*)"CEST" + }; + test_localtime("CET", testTime, ctm); + test_gmtime("CET", testTime, gtm); + ctm.tm_wday = -1; + ctm.tm_yday = -1; + test_mktime("CET", ctm, testTime, 6, 197); + tm latm = { 9, 26, 11, 17, 6, 110, 6, 197, 1, -7 * 3600, (char*)"PDT" }; - test_localtime("America/Los_Angeles", testTime, latm); - test_gmtime("America/Los_Angeles", testTime, gtm); + test_localtime(":America/Los_Angeles", testTime, latm); + test_gmtime(":America/Los_Angeles", testTime, gtm); latm.tm_wday = -1; latm.tm_yday = -1; - test_mktime("America/Los_Angeles", latm, testTime, 6, 197); + test_mktime(":America/Los_Angeles", latm, testTime, 6, 197); + tm ttm = { 9, 26, 3, 18, 6, 110, 0, 198, 0, 9 * 3600, (char*)"JST" }; - test_localtime("Asia/Tokyo", testTime, ttm); - test_gmtime("Asia/Tokyo", testTime, gtm); + test_localtime(":Asia/Tokyo", testTime, ttm); + test_gmtime(":Asia/Tokyo", testTime, gtm); ttm.tm_wday = -1; ttm.tm_yday = -1; - test_mktime("Asia/Tokyo", ttm, testTime, 0, 198); + test_mktime(":Asia/Tokyo", ttm, testTime, 0, 198); } { @@ -1855,30 +1867,42 @@ test_timeconversions() gtm.tm_wday = -1; gtm.tm_yday = -1; test_mktime("GMT", gtm, testTime, 2, 67); + tm btm = { 9, 26, 19, 9, 2, 110, 2, 67, 0, 3600, (char*)"CET" }; - test_localtime("Europe/Berlin", testTime, btm); - test_gmtime("Europe/Berlin", testTime, gtm); + test_localtime(":Europe/Berlin", testTime, btm); + test_gmtime(":Europe/Berlin", testTime, gtm); btm.tm_wday = -1; btm.tm_yday = -1; - test_mktime("Europe/Berlin", btm, testTime, 2, 67); + test_mktime(":Europe/Berlin", btm, testTime, 2, 67); + + tm ctm = { + 9, 26, 19, 9, 2, 110, 2, 67, 0, 3600, (char*)"CET" + }; + test_localtime("CET", testTime, ctm); + test_gmtime("CET", testTime, gtm); + ctm.tm_wday = -1; + ctm.tm_yday = -1; + test_mktime("CET", ctm, testTime, 2, 67); + tm latm = { 9, 26, 10, 9, 2, 110, 2, 67, 0, -8 * 3600, (char*)"PST" }; - test_localtime("America/Los_Angeles", testTime, latm); - test_gmtime("America/Los_Angeles", testTime, gtm); + test_localtime(":America/Los_Angeles", testTime, latm); + test_gmtime(":America/Los_Angeles", testTime, gtm); latm.tm_wday = -1; latm.tm_yday = -1; - test_mktime("America/Los_Angeles", latm, testTime, 2, 67); + test_mktime(":America/Los_Angeles", latm, testTime, 2, 67); + tm ttm = { 9, 26, 3, 10, 2, 110, 3, 68, 0, 9 * 3600, (char*)"JST" }; - test_localtime("Asia/Tokyo", testTime, ttm); - test_gmtime("Asia/Tokyo", testTime, gtm); + test_localtime(":Asia/Tokyo", testTime, ttm); + test_gmtime(":Asia/Tokyo", testTime, gtm); ttm.tm_wday = -1; ttm.tm_yday = -1; - test_mktime("Asia/Tokyo", ttm, testTime, 3, 68); + test_mktime(":Asia/Tokyo", ttm, testTime, 3, 68); } { @@ -1891,30 +1915,42 @@ test_timeconversions() gtm.tm_wday = -1; gtm.tm_yday = -1; test_mktime("GMT", gtm, testTime, 4, 0); + tm btm = { 0, 0, 1, 1, 0, 70, 4, 0, 0, 1 * 3600, (char*)"CET" }; - test_localtime("Europe/Berlin", testTime, btm); - test_gmtime("Europe/Berlin", testTime, gtm); + test_localtime(":Europe/Berlin", testTime, btm); + test_gmtime(":Europe/Berlin", testTime, gtm); btm.tm_wday = -1; btm.tm_yday = -1; - test_mktime("Europe/Berlin", btm, testTime, 4, 0); + test_mktime(":Europe/Berlin", btm, testTime, 4, 0); + + tm ctm = { + 0, 0, 1, 1, 0, 70, 4, 0, 0, 1 * 3600, (char*)"CET" + }; + test_localtime("CET", testTime, ctm); + test_gmtime("CET", testTime, gtm); + ctm.tm_wday = -1; + ctm.tm_yday = -1; + test_mktime("CET", ctm, testTime, 4, 0); + tm latm = { 0, 0, 16, 31, 11, 69, 3, 364, 0, -8 * 3600, (char*)"PST" }; - test_localtime("America/Los_Angeles", testTime, latm); - test_gmtime("America/Los_Angeles", testTime, gtm); + test_localtime(":America/Los_Angeles", testTime, latm); + test_gmtime(":America/Los_Angeles", testTime, gtm); latm.tm_wday = -1; latm.tm_yday = -1; - test_mktime("America/Los_Angeles", latm, testTime, 3, 364); + test_mktime(":America/Los_Angeles", latm, testTime, 3, 364); + tm ttm = { 0, 0, 9, 1, 0, 70, 4, 0, 0, 9 * 3600, (char*)"JST" }; - test_localtime("Asia/Tokyo", testTime, ttm); - test_gmtime("Asia/Tokyo", testTime, gtm); + test_localtime(":Asia/Tokyo", testTime, ttm); + test_gmtime(":Asia/Tokyo", testTime, gtm); ttm.tm_wday = -1; ttm.tm_yday = -1; - test_mktime("Asia/Tokyo", ttm, testTime, 4, 0); + test_mktime(":Asia/Tokyo", ttm, testTime, 4, 0); } }