Skip to content

Commit

Permalink
[winpr,timezone] refactor timezone mapping
Browse files Browse the repository at this point in the history
* update windowsZones.xml update url
* add option to use ICU as a fallback mapping (eliminate need for
  WindowsZones mapping list)
* extract timezone related settings from localtime_r and eliminate the
  need to parse the complex TimeZones table
* Add new TimeZoneNameMap to map IANA to windows names (Id, Stnadard,
  Daylight and Display names)
* Implement GetDynamicTimeZoneInformation
  • Loading branch information
akallabeth committed Apr 25, 2024
1 parent f5548cb commit 4d9ef46
Show file tree
Hide file tree
Showing 8 changed files with 713 additions and 4,300 deletions.
4 changes: 4 additions & 0 deletions winpr/include/winpr/timezone.h
Expand Up @@ -57,6 +57,10 @@ extern "C"
} DYNAMIC_TIME_ZONE_INFORMATION, *PDYNAMIC_TIME_ZONE_INFORMATION,
*LPDYNAMIC_TIME_ZONE_INFORMATION;

#define TIME_ZONE_ID_UNKNOWN 0
#define TIME_ZONE_ID_STANDARD 1
#define TIME_ZONE_ID_DAYLIGHT 2

WINPR_API DWORD GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation);
WINPR_API BOOL SetTimeZoneInformation(const TIME_ZONE_INFORMATION* lpTimeZoneInformation);
WINPR_API BOOL SystemTimeToFileTime(const SYSTEMTIME* lpSystemTime, LPFILETIME lpFileTime);
Expand Down
26 changes: 25 additions & 1 deletion winpr/libwinpr/timezone/CMakeLists.txt
Expand Up @@ -15,4 +15,28 @@
# See the License for the specific language governing permissions and
# limitations under the License.

winpr_module_add(timezone.c TimeZones.c WindowsZones.c TimeZones.h WindowsZones.h)
set(SRCS
TimeZoneNameMap.c
TimeZoneNameMap.h
TimeZoneNameMapUtils.c
)
if (NOT WIN32)
list(APPEND SRCS
timezone.c
)
endif()

option(WITH_TIMEZONE_ICU "Use ICU for improved timezone mapping" OFF)
if (WITH_TIMEZONE_ICU)
find_package(ICU COMPONENTS i18n uc REQUIRED)
winpr_include_directory_add(${ICU_INCLUDE_DIRS})
winpr_library_add_private(${ICU_LIBRARIES})
winpr_definition_add(-DWITH_TIMEZONE_ICU)
else()
list(APPEND SRCS
WindowsZones.c
WindowsZones.h
)
endif()

winpr_module_add(${SRCS})
289 changes: 289 additions & 0 deletions winpr/libwinpr/timezone/TimeZoneNameMap.c

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions winpr/libwinpr/timezone/TimeZoneNameMap.h
@@ -0,0 +1,30 @@

#ifndef WINPR_TIME_NAME_MAP_H_
#define WINPR_TIME_NAME_MAP_H_

#include <winpr/wtypes.h>

typedef enum
{
TIME_ZONE_NAME_ID,
TIME_ZONE_NAME_STANDARD,
TIME_ZONE_NAME_DISPLAY,
TIME_ZONE_NAME_DAYLIGHT,
TIME_ZONE_NAME_IANA,
} TimeZoneNameType;

typedef struct
{
const char* Id;
const char* StandardName;
const char* DisplayName;
const char* DaylightName;
const char* Iana;
} TimeZoneNameMapEntry;

extern const TimeZoneNameMapEntry TimeZoneNameMap[];
extern const size_t TimeZoneNameMapSize;

const char* TimeZoneIanaToWindows(const char* iana, TimeZoneNameType type);

#endif
130 changes: 130 additions & 0 deletions winpr/libwinpr/timezone/TimeZoneNameMapUtils.c
@@ -0,0 +1,130 @@
#include <winpr/config.h>
#include <winpr/assert.h>
#include <winpr/string.h>

#include <string.h>

#if defined(WITH_TIMEZONE_ICU)
#include <unicode/ucal.h>
#else
#include "WindowsZones.h"
#endif

#include "TimeZoneNameMap.h"

static const char* return_type(const TimeZoneNameMapEntry* entry, TimeZoneNameType type)
{
WINPR_ASSERT(entry);
switch (type)
{
case TIME_ZONE_NAME_IANA:
return entry->Iana;
case TIME_ZONE_NAME_ID:
return entry->Id;
case TIME_ZONE_NAME_STANDARD:
return entry->StandardName;
case TIME_ZONE_NAME_DISPLAY:
return entry->DisplayName;
case TIME_ZONE_NAME_DAYLIGHT:
return entry->DaylightName;
default:
return NULL;
}
}

#if defined(WITH_TIMEZONE_ICU)
static char* get_wzid_icu(const UChar* utzid, size_t utzid_len)
{
char* res = NULL;
UErrorCode error = U_ZERO_ERROR;

int32_t rc = ucal_getWindowsTimeZoneID(utzid, utzid_len, NULL, 0, &error);
if ((error == U_BUFFER_OVERFLOW_ERROR) && (rc > 0))
{
rc++; // make space for '\0'
UChar* wzid = calloc((size_t)rc + 1, sizeof(UChar));
if (wzid)
{
UErrorCode error2 = U_ZERO_ERROR;
int32_t rc2 = ucal_getWindowsTimeZoneID(utzid, utzid_len, wzid, rc, &error2);
if (U_SUCCESS(error2) && (rc2 > 0))
res = ConvertWCharNToUtf8Alloc(wzid, (size_t)rc, NULL);
free(wzid);
}
}
return res;
}

static char* get(const char* iana)
{
size_t utzid_len = 0;
UChar* utzid = ConvertUtf8ToWCharAlloc(iana, &utzid_len);
if (!utzid)
return NULL;

char* wzid = get_wzid_icu(utzid, utzid_len);
free(utzid);
return wzid;
}

static const char* map_fallback(const char* iana, TimeZoneNameType type)
{
const char* res = NULL;
char* wzid = get(iana);
if (!wzid)
return NULL;

for (size_t x = 0; x < TimeZoneNameMapSize; x++)
{
const TimeZoneNameMapEntry* entry = &TimeZoneNameMap[x];
if (strcmp(wzid, entry->Id) == 0)
{
res = return_type(entry, type);
break;
}
}

free(wzid);
return res;
}
#else
static const char* map_fallback(const char* iana, TimeZoneNameType type)
{
if (!iana)
return NULL;

for (size_t x = 0; x < WindowsTimeZoneIdTableNrElements; x++)
{
const WINDOWS_TZID_ENTRY* const entry = &WindowsTimeZoneIdTable[x];
if (strcmp(entry->tzid, iana) == 0)
return entry->windows;
}

return NULL;
}
#endif

const char* TimeZoneIanaToWindows(const char* iana, TimeZoneNameType type)
{
if (!iana)
return NULL;

for (size_t x = 0; x < TimeZoneNameMapSize; x++)
{
const TimeZoneNameMapEntry* entry = &TimeZoneNameMap[x];
if (strcmp(iana, entry->Iana) == 0)
return return_type(entry, type);
}

const char* wzid = map_fallback(iana, type);
if (!wzid)
return NULL;

for (size_t x = 0; x < TimeZoneNameMapSize; x++)
{
const TimeZoneNameMapEntry* entry = &TimeZoneNameMap[x];
if (strcmp(wzid, entry->Id) == 0)
return return_type(entry, type);
}
return NULL;
}

0 comments on commit 4d9ef46

Please sign in to comment.