* 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:
@@ -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"
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user