* 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;
TimeZone* fTimeZone;
char fTimeZoneID[B_FILE_NAME_LENGTH];
};
@@ -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 <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 {
// 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<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 = 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<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 @@ ICUTimeConversion::Gmtime(const time_t* inTime, struct tm* tmOut)
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 @@ 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);
}
@@ -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"
};