* 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

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@40788 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Oliver Tappe
2011-03-03 00:34:51 +00:00
parent 548836e4f8
commit e8226ce48d
3 changed files with 74 additions and 37 deletions
@@ -42,6 +42,7 @@ private:
TimeConversionDataBridge* fDataBridge; TimeConversionDataBridge* fDataBridge;
TimeZone* fTimeZone;
char fTimeZoneID[B_FILE_NAME_LENGTH]; char fTimeZoneID[B_FILE_NAME_LENGTH];
}; };
@@ -21,7 +21,8 @@ namespace Libroot {
ICUTimeConversion::ICUTimeConversion(const ICUTimeData& timeData) ICUTimeConversion::ICUTimeConversion(const ICUTimeData& timeData)
: :
fTimeData(timeData), fTimeData(timeData),
fDataBridge(NULL) fDataBridge(NULL),
fTimeZone(NULL)
{ {
fTimeZoneID[0] = '\0'; fTimeZoneID[0] = '\0';
} }
@@ -29,6 +30,7 @@ ICUTimeConversion::ICUTimeConversion(const ICUTimeData& timeData)
ICUTimeConversion::~ICUTimeConversion() ICUTimeConversion::~ICUTimeConversion()
{ {
delete fTimeZone;
} }
@@ -42,6 +44,8 @@ ICUTimeConversion::Initialize(TimeConversionDataBridge* dataBridge)
status_t status_t
ICUTimeConversion::TZSet(const char* timeZoneID, const char* tz) ICUTimeConversion::TZSet(const char* timeZoneID, const char* tz)
{ {
bool offsetHasBeenSet = false;
// The given TZ environment variable's content overrides the default // The given TZ environment variable's content overrides the default
// system timezone. // system timezone.
if (tz != NULL) { if (tz != NULL) {
@@ -54,19 +58,31 @@ ICUTimeConversion::TZSet(const char* timeZoneID, const char* tz)
strlcpy(fTimeZoneID, tz + 1, sizeof(fTimeZoneID)); strlcpy(fTimeZoneID, tz + 1, sizeof(fTimeZoneID));
} else { } else {
// We ignore anything following the timezone name, as those values // note timezone name
// are determined by the corresponding ICU-timezone (and glibc's strlcpy(fTimeZoneID, tz, sizeof(fTimeZoneID));
// 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 // nothing to do if the given name matches the current timezone
if (strcasecmp(fTimeZoneID, fDataBridge->addrOfTZName[0]) == 0) if (strcasecmp(fTimeZoneID, fDataBridge->addrOfTZName[0]) == 0)
return B_OK; 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));
*fDataBridge->addrOfTimezone = (*tzNameEnd == '-' ? -1 : 1)
* (hours * 3600 + minutes * 60 + seconds);
offsetHasBeenSet = true;
}
} }
} else { } else {
// nothing to do if the given name matches the current timezone // 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)); strlcpy(fTimeZoneID, timeZoneID, sizeof(fTimeZoneID));
} }
ObjectDeleter<TimeZone> icuTimeZone = TimeZone::createTimeZone(fTimeZoneID); delete fTimeZone;
if (icuTimeZone.Get() == NULL) fTimeZone = TimeZone::createTimeZone(fTimeZoneID);
if (fTimeZone == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
int32_t rawOffset; if (offsetHasBeenSet) {
int32_t dstOffset; fTimeZone->setRawOffset(*fDataBridge->addrOfTimezone * -1 * 1000);
UDate nowMillis = 1000 * (UDate)time(NULL); } else {
UErrorCode icuStatus = U_ZERO_ERROR; int32_t rawOffset;
icuTimeZone->getOffset(nowMillis, FALSE, rawOffset, dstOffset, icuStatus); int32_t dstOffset;
if (!U_SUCCESS(icuStatus)) { UDate nowMillis = 1000 * (UDate)time(NULL);
*fDataBridge->addrOfTimezone = 0; UErrorCode icuStatus = U_ZERO_ERROR;
*fDataBridge->addrOfDaylight = false; fTimeZone->getOffset(nowMillis, FALSE, rawOffset, dstOffset, icuStatus);
strcpy(fDataBridge->addrOfTZName[0], "GMT"); if (!U_SUCCESS(icuStatus)) {
strcpy(fDataBridge->addrOfTZName[1], "GMT"); *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; *fDataBridge->addrOfDaylight = fTimeZone->useDaylightTime();
// we want seconds, not the ms that ICU gives us
*fDataBridge->addrOfDaylight = icuTimeZone->useDaylightTime();
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
if (tz != NULL && *tz != ':' && i == 0) { if (tz != NULL && *tz != ':' && i == 0) {
strcpy(fDataBridge->addrOfTZName[0], fTimeZoneID); strcpy(fDataBridge->addrOfTZName[0], fTimeZoneID);
} else { } else {
UnicodeString icuString; UnicodeString icuString;
icuTimeZone->getDisplayName(i == 1, TimeZone::SHORT_COMMONLY_USED, fTimeZone->getDisplayName(i == 1, TimeZone::SHORT_COMMONLY_USED,
fTimeData.ICULocale(), icuString); fTimeData.ICULocale(), icuString);
CheckedArrayByteSink byteSink(fDataBridge->addrOfTZName[i], CheckedArrayByteSink byteSink(fDataBridge->addrOfTZName[i],
sizeof(fTimeZoneID)); sizeof(fTimeZoneID));
@@ -123,12 +143,11 @@ ICUTimeConversion::TZSet(const char* timeZoneID, const char* tz)
status_t status_t
ICUTimeConversion::Localtime(const time_t* inTime, struct tm* tmOut) ICUTimeConversion::Localtime(const time_t* inTime, struct tm* tmOut)
{ {
ObjectDeleter<TimeZone> icuTimeZone = TimeZone::createTimeZone(fTimeZoneID); if (fTimeZone == NULL)
if (icuTimeZone.Get() == NULL) return B_NO_INIT;
return B_NO_MEMORY;
tmOut->tm_zone = fTimeZoneID; 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 status_t
ICUTimeConversion::Mktime(struct tm* inOutTm, time_t& timeOut) ICUTimeConversion::Mktime(struct tm* inOutTm, time_t& timeOut)
{ {
ObjectDeleter<TimeZone> icuTimeZone = TimeZone::createTimeZone(fTimeZoneID); if (fTimeZone == NULL)
if (icuTimeZone.Get() == NULL) return B_NO_INIT;
return B_NO_MEMORY;
UErrorCode icuStatus = U_ZERO_ERROR; UErrorCode icuStatus = U_ZERO_ERROR;
GregorianCalendar calendar(*icuTimeZone.Get(), fTimeData.ICULocale(), GregorianCalendar calendar(*fTimeZone, fTimeData.ICULocale(),
icuStatus); icuStatus);
if (!U_SUCCESS(icuStatus)) if (!U_SUCCESS(icuStatus))
return B_ERROR; return B_ERROR;
@@ -164,7 +182,7 @@ ICUTimeConversion::Mktime(struct tm* inOutTm, time_t& timeOut)
return B_ERROR; return B_ERROR;
timeOut = (time_t)((int64_t)timeInMillis / 1000); timeOut = (time_t)((int64_t)timeInMillis / 1000);
return _FillTmValues(icuTimeZone.Get(), &timeOut, inOutTm); return _FillTmValues(fTimeZone, &timeOut, inOutTm);
} }
@@ -1820,6 +1820,24 @@ test_timeconversions()
gtm.tm_yday = -1; gtm.tm_yday = -1;
test_mktime("GMT", gtm, testTime, 6, 197); 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 = { tm btm = {
9, 26, 20, 17, 6, 110, 6, 197, 1, 2 * 3600, (char*)"CEST" 9, 26, 20, 17, 6, 110, 6, 197, 1, 2 * 3600, (char*)"CEST"
}; };