mirror of https://github.com/FreeRDP/FreeRDP
[winpr,timezone] add IANA to short mapping
* Add a generator for IANA and short zone name mappings. * Add a function to get all IANA mappings for a short zone name.
This commit is contained in:
parent
52a884ed2e
commit
cd18532f45
|
@ -22,7 +22,9 @@ set(SRCS
|
|||
)
|
||||
if (NOT WIN32)
|
||||
list(APPEND SRCS
|
||||
timezone.c
|
||||
TimeZoneIanaAbbrevMap.c
|
||||
TimeZoneIanaAbbrevMap.h
|
||||
timezone.c
|
||||
)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -0,0 +1,268 @@
|
|||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Time Zone
|
||||
*
|
||||
* Copyright 2024 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2024 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "TimeZoneIanaAbbrevMap.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <winpr/string.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* Iana;
|
||||
const char* Abbrev;
|
||||
} TimeZoneInanaAbbrevMapEntry;
|
||||
|
||||
const static char* zonepath = "/usr/share/zoneinfo";
|
||||
|
||||
static TimeZoneInanaAbbrevMapEntry* TimeZoneIanaAbbrevMap = NULL;
|
||||
static size_t TimeZoneIanaAbbrevMapSize = 0;
|
||||
|
||||
static void append(const char* iana, const char* sname)
|
||||
{
|
||||
const size_t size = TimeZoneIanaAbbrevMapSize + 1;
|
||||
|
||||
TimeZoneInanaAbbrevMapEntry* tmp =
|
||||
realloc(TimeZoneIanaAbbrevMap, size * sizeof(TimeZoneInanaAbbrevMapEntry));
|
||||
if (!tmp)
|
||||
return;
|
||||
TimeZoneIanaAbbrevMap = tmp;
|
||||
TimeZoneIanaAbbrevMapSize = size;
|
||||
|
||||
TimeZoneInanaAbbrevMapEntry* cur = &TimeZoneIanaAbbrevMap[size - 1];
|
||||
cur->Abbrev = _strdup(sname);
|
||||
cur->Iana = _strdup(iana);
|
||||
}
|
||||
|
||||
static void append_timezone(const char* dir, const char* name)
|
||||
{
|
||||
char* tz = NULL;
|
||||
if (!dir && !name)
|
||||
return;
|
||||
if (!dir)
|
||||
{
|
||||
size_t len = 0;
|
||||
winpr_asprintf(&tz, &len, "%s", name);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t len = 0;
|
||||
winpr_asprintf(&tz, &len, "%s/%s", dir, name);
|
||||
}
|
||||
if (!tz)
|
||||
return;
|
||||
|
||||
const char* otz = getenv("TZ");
|
||||
char* oldtz = NULL;
|
||||
if (otz)
|
||||
oldtz = _strdup(otz);
|
||||
setenv("TZ", tz, 1);
|
||||
tzset();
|
||||
const time_t t = time(NULL);
|
||||
struct tm lt = { 0 };
|
||||
localtime_r(&t, <);
|
||||
append(tz, lt.tm_zone);
|
||||
if (oldtz)
|
||||
{
|
||||
setenv("TZ", oldtz, 1);
|
||||
free(oldtz);
|
||||
}
|
||||
else
|
||||
unsetenv("TZ");
|
||||
free(tz);
|
||||
}
|
||||
|
||||
static void handle_link(const char* dir, const char* name, const char* base);
|
||||
|
||||
static char* topath(const char* base, const char* bname, const char* name)
|
||||
{
|
||||
size_t plen = 0;
|
||||
char* path = NULL;
|
||||
|
||||
if (!base && !bname && !name)
|
||||
return NULL;
|
||||
|
||||
if (!base && !name)
|
||||
return _strdup(bname);
|
||||
|
||||
if (!bname && !name)
|
||||
return _strdup(base);
|
||||
|
||||
if (!base && !bname)
|
||||
return _strdup(bname);
|
||||
|
||||
if (!base)
|
||||
winpr_asprintf(&path, &plen, "%s/%s", bname, name);
|
||||
else if (!bname)
|
||||
winpr_asprintf(&path, &plen, "%s/%s", base, name);
|
||||
else if (!name)
|
||||
winpr_asprintf(&path, &plen, "%s/%s", base, bname);
|
||||
else
|
||||
winpr_asprintf(&path, &plen, "%s/%s/%s", base, bname, name);
|
||||
return path;
|
||||
}
|
||||
|
||||
static void iterate_subdir_recursive(const char* base, const char* bname, const char* name)
|
||||
{
|
||||
char* path = topath(base, bname, name);
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
struct DIR* d = opendir(path);
|
||||
if (d)
|
||||
{
|
||||
struct dirent* dp = NULL;
|
||||
while ((dp = readdir(d)) != NULL)
|
||||
{
|
||||
switch (dp->d_type)
|
||||
{
|
||||
case DT_DIR:
|
||||
{
|
||||
if (strcmp(dp->d_name, ".") == 0)
|
||||
continue;
|
||||
if (strcmp(dp->d_name, "..") == 0)
|
||||
continue;
|
||||
iterate_subdir_recursive(path, dp->d_name, NULL);
|
||||
}
|
||||
break;
|
||||
case DT_LNK:
|
||||
handle_link(base, bname, dp->d_name);
|
||||
break;
|
||||
case DT_REG:
|
||||
append_timezone(bname, dp->d_name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
|
||||
static char* get_link_target(const char* base, const char* dir, const char* name)
|
||||
{
|
||||
char* apath = NULL;
|
||||
char* path = topath(base, dir, name);
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
SSIZE_T rc = -1;
|
||||
size_t size = 0;
|
||||
char* target = NULL;
|
||||
do
|
||||
{
|
||||
size += 64;
|
||||
char* tmp = realloc(target, size + 1);
|
||||
if (!tmp)
|
||||
goto fail;
|
||||
|
||||
target = tmp;
|
||||
|
||||
memset(target, 0, size + 1);
|
||||
rc = readlink(path, target, size);
|
||||
if (rc < 0)
|
||||
goto fail;
|
||||
} while (rc >= size);
|
||||
|
||||
apath = topath(base, dir, target);
|
||||
fail:
|
||||
free(target);
|
||||
free(path);
|
||||
return apath;
|
||||
}
|
||||
|
||||
void handle_link(const char* base, const char* dir, const char* name)
|
||||
{
|
||||
int isDir = -1;
|
||||
|
||||
char* target = get_link_target(base, dir, name);
|
||||
if (target)
|
||||
{
|
||||
struct stat s = { 0 };
|
||||
const int rc3 = stat(target, &s);
|
||||
if (rc3 == 0)
|
||||
isDir = S_ISDIR(s.st_mode);
|
||||
|
||||
free(target);
|
||||
}
|
||||
|
||||
switch (isDir)
|
||||
{
|
||||
case 1:
|
||||
iterate_subdir_recursive(base, dir, name);
|
||||
break;
|
||||
case 0:
|
||||
append_timezone(dir, name);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void TimeZoneIanaAbbrevCleanup(void)
|
||||
{
|
||||
if (!TimeZoneIanaAbbrevMap)
|
||||
return;
|
||||
|
||||
for (size_t x = 0; x < TimeZoneIanaAbbrevMapSize; x++)
|
||||
{
|
||||
TimeZoneInanaAbbrevMapEntry* entry = &TimeZoneIanaAbbrevMap[x];
|
||||
free(entry->Iana);
|
||||
free(entry->Abbrev);
|
||||
}
|
||||
free(TimeZoneIanaAbbrevMap);
|
||||
TimeZoneIanaAbbrevMap = NULL;
|
||||
TimeZoneIanaAbbrevMapSize = 0;
|
||||
}
|
||||
|
||||
static void TimeZoneIanaAbbrevInitialize(void)
|
||||
{
|
||||
static BOOL initialized = FALSE;
|
||||
if (initialized)
|
||||
return;
|
||||
|
||||
iterate_subdir_recursive(zonepath, NULL, NULL);
|
||||
atexit(TimeZoneIanaAbbrevCleanup);
|
||||
initialized = TRUE;
|
||||
}
|
||||
|
||||
size_t TimeZoneIanaAbbrevGet(const char* abbrev, const char** list, size_t listsize)
|
||||
{
|
||||
TimeZoneIanaAbbrevInitialize();
|
||||
|
||||
size_t rc = 0;
|
||||
for (size_t x = 0; x < TimeZoneIanaAbbrevMapSize; x++)
|
||||
{
|
||||
const TimeZoneInanaAbbrevMapEntry* entry = &TimeZoneIanaAbbrevMap[x];
|
||||
if (strcmp(abbrev, entry->Abbrev) == 0)
|
||||
{
|
||||
if (listsize > rc)
|
||||
list[rc] = entry->Iana;
|
||||
rc++;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Time Zone
|
||||
*
|
||||
* Copyright 2024 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2024 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef WINPR_TIMEZONE_IANA_ABBREV
|
||||
#define WINPR_TIMEZONE_IANA_ABBREV
|
||||
|
||||
#include <winpr/wtypes.h>
|
||||
|
||||
/**! \brief returns a list of IANA names for a short timezone name
|
||||
*
|
||||
* \param abbrev The short name to look for
|
||||
* \param list The list to hold the const IANA names
|
||||
* \param listsize The size of the \b list. Set to 0 to only get the required size.
|
||||
*
|
||||
* \return The number of mappings found
|
||||
*/
|
||||
size_t TimeZoneIanaAbbrevGet(const char* abbrev, const char** list, size_t listsize);
|
||||
|
||||
#endif
|
|
@ -3,6 +3,8 @@
|
|||
* Time Zone
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2024 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2024 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -34,6 +36,7 @@
|
|||
#endif
|
||||
|
||||
#include "TimeZoneNameMap.h"
|
||||
#include "TimeZoneIanaAbbrevMap.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
|
@ -465,8 +468,25 @@ static LONG get_bias(const struct tm* start, BOOL dstBias)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static BOOL map_iana_id(const char* iana, LPTIME_ZONE_INFORMATION tz)
|
||||
{
|
||||
const char* winId = TimeZoneIanaToWindows(iana, TIME_ZONE_NAME_ID);
|
||||
if (!winId)
|
||||
return FALSE;
|
||||
|
||||
const char* winStd = TimeZoneIanaToWindows(iana, TIME_ZONE_NAME_STANDARD);
|
||||
const char* winDst = TimeZoneIanaToWindows(iana, TIME_ZONE_NAME_DAYLIGHT);
|
||||
|
||||
ConvertUtf8ToWChar(winStd, tz->StandardName, ARRAYSIZE(tz->StandardName));
|
||||
ConvertUtf8ToWChar(winDst, tz->DaylightName, ARRAYSIZE(tz->DaylightName));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
DWORD GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation)
|
||||
{
|
||||
const char** list = NULL;
|
||||
char* tzid = NULL;
|
||||
const char* defaultName = "Client Local Time";
|
||||
DWORD res = TIME_ZONE_ID_UNKNOWN;
|
||||
const TIME_ZONE_INFORMATION empty = { 0 };
|
||||
|
@ -477,8 +497,6 @@ DWORD GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation)
|
|||
*tz = empty;
|
||||
ConvertUtf8ToWChar(defaultName, tz->StandardName, ARRAYSIZE(tz->StandardName));
|
||||
|
||||
char* tzid = winpr_guess_time_zone();
|
||||
|
||||
const time_t t = time(NULL);
|
||||
struct tm tres = { 0 };
|
||||
struct tm* local_time = localtime_r(&t, &tres);
|
||||
|
@ -495,22 +513,30 @@ DWORD GetTimeZoneInformation(LPTIME_ZONE_INFORMATION lpTimeZoneInformation)
|
|||
get_transition_date(local_time, TRUE, &tz->DaylightDate);
|
||||
}
|
||||
|
||||
ConvertUtf8ToWChar(local_time->tm_zone, tz->StandardName, ARRAYSIZE(tz->StandardName));
|
||||
|
||||
const char* winId = TimeZoneIanaToWindows(tzid, TIME_ZONE_NAME_ID);
|
||||
if (winId)
|
||||
tzid = winpr_guess_time_zone();
|
||||
if (!map_iana_id(tzid, tz))
|
||||
{
|
||||
const char* winStd = TimeZoneIanaToWindows(tzid, TIME_ZONE_NAME_STANDARD);
|
||||
const char* winDst = TimeZoneIanaToWindows(tzid, TIME_ZONE_NAME_DAYLIGHT);
|
||||
|
||||
ConvertUtf8ToWChar(winStd, tz->StandardName, ARRAYSIZE(tz->StandardName));
|
||||
ConvertUtf8ToWChar(winDst, tz->DaylightName, ARRAYSIZE(tz->DaylightName));
|
||||
|
||||
res = (local_time->tm_isdst) ? TIME_ZONE_ID_DAYLIGHT : TIME_ZONE_ID_STANDARD;
|
||||
const size_t len = TimeZoneIanaAbbrevGet(local_time->tm_zone, NULL, 0);
|
||||
list = calloc(len, sizeof(char*));
|
||||
if (!list)
|
||||
goto out_error;
|
||||
const size_t size = TimeZoneIanaAbbrevGet(local_time->tm_zone, list, len);
|
||||
for (size_t x = 0; x < size; x++)
|
||||
{
|
||||
const char* id = list[x];
|
||||
if (map_iana_id(id, tz))
|
||||
{
|
||||
res = (local_time->tm_isdst) ? TIME_ZONE_ID_DAYLIGHT : TIME_ZONE_ID_STANDARD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
res = (local_time->tm_isdst) ? TIME_ZONE_ID_DAYLIGHT : TIME_ZONE_ID_STANDARD;
|
||||
|
||||
out_error:
|
||||
free(tzid);
|
||||
free(list);
|
||||
switch (res)
|
||||
{
|
||||
case TIME_ZONE_ID_DAYLIGHT:
|
||||
|
|
Loading…
Reference in New Issue