Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ICU-13827 Clean up ICU4C "wintz.cpp" time zone detection code. #129

Merged
merged 1 commit into from
Sep 19, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 49 additions & 51 deletions icu4c/source/common/wintz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

#include "unicode/ures.h"
#include "unicode/ustring.h"
#include "uresimp.h"

#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
Expand All @@ -32,97 +33,94 @@
# define NOMCX
#include <windows.h>

U_NAMESPACE_BEGIN

// The value of MAX_TIMEZONE_ID_LENGTH is 128, which is defined in DYNAMIC_TIME_ZONE_INFORMATION
#define MAX_TIMEZONE_ID_LENGTH 128

/**
* Main Windows time zone detection function. Returns the Windows
* time zone, translated to an ICU time zone, or nullptr upon failure.
* It is calling GetDynamicTimeZoneInformation to get the current time zone info.
* The API returns non-localized time zone name so it can be used for mapping ICU time zone name.
* Main Windows time zone detection function.
* Returns the Windows time zone converted to an ICU time zone as a heap-allocated buffer, or nullptr upon failure.
* Note: We use the Win32 API GetDynamicTimeZoneInformation to get the current time zone info.
* This API returns a non-localized time zone name, which we can then map to an ICU time zone name.
*/
U_CFUNC const char* U_EXPORT2
uprv_detectWindowsTimeZone()
{
UErrorCode status = U_ZERO_ERROR;
UResourceBundle* bundle = nullptr;
char* icuid = nullptr;
char dynamicTZKeyName[MAX_TIMEZONE_ID_LENGTH] = {};
char tmpid[MAX_TIMEZONE_ID_LENGTH] = {};
char dynamicTZKeyName[MAX_TIMEZONE_ID_LENGTH];
char tmpid[MAX_TIMEZONE_ID_LENGTH];
int32_t len;
int id;
int id = GEOID_NOT_AVAILABLE;
int errorCode;
wchar_t ISOcodeW[3] = {}; /* 2 letter iso code in UTF-16*/
char ISOcodeA[3] = {}; /* 2 letter iso code in ansi */
wchar_t ISOcodeW[3] = {}; /* 2 letter ISO code in UTF-16 */
char ISOcode[3] = {}; /* 2 letter ISO code in UTF-8 */

DYNAMIC_TIME_ZONE_INFORMATION dynamicTZI;
uprv_memset(&dynamicTZI, 0, sizeof(dynamicTZI));
uprv_memset(dynamicTZKeyName, 0, sizeof(dynamicTZKeyName));
uprv_memset(tmpid, 0, sizeof(tmpid));

/* Obtain TIME_ZONE_INFORMATION from the API and get the non-localized time zone name. */
uprv_memset(&dynamicTZI, 0, sizeof(dynamicTZI));
if (TIME_ZONE_ID_INVALID == GetDynamicTimeZoneInformation(&dynamicTZI))
{
if (TIME_ZONE_ID_INVALID == GetDynamicTimeZoneInformation(&dynamicTZI)) {
return nullptr;
}

tmpid[0] = 0;

id = GetUserGeoID(GEOCLASS_NATION);
errorCode = GetGeoInfoW(id, GEO_ISO2, ISOcodeW, 3, 0);
u_strToUTF8(ISOcodeA, 3, nullptr, (const UChar *)ISOcodeW, 3, &status);

bundle = ures_openDirect(nullptr, "windowsZones", &status);
ures_getByKey(bundle, "mapTimezones", bundle, &status);
// convert from wchar_t* (UTF-16 on Windows) to char* (UTF-8).
u_strToUTF8(ISOcode, UPRV_LENGTHOF(ISOcode), nullptr,
reinterpret_cast<const UChar*>(ISOcodeW), UPRV_LENGTHOF(ISOcodeW), &status);

/* Convert the wchar_t* standard name to char* */
uprv_memset(dynamicTZKeyName, 0, sizeof(dynamicTZKeyName));
u_strToUTF8(dynamicTZKeyName, MAX_TIMEZONE_ID_LENGTH, nullptr, (const UChar *)dynamicTZI.TimeZoneKeyName, MAX_TIMEZONE_ID_LENGTH, &status);
LocalUResourceBundlePointer bundle(ures_openDirect(nullptr, "windowsZones", &status));
ures_getByKey(bundle.getAlias(), "mapTimezones", bundle.getAlias(), &status);

// convert from wchar_t* (UTF-16 on Windows) to char* (UTF-8).
u_strToUTF8(dynamicTZKeyName, UPRV_LENGTHOF(dynamicTZKeyName), nullptr,
reinterpret_cast<const UChar*>(dynamicTZI.TimeZoneKeyName), UPRV_LENGTHOF(dynamicTZI.TimeZoneKeyName), &status);

if (dynamicTZI.TimeZoneKeyName[0] != 0)
{
UResourceBundle* winTZ = ures_getByKey(bundle, dynamicTZKeyName, nullptr, &status);
if (U_SUCCESS(status))
{
if (U_FAILURE(status)) {
return nullptr;
}

if (dynamicTZI.TimeZoneKeyName[0] != 0) {
UResourceBundle winTZ;
ures_initStackObject(&winTZ);
ures_getByKey(bundle.getAlias(), dynamicTZKeyName, &winTZ, &status);

if (U_SUCCESS(status)) {
const UChar* icuTZ = nullptr;
if (errorCode != 0)
{
icuTZ = ures_getStringByKey(winTZ, ISOcodeA, &len, &status);
if (errorCode != 0) {
icuTZ = ures_getStringByKey(&winTZ, ISOcode, &len, &status);
}
if (errorCode == 0 || icuTZ == nullptr)
{
if (errorCode == 0 || icuTZ == nullptr) {
/* fallback to default "001" and reset status */
status = U_ZERO_ERROR;
icuTZ = ures_getStringByKey(winTZ, "001", &len, &status);
icuTZ = ures_getStringByKey(&winTZ, "001", &len, &status);
}

if (U_SUCCESS(status))
{
if (U_SUCCESS(status)) {
int index = 0;
while (!(*icuTZ == '\0' || *icuTZ == ' '))
{
tmpid[index++] = (char)(*icuTZ++); /* safe to assume 'char' is ASCII compatible on windows */

while (!(*icuTZ == '\0' || *icuTZ == ' ')) {
// time zone IDs only contain ASCII invariant characters.
tmpid[index++] = (char)(*icuTZ++);
}
tmpid[index] = '\0';
}
}
ures_close(winTZ);
ures_close(&winTZ);
}

/*
* Copy the timezone ID to icuid to be returned.
*/
if (tmpid[0] != 0)
{
len = static_cast<int32_t>(uprv_strlen(tmpid));
icuid = (char*)uprv_calloc(len + 1, sizeof(char));
if (icuid != nullptr)
{
uprv_strcpy(icuid, tmpid);
}
// Copy the timezone ID to icuid to be returned.
if (tmpid[0] != 0) {
icuid = uprv_strdup(tmpid);
}

ures_close(bundle);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we need to close the new bundle pointer?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the neat thing about LocalUResourceBundlePointer, it will automatically call ures_close when the object goes out of scope. So we don't have to worry about remembering to call close. :)


return icuid;
}

U_NAMESPACE_END
#endif /* U_PLATFORM_HAS_WIN32_API */