Author: zooey Date: 2011-03-03 01:34:51 +0100 (Thu, 03 Mar 2011) New Revision: 40788 Changeset: http://dev.haiku-os.org/changeset/40788 Modified: haiku/trunk/headers/private/libroot/locale/ICUTimeConversion.h haiku/trunk/src/system/libroot/add-ons/icu/ICUTimeConversion.cpp haiku/trunk/src/tests/system/libroot/posix/locale_test.cpp Log: * fix support for influencing tzset() via TZ environment variable (now at least <std> and <offset> 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 Modified: haiku/trunk/headers/private/libroot/locale/ICUTimeConversion.h =================================================================== --- haiku/trunk/headers/private/libroot/locale/ICUTimeConversion.h 2011-03-02 18:58:14 UTC (rev 40787) +++ haiku/trunk/headers/private/libroot/locale/ICUTimeConversion.h 2011-03-03 00:34:51 UTC (rev 40788) @@ -42,6 +42,7 @@ TimeConversionDataBridge* fDataBridge; + TimeZone* fTimeZone; char fTimeZoneID[B_FILE_NAME_LENGTH]; }; Modified: haiku/trunk/src/system/libroot/add-ons/icu/ICUTimeConversion.cpp =================================================================== --- haiku/trunk/src/system/libroot/add-ons/icu/ICUTimeConversion.cpp 2011-03-02 18:58:14 UTC (rev 40787) +++ haiku/trunk/src/system/libroot/add-ons/icu/ICUTimeConversion.cpp 2011-03-03 00:34:51 UTC (rev 40788) @@ -21,7 +21,8 @@ ICUTimeConversion::ICUTimeConversion(const ICUTimeData& timeData) : fTimeData(timeData), - fDataBridge(NULL) + fDataBridge(NULL), + fTimeZone(NULL) { fTimeZoneID[0] = '\0'; } @@ -29,6 +30,7 @@ ICUTimeConversion::~ICUTimeConversion() { + delete fTimeZone; } @@ -42,6 +44,8 @@ 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 @@ 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). + // 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 <std> and <offset> 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)); - 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; + *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 @@ strlcpy(fTimeZoneID, timeZoneID, sizeof(fTimeZoneID)); } - ObjectDeleter<TimeZone> 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 = fTimeZone->useDaylightTime(); - *fDataBridge->addrOfDaylight = icuTimeZone->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 @@ status_t ICUTimeConversion::Localtime(const time_t* inTime, struct tm* tmOut) { - ObjectDeleter<TimeZone> 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 @@ status_t ICUTimeConversion::Mktime(struct tm* inOutTm, time_t& timeOut) { - ObjectDeleter<TimeZone> 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 @@ return B_ERROR; timeOut = (time_t)((int64_t)timeInMillis / 1000); - return _FillTmValues(icuTimeZone.Get(), &timeOut, inOutTm); + return _FillTmValues(fTimeZone, &timeOut, inOutTm); } Modified: haiku/trunk/src/tests/system/libroot/posix/locale_test.cpp =================================================================== --- haiku/trunk/src/tests/system/libroot/posix/locale_test.cpp 2011-03-02 18:58:14 UTC (rev 40787) +++ haiku/trunk/src/tests/system/libroot/posix/locale_test.cpp 2011-03-03 00:34:51 UTC (rev 40788) @@ -1820,6 +1820,24 @@ 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" };