From e8226ce48d295aa227b3faea3f43747c77604f58 Mon Sep 17 00:00:00 2001 From: Oliver Tappe Date: Thu, 3 Mar 2011 00:34:51 +0000 Subject: [PATCH] * fix support for influencing tzset() via TZ environment variable (now at least and are supported properly) * instead of creating a TimeZone object whenever needed, we now create it in tzset() and keep it around * add tests for TZ to locale_test git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@40788 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../libroot/locale/ICUTimeConversion.h | 1 + .../libroot/add-ons/icu/ICUTimeConversion.cpp | 92 +++++++++++-------- .../system/libroot/posix/locale_test.cpp | 18 ++++ 3 files changed, 74 insertions(+), 37 deletions(-) diff --git a/headers/private/libroot/locale/ICUTimeConversion.h b/headers/private/libroot/locale/ICUTimeConversion.h index 58b2b098e7..f46dfbaec2 100644 --- a/headers/private/libroot/locale/ICUTimeConversion.h +++ b/headers/private/libroot/locale/ICUTimeConversion.h @@ -42,6 +42,7 @@ private: TimeConversionDataBridge* fDataBridge; + TimeZone* fTimeZone; char fTimeZoneID[B_FILE_NAME_LENGTH]; }; diff --git a/src/system/libroot/add-ons/icu/ICUTimeConversion.cpp b/src/system/libroot/add-ons/icu/ICUTimeConversion.cpp index 5a5855b7f0..f453f7ee86 100644 --- a/src/system/libroot/add-ons/icu/ICUTimeConversion.cpp +++ b/src/system/libroot/add-ons/icu/ICUTimeConversion.cpp @@ -21,7 +21,8 @@ namespace Libroot { ICUTimeConversion::ICUTimeConversion(const ICUTimeData& timeData) : fTimeData(timeData), - fDataBridge(NULL) + fDataBridge(NULL), + fTimeZone(NULL) { fTimeZoneID[0] = '\0'; } @@ -29,6 +30,7 @@ ICUTimeConversion::ICUTimeConversion(const ICUTimeData& timeData) ICUTimeConversion::~ICUTimeConversion() { + delete fTimeZone; } @@ -42,6 +44,8 @@ ICUTimeConversion::Initialize(TimeConversionDataBridge* dataBridge) status_t ICUTimeConversion::TZSet(const char* timeZoneID, const char* tz) { + bool offsetHasBeenSet = false; + // The given TZ environment variable's content overrides the default // system timezone. if (tz != NULL) { @@ -54,19 +58,31 @@ ICUTimeConversion::TZSet(const char* timeZoneID, const char* tz) 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))); + // note timezone name + strlcpy(fTimeZoneID, tz, sizeof(fTimeZoneID)); // nothing to do if the given name matches the current timezone if (strcasecmp(fTimeZoneID, fDataBridge->addrOfTZName[0]) == 0) return B_OK; + + // parse TZ variable (only and supported) + const char* tzNameEnd = tz; + while(isalpha(*tzNameEnd)) + ++tzNameEnd; + if (*tzNameEnd == '-' || *tzNameEnd == '+') { + int hours = 0; + int minutes = 0; + int seconds = 0; + sscanf(tzNameEnd + 1, "%2d:%2d:%2d", &hours, &minutes, + &seconds); + hours = min_c(24, max_c(0, hours)); + minutes = min_c(59, max_c(0, minutes)); + seconds = min_c(59, max_c(0, seconds)); + + *fDataBridge->addrOfTimezone = (*tzNameEnd == '-' ? -1 : 1) + * (hours * 3600 + minutes * 60 + seconds); + offsetHasBeenSet = true; + } } } else { // nothing to do if the given name matches the current timezone @@ -76,35 +92,39 @@ ICUTimeConversion::TZSet(const char* timeZoneID, const char* tz) strlcpy(fTimeZoneID, timeZoneID, sizeof(fTimeZoneID)); } - ObjectDeleter icuTimeZone = TimeZone::createTimeZone(fTimeZoneID); - if (icuTimeZone.Get() == NULL) + delete fTimeZone; + fTimeZone = TimeZone::createTimeZone(fTimeZoneID); + if (fTimeZone == NULL) return B_NO_MEMORY; - int32_t rawOffset; - int32_t dstOffset; - UDate nowMillis = 1000 * (UDate)time(NULL); - UErrorCode icuStatus = U_ZERO_ERROR; - icuTimeZone->getOffset(nowMillis, FALSE, rawOffset, dstOffset, icuStatus); - if (!U_SUCCESS(icuStatus)) { - *fDataBridge->addrOfTimezone = 0; - *fDataBridge->addrOfDaylight = false; - strcpy(fDataBridge->addrOfTZName[0], "GMT"); - strcpy(fDataBridge->addrOfTZName[1], "GMT"); + if (offsetHasBeenSet) { + fTimeZone->setRawOffset(*fDataBridge->addrOfTimezone * -1 * 1000); + } else { + int32_t rawOffset; + int32_t dstOffset; + UDate nowMillis = 1000 * (UDate)time(NULL); + UErrorCode icuStatus = U_ZERO_ERROR; + fTimeZone->getOffset(nowMillis, FALSE, rawOffset, dstOffset, icuStatus); + if (!U_SUCCESS(icuStatus)) { + *fDataBridge->addrOfTimezone = 0; + *fDataBridge->addrOfDaylight = false; + strcpy(fDataBridge->addrOfTZName[0], "GMT"); + strcpy(fDataBridge->addrOfTZName[1], "GMT"); - return B_ERROR; + return B_ERROR; + } + *fDataBridge->addrOfTimezone = -1 * (rawOffset + dstOffset) / 1000; + // we want seconds, not the ms that ICU gives us } - *fDataBridge->addrOfTimezone = -1 * (rawOffset + dstOffset) / 1000; - // we want seconds, not the ms that ICU gives us - - *fDataBridge->addrOfDaylight = icuTimeZone->useDaylightTime(); + *fDataBridge->addrOfDaylight = fTimeZone->useDaylightTime(); for (int i = 0; i < 2; ++i) { if (tz != NULL && *tz != ':' && i == 0) { strcpy(fDataBridge->addrOfTZName[0], fTimeZoneID); } else { UnicodeString icuString; - icuTimeZone->getDisplayName(i == 1, TimeZone::SHORT_COMMONLY_USED, + fTimeZone->getDisplayName(i == 1, TimeZone::SHORT_COMMONLY_USED, fTimeData.ICULocale(), icuString); CheckedArrayByteSink byteSink(fDataBridge->addrOfTZName[i], sizeof(fTimeZoneID)); @@ -123,12 +143,11 @@ ICUTimeConversion::TZSet(const char* timeZoneID, const char* tz) status_t ICUTimeConversion::Localtime(const time_t* inTime, struct tm* tmOut) { - ObjectDeleter icuTimeZone = TimeZone::createTimeZone(fTimeZoneID); - if (icuTimeZone.Get() == NULL) - return B_NO_MEMORY; + if (fTimeZone == NULL) + return B_NO_INIT; tmOut->tm_zone = fTimeZoneID; - return _FillTmValues(icuTimeZone.Get(), inTime, tmOut); + return _FillTmValues(fTimeZone, inTime, tmOut); } @@ -145,12 +164,11 @@ ICUTimeConversion::Gmtime(const time_t* inTime, struct tm* tmOut) status_t ICUTimeConversion::Mktime(struct tm* inOutTm, time_t& timeOut) { - ObjectDeleter icuTimeZone = TimeZone::createTimeZone(fTimeZoneID); - if (icuTimeZone.Get() == NULL) - return B_NO_MEMORY; + if (fTimeZone == NULL) + return B_NO_INIT; UErrorCode icuStatus = U_ZERO_ERROR; - GregorianCalendar calendar(*icuTimeZone.Get(), fTimeData.ICULocale(), + GregorianCalendar calendar(*fTimeZone, fTimeData.ICULocale(), icuStatus); if (!U_SUCCESS(icuStatus)) return B_ERROR; @@ -164,7 +182,7 @@ ICUTimeConversion::Mktime(struct tm* inOutTm, time_t& timeOut) return B_ERROR; timeOut = (time_t)((int64_t)timeInMillis / 1000); - return _FillTmValues(icuTimeZone.Get(), &timeOut, inOutTm); + return _FillTmValues(fTimeZone, &timeOut, inOutTm); } diff --git a/src/tests/system/libroot/posix/locale_test.cpp b/src/tests/system/libroot/posix/locale_test.cpp index cb1594896b..d4b12c9ca9 100644 --- a/src/tests/system/libroot/posix/locale_test.cpp +++ b/src/tests/system/libroot/posix/locale_test.cpp @@ -1820,6 +1820,24 @@ test_timeconversions() gtm.tm_yday = -1; test_mktime("GMT", gtm, testTime, 6, 197); + tm gtmplus2 = { + 9, 26, 16, 17, 6, 110, 6, 197, 0, -2 * 3600, (char*)"GMT+2" + }; + test_localtime("GMT+2", testTime, gtmplus2); + test_gmtime("GMT+2", testTime, gtm); + gtmplus2.tm_wday = -1; + gtmplus2.tm_yday = -1; + test_mktime("GMT+2", gtmplus2, testTime, 6, 197); + + tm gtmminus2 = { + 9, 26, 20, 17, 6, 110, 6, 197, 0, 2 * 3600, (char*)"GMT-2" + }; + test_localtime("GMT-2", testTime, gtmminus2); + test_gmtime("GMT-2", testTime, gtm); + gtmminus2.tm_wday = -1; + gtmminus2.tm_yday = -1; + test_mktime("GMT-2", gtmminus2, testTime, 6, 197); + tm btm = { 9, 26, 20, 17, 6, 110, 6, 197, 1, 2 * 3600, (char*)"CEST" };