diff --git a/recipes/date/all/conandata.yml b/recipes/date/all/conandata.yml index 3a7c6f92926db..bc328962e1435 100644 --- a/recipes/date/all/conandata.yml +++ b/recipes/date/all/conandata.yml @@ -21,6 +21,10 @@ patches: - patch_file: "patches/cmake-3.0.0.patch" patch_description: "Disable string view to workaround clang 5 not having it" patch_type: "portability" + - patch_file: "patches/load-tzdb-from-envvar-and-windows-binary-db-support-3.0.0.patch" + patch_description: "Allow TZDB to be loaded by envvar and add ability for windows to read zic-compiled binary db" + patch_type: "portability" + patch_source: "https://github.com/HowardHinnant/date/pull/611" "2.4.1": - patch_file: "patches/0001-fix-uwp.patch" patch_description: "Fix Universal Windows Platform (UWP) unhandled exception support. See https://github.com/microsoft/vcpkg/pull/8151#issuecomment-531175393." @@ -31,3 +35,7 @@ patches: - patch_file: "patches/string_view.patch" patch_description: "Disable string view to workaround clang 5 not having it" patch_type: "portability" + - patch_file: "patches/load-tzdb-from-envvar-and-windows-binary-db-support-2.4.1.patch" + patch_description: "Allow TZDB to be loaded by envvar and add ability for windows to read zic-compiled binary db" + patch_type: "portability" + patch_source: "https://github.com/HowardHinnant/date/pull/611" diff --git a/recipes/date/all/patches/load-tzdb-from-envvar-and-windows-binary-db-support-2.4.1.patch b/recipes/date/all/patches/load-tzdb-from-envvar-and-windows-binary-db-support-2.4.1.patch new file mode 100644 index 0000000000000..521c7c2ae25eb --- /dev/null +++ b/recipes/date/all/patches/load-tzdb-from-envvar-and-windows-binary-db-support-2.4.1.patch @@ -0,0 +1,359 @@ +From 7505317a4b5397f70e9007c871a73245a4f60417 Mon Sep 17 00:00:00 2001 +From: John McCann Cunniff Jr +Date: Tue, 20 Oct 2020 12:54:56 -0400 +Subject: [PATCH] ADD windows support for USE_SYSTEM_TZ_DB + +--- + CMakeLists.txt | 23 ++++--- + include/date/tz.h | 3 - + src/tz.cpp | 170 ++++++++++++++++++++++++++++++++++++---------- + 3 files changed, 149 insertions(+), 47 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 7bc93df..ac989c8 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -23,6 +23,7 @@ function( print_option OPT ) + endfunction( ) + + print_option( USE_SYSTEM_TZ_DB ) ++print_option( MANUAL_TZ_DB ) + print_option( USE_TZ_DB_IN_DOT ) + print_option( BUILD_SHARED_LIBS ) + print_option( ENABLE_DATE_TESTING ) +@@ -45,20 +46,26 @@ set( HEADER_FILES + + add_library( tz ${HEADER_FILES} ${SOURCE_FOLDER}/tz.cpp ) + +-if( USE_SYSTEM_TZ_DB ) ++if( USE_SYSTEM_TZ_DB OR MANUAL_TZ_DB ) + target_compile_definitions( tz PRIVATE -DUSE_AUTOLOAD=0 ) + target_compile_definitions( tz PRIVATE -DHAS_REMOTE_API=0 ) +- # cannot set USE_OS_TZDB to 1 on Windows +- if( NOT WIN32 ) +- target_compile_definitions( tz PUBLIC -DUSE_OS_TZDB=1 ) +- endif( ) + else( ) + target_compile_definitions( tz PRIVATE -DUSE_AUTOLOAD=1 ) + target_compile_definitions( tz PRIVATE -DHAS_REMOTE_API=1 ) +- target_compile_definitions( tz PUBLIC -DUSE_OS_TZDB=0 ) +- find_package( CURL REQUIRED ) ++endif( ) ++ ++if ( USE_SYSTEM_TZ_DB AND NOT MANUAL_TZ_DB ) ++ target_compile_definitions( tz PRIVATE -DINSTALL=. ) ++ target_compile_definitions( tz PUBLIC -DUSE_OS_TZDB=1 ) ++else( ) ++ target_compile_definitions( tz PUBLIC USE_OS_TZDB=0 ) ++endif( ) ++ ++if( NOT USE_SYSTEM_TZ_DB AND NOT MANUAL_TZ_DB ) ++ find_package( CURL REQUIRED ) ++ include_directories( SYSTEM ${CURL_INCLUDE_DIRS} ) + set( OPTIONAL_LIBRARIES CURL::libcurl ) +-endif() ++endif( ) + + if( BUILD_SHARED_LIBS ) + target_compile_definitions( tz PRIVATE -DDATE_BUILD_DLL=1 ) +diff --git a/include/date/tz.h b/include/date/tz.h +index 9840e7b..53f4a56 100644 +--- a/include/date/tz.h ++++ b/include/date/tz.h +@@ -83,9 +83,6 @@ static_assert(HAS_REMOTE_API == 0 ? AUTO_DOWNLOAD == 0 : true, + #endif + + #if USE_OS_TZDB +-# ifdef _WIN32 +-# error "USE_OS_TZDB can not be used on Windows" +-# endif + # ifndef MISSING_LEAP_SECONDS + # ifdef __APPLE__ + # define MISSING_LEAP_SECONDS 1 +diff --git a/src/tz.cpp b/src/tz.cpp +index f4a819b..0cde872 100644 +--- a/src/tz.cpp ++++ b/src/tz.cpp +@@ -81,6 +81,10 @@ + # endif // __MINGW32__ + + # include ++# include ++# if !defined(S_ISDIR) && defined(S_IFMT) && defined(_S_IFDIR) ++# define S_ISDIR(m) (((m) & S_IFMT) == _S_IFDIR) ++# endif + #endif // _WIN32 + + #include "date/tz_private.h" +@@ -92,7 +96,12 @@ + #endif + + #if USE_OS_TZDB +-# include ++# if !WIN32 ++# include ++# else ++# include ++# include ++# endif + #endif + #include + #include +@@ -276,6 +285,14 @@ access_install() + #undef STRINGIZE + #endif // !INSTALL + ++ { ++ static char* tz_local_env = getenv("TZDATA"); ++ if (tz_local_env != nullptr) { ++ static std::string tz_local_env_s = tz_local_env; ++ return tz_local_env_s; ++ } ++ } ++ + return install; + } + +@@ -318,7 +335,6 @@ CONSTCD14 const sys_seconds min_seconds = sys_days(min_year/min_day); + + #endif // USE_OS_TZDB + +-#ifndef _WIN32 + + static + std::string +@@ -330,6 +346,16 @@ discover_tz_dir() + CONSTDATA auto tz_dir_default = "/usr/share/zoneinfo"; + CONSTDATA auto tz_dir_buildroot = "/usr/share/zoneinfo/uclibc"; + ++ { ++ // TZDIR is from the posix naming ++ // https://man7.org/linux/man-pages/man3/tzset.3.html ++ static char* tz_local_env = getenv("TZDATA"); ++ if (tz_local_env != nullptr) { ++ static std::string tz_local_env_s = tz_local_env; ++ return tz_local_env_s; ++ } ++ } ++ + // Check special path which is valid for buildroot with uclibc builds + if(stat(tz_dir_buildroot, &sb) == 0 && S_ISDIR(sb.st_mode)) + return tz_dir_buildroot; +@@ -369,7 +395,6 @@ get_tz_dir() + return tz_dir; + } + +-#endif + + // +-------------------+ + // | End Configuration | +@@ -443,7 +468,31 @@ get_tzdb_list() + return tz_db; + } + +-#if !USE_OS_TZDB ++static ++std::string ++parse3(std::istream& in) ++{ ++ std::string r(3, ' '); ++ ws(in); ++ r[0] = static_cast(in.get()); ++ r[1] = static_cast(in.get()); ++ r[2] = static_cast(in.get()); ++ return r; ++} ++ ++static ++unsigned ++parse_month(std::istream& in) ++{ ++ CONSTDATA char*const month_names[] = ++ {"Jan", "Feb", "Mar", "Apr", "May", "Jun", ++ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; ++ auto s = parse3(in); ++ auto m = std::find(std::begin(month_names), std::end(month_names), s) - month_names; ++ if (m >= std::end(month_names) - std::begin(month_names)) ++ throw std::runtime_error("oops: bad month name: " + s); ++ return static_cast(++m); ++} + + #ifdef _WIN32 + +@@ -655,19 +704,9 @@ load_timezone_mappings_from_xml_file(const std::string& input_path) + + #endif // _WIN32 + +-// Parsing helpers + +-static +-std::string +-parse3(std::istream& in) +-{ +- std::string r(3, ' '); +- ws(in); +- r[0] = static_cast(in.get()); +- r[1] = static_cast(in.get()); +- r[2] = static_cast(in.get()); +- return r; +-} ++#if !USE_OS_TZDB ++// Parsing helpers + + static + unsigned +@@ -682,20 +721,6 @@ parse_dow(std::istream& in) + return static_cast(dow); + } + +-static +-unsigned +-parse_month(std::istream& in) +-{ +- CONSTDATA char*const month_names[] = +- {"Jan", "Feb", "Mar", "Apr", "May", "Jun", +- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +- auto s = parse3(in); +- auto m = std::find(std::begin(month_names), std::end(month_names), s) - month_names; +- if (m >= std::end(month_names) - std::begin(month_names)) +- throw std::runtime_error("oops: bad month name: " + s); +- return static_cast(++m); +-} +- + static + std::chrono::seconds + parse_unsigned_time(std::istream& in) +@@ -1710,9 +1735,15 @@ time_zone::time_zone(const std::string& s, detail::undocumented) + + enum class endian + { +- native = __BYTE_ORDER__, ++#ifdef _WIN32 ++ little = 0, ++ big = 1, ++ native = little ++#else + little = __ORDER_LITTLE_ENDIAN__, +- big = __ORDER_BIG_ENDIAN__ ++ big = __ORDER_BIG_ENDIAN__, ++ native = __BYTE_ORDER__ ++#endif + }; + + static +@@ -2018,8 +2049,10 @@ time_zone::init_impl() + { + using namespace std; + using namespace std::chrono; +- auto name = get_tz_dir() + ('/' + name_); +- std::ifstream inf(name); ++ auto name = get_tz_dir() + (folder_delimiter + name_); ++ // Some platforms will open the inf stream in text mode. Specify binary ++ // to avoid confusion. ++ std::ifstream inf(name, std::ios::in | std::ios::binary); + if (!inf.is_open()) + throw std::runtime_error{"Unable to open " + name}; + inf.exceptions(std::ios::failbit | std::ios::badbit); +@@ -2630,8 +2663,10 @@ init_tzdb() + //Iterate through folders + std::queue subfolders; + subfolders.emplace(get_tz_dir()); +- struct dirent* d; ++ + struct stat s; ++#if !WIN32 // !WIN32 ++ struct dirent* d; + while (!subfolders.empty()) + { + auto dirname = std::move(subfolders.front()); +@@ -2648,8 +2683,11 @@ init_tzdb() + strcmp(d->d_name, "iso3166.tab") == 0 || + strcmp(d->d_name, "right") == 0 || + strcmp(d->d_name, "+VERSION") == 0 || ++ strcmp(d->d_name, "version") == 0 || + strcmp(d->d_name, "zone.tab") == 0 || + strcmp(d->d_name, "zone1970.tab") == 0 || ++ strcmp(d->d_name, "tzdata.zi") == 0 || ++ strcmp(d->d_name, "leapseconds") == 0 || + strcmp(d->d_name, "leap-seconds.list") == 0 ) + continue; + auto subname = dirname + folder_delimiter + d->d_name; +@@ -2671,6 +2709,56 @@ init_tzdb() + } + closedir(dir); + } ++#else // WIN32 ++ // POSIX dirent is not availible in Visual C++ ++ // Use Windows file API instead ++ WIN32_FIND_DATA hFindData; ++ while (!subfolders.empty()) ++ { ++ auto dirname = std::move(subfolders.front()); ++ subfolders.pop(); ++ HANDLE hFind = FindFirstFile((dirname + "\\\\*").c_str(), &hFindData); ++ if (hFind == INVALID_HANDLE_VALUE) ++ { ++ continue; ++ } ++ ++ do ++ { ++ // Ignore these files: ++ if (hFindData.cFileName[0] == '.' || // curdir, prevdir, hidden ++ memcmp(hFindData.cFileName, "posix", 5) == 0 || // starts with posix ++ strcmp(hFindData.cFileName, "Factory") == 0 || ++ strcmp(hFindData.cFileName, "iso3166.tab") == 0 || ++ strcmp(hFindData.cFileName, "right") == 0 || ++ strcmp(hFindData.cFileName, "+VERSION") == 0 || ++ strcmp(hFindData.cFileName, "zone.tab") == 0 || ++ strcmp(hFindData.cFileName, "zone1970.tab") == 0 || ++ strcmp(hFindData.cFileName, "tzdata.zi") == 0 || ++ strcmp(hFindData.cFileName, "leapseconds") == 0 || ++ strcmp(hFindData.cFileName, "leap-seconds.list") == 0 ) ++ { ++ continue; ++ } ++ auto subname = dirname + folder_delimiter + hFindData.cFileName; ++ if(stat(subname.c_str(), &s) == 0) ++ { ++ if(S_ISDIR(s.st_mode)) ++ { ++ subfolders.push(subname); ++ } ++ else ++ { ++ std::string zone = subname.substr(get_tz_dir().size()+1); ++ db->zones.emplace_back(zone, ++ detail::undocumented{}); ++ } ++ } ++ } ++ while (FindNextFile(hFind, &hFindData )); ++ FindClose(hFind); ++ } ++#endif // WIN32 + db->zones.shrink_to_fit(); + std::sort(db->zones.begin(), db->zones.end()); + # if !MISSING_LEAP_SECONDS +@@ -3528,7 +3616,17 @@ locate_zone(std::string_view tz_name) + locate_zone(const std::string& tz_name) + #endif + { +- return get_tzdb().locate_zone(tz_name); ++ return get_tzdb().locate_zone( ++#if WIN32 && USE_OS_TZDB ++ // When USE_OS_TZDB=ON, the tz_name needs to be a valid path. ++ // For timezone labeling consistency in Windows, switch "/" to "\". ++ // This will allow for tz_name to have consistent naming regardless of ++ // if USE_OS_TZDB is ON. ++ std::regex_replace(tz_name, std::regex("\\/"), "\\") ++#else ++ tz_name ++#endif ++ ); + } + + #if USE_OS_TZDB +-- +2.24.3 (Apple Git-128) + diff --git a/recipes/date/all/patches/load-tzdb-from-envvar-and-windows-binary-db-support-3.0.0.patch b/recipes/date/all/patches/load-tzdb-from-envvar-and-windows-binary-db-support-3.0.0.patch new file mode 100644 index 0000000000000..7abdead8c980d --- /dev/null +++ b/recipes/date/all/patches/load-tzdb-from-envvar-and-windows-binary-db-support-3.0.0.patch @@ -0,0 +1,358 @@ +From d6181fa9b07cc3b85a1771941b5b874d0d867f30 Mon Sep 17 00:00:00 2001 +From: John McCann Cunniff Jr +Date: Tue, 20 Oct 2020 12:54:56 -0400 +Subject: [PATCH] ADD windows support for USE_SYSTEM_TZ_DB + +--- + CMakeLists.txt | 9 +-- + include/date/tz.h | 3 - + src/tz.cpp | 170 ++++++++++++++++++++++++++++++++++++---------- + 3 files changed, 138 insertions(+), 44 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index ad74900..73f015a 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -7,7 +7,7 @@ + include( FetchContent ) + FetchContent_Declare( date_src + GIT_REPOSITORY https://github.com/HowardHinnant/date.git +- GIT_TAG 2.4.2 # adjust tag/branch/commit as needed ++ GIT_TAG v3.0.0 # adjust tag/branch/commit as needed + ) + FetchContent_MakeAvailable(date_src) + ... +@@ -17,7 +17,8 @@ + + cmake_minimum_required( VERSION 3.7 ) + +-project( date VERSION 2.4.1 ) ++project( date VERSION 3.0.0 ) ++set(ABI_VERSION 3) # used as SOVERSION, increment when ABI changes + + include( GNUInstallDirs ) + +@@ -106,7 +107,7 @@ if( BUILD_TZ_LIB ) + $<$,$>:DATE_BUILD_DLL=1> + $<$:INSTALL=.> + PUBLIC +- USE_OS_TZDB=$,$>,$>>,1,0> ++ USE_OS_TZDB=$,$>>,1,0> + INTERFACE + $<$,$>:DATE_USE_DLL=1> ) + set(TZ_HEADERS include/date/tz.h) +@@ -117,7 +118,7 @@ if( BUILD_TZ_LIB ) + POSITION_INDEPENDENT_CODE ON + PUBLIC_HEADER "${TZ_HEADERS}" + VERSION "${PROJECT_VERSION}" +- SOVERSION "${PROJECT_VERSION}" ) ++ SOVERSION "${ABI_VERSION}" ) + if( NOT MSVC ) + find_package( Threads ) + target_link_libraries( date-tz PUBLIC Threads::Threads ) +diff --git a/include/date/tz.h b/include/date/tz.h +index 45fab11..39df2cc 100644 +--- a/include/date/tz.h ++++ b/include/date/tz.h +@@ -83,9 +83,6 @@ static_assert(HAS_REMOTE_API == 0 ? AUTO_DOWNLOAD == 0 : true, + #endif + + #if USE_OS_TZDB +-# ifdef _WIN32 +-# error "USE_OS_TZDB can not be used on Windows" +-# endif + # ifndef MISSING_LEAP_SECONDS + # ifdef __APPLE__ + # define MISSING_LEAP_SECONDS 1 +diff --git a/src/tz.cpp b/src/tz.cpp +index f455f22..9f8c098 100644 +--- a/src/tz.cpp ++++ b/src/tz.cpp +@@ -81,6 +81,10 @@ + # endif // __MINGW32__ + + # include ++# include ++# if !defined(S_ISDIR) && defined(S_IFMT) && defined(_S_IFDIR) ++# define S_ISDIR(m) (((m) & S_IFMT) == _S_IFDIR) ++# endif + #endif // _WIN32 + + #include "date/tz_private.h" +@@ -93,7 +97,12 @@ + #endif + + #if USE_OS_TZDB +-# include ++# if !WIN32 ++# include ++# else ++# include ++# include ++# endif + #endif + #include + #include +@@ -299,6 +308,14 @@ access_install() + #undef STRINGIZE + #endif // !INSTALL + ++ { ++ static char* tz_local_env = getenv("TZDATA"); ++ if (tz_local_env != nullptr) { ++ static std::string tz_local_env_s = tz_local_env; ++ return tz_local_env_s; ++ } ++ } ++ + return install; + } + +@@ -341,7 +358,6 @@ CONSTCD14 const sys_seconds min_seconds = sys_days(min_year/min_day); + + #endif // USE_OS_TZDB + +-#ifndef _WIN32 + + static + std::string +@@ -353,6 +369,16 @@ discover_tz_dir() + CONSTDATA auto tz_dir_default = "/usr/share/zoneinfo"; + CONSTDATA auto tz_dir_buildroot = "/usr/share/zoneinfo/uclibc"; + ++ { ++ // TZDIR is from the posix naming ++ // https://man7.org/linux/man-pages/man3/tzset.3.html ++ static char* tz_local_env = getenv("TZDATA"); ++ if (tz_local_env != nullptr) { ++ static std::string tz_local_env_s = tz_local_env; ++ return tz_local_env_s; ++ } ++ } ++ + // Check special path which is valid for buildroot with uclibc builds + if(stat(tz_dir_buildroot, &sb) == 0 && S_ISDIR(sb.st_mode)) + return tz_dir_buildroot; +@@ -396,7 +422,6 @@ get_tz_dir() + return tz_dir; + } + +-#endif + + // +-------------------+ + // | End Configuration | +@@ -465,7 +490,31 @@ get_tzdb_list() + return tz_db; + } + +-#if !USE_OS_TZDB ++static ++std::string ++parse3(std::istream& in) ++{ ++ std::string r(3, ' '); ++ ws(in); ++ r[0] = static_cast(in.get()); ++ r[1] = static_cast(in.get()); ++ r[2] = static_cast(in.get()); ++ return r; ++} ++ ++static ++unsigned ++parse_month(std::istream& in) ++{ ++ CONSTDATA char*const month_names[] = ++ {"Jan", "Feb", "Mar", "Apr", "May", "Jun", ++ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; ++ auto s = parse3(in); ++ auto m = std::find(std::begin(month_names), std::end(month_names), s) - month_names; ++ if (m >= std::end(month_names) - std::begin(month_names)) ++ throw std::runtime_error("oops: bad month name: " + s); ++ return static_cast(++m); ++} + + #ifdef _WIN32 + +@@ -677,19 +726,9 @@ load_timezone_mappings_from_xml_file(const std::string& input_path) + + #endif // _WIN32 + +-// Parsing helpers + +-static +-std::string +-parse3(std::istream& in) +-{ +- std::string r(3, ' '); +- ws(in); +- r[0] = static_cast(in.get()); +- r[1] = static_cast(in.get()); +- r[2] = static_cast(in.get()); +- return r; +-} ++#if !USE_OS_TZDB ++// Parsing helpers + + static + unsigned +@@ -704,20 +743,6 @@ parse_dow(std::istream& in) + return static_cast(dow); + } + +-static +-unsigned +-parse_month(std::istream& in) +-{ +- CONSTDATA char*const month_names[] = +- {"Jan", "Feb", "Mar", "Apr", "May", "Jun", +- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; +- auto s = parse3(in); +- auto m = std::find(std::begin(month_names), std::end(month_names), s) - month_names; +- if (m >= std::end(month_names) - std::begin(month_names)) +- throw std::runtime_error("oops: bad month name: " + s); +- return static_cast(++m); +-} +- + static + std::chrono::seconds + parse_unsigned_time(std::istream& in) +@@ -1732,9 +1757,15 @@ time_zone::time_zone(const std::string& s, detail::undocumented) + + enum class endian + { +- native = __BYTE_ORDER__, ++#ifdef _WIN32 ++ little = 0, ++ big = 1, ++ native = little ++#else + little = __ORDER_LITTLE_ENDIAN__, +- big = __ORDER_BIG_ENDIAN__ ++ big = __ORDER_BIG_ENDIAN__, ++ native = __BYTE_ORDER__ ++#endif + }; + + static +@@ -2041,8 +2072,10 @@ time_zone::init_impl() + { + using namespace std; + using namespace std::chrono; +- auto name = get_tz_dir() + ('/' + name_); +- std::ifstream inf(name); ++ auto name = get_tz_dir() + (folder_delimiter + name_); ++ // Some platforms will open the inf stream in text mode. Specify binary ++ // to avoid confusion. ++ std::ifstream inf(name, std::ios::in | std::ios::binary); + if (!inf.is_open()) + throw std::runtime_error{"Unable to open " + name}; + inf.exceptions(std::ios::failbit | std::ios::badbit); +@@ -2167,7 +2200,7 @@ time_zone::get_info_impl(local_seconds tp) const + { + i.second = load_sys_info(--tr); + tps = sys_seconds{(tp - i.second.offset).time_since_epoch()}; +- if (tps < i.second.end) ++ if (tps < i.second.end && i.first.end != i.second.end) + { + i.result = local_info::ambiguous; + std::swap(i.first, i.second); +@@ -2653,8 +2686,10 @@ init_tzdb() + //Iterate through folders + std::queue subfolders; + subfolders.emplace(get_tz_dir()); +- struct dirent* d; ++ + struct stat s; ++#if !WIN32 // !WIN32 ++ struct dirent* d; + while (!subfolders.empty()) + { + auto dirname = std::move(subfolders.front()); +@@ -2671,6 +2706,7 @@ init_tzdb() + strcmp(d->d_name, "iso3166.tab") == 0 || + strcmp(d->d_name, "right") == 0 || + strcmp(d->d_name, "+VERSION") == 0 || ++ strcmp(d->d_name, "version") == 0 || + strcmp(d->d_name, "zone.tab") == 0 || + strcmp(d->d_name, "zone1970.tab") == 0 || + strcmp(d->d_name, "tzdata.zi") == 0 || +@@ -2696,6 +2732,56 @@ init_tzdb() + } + closedir(dir); + } ++#else // WIN32 ++ // POSIX dirent is not availible in Visual C++ ++ // Use Windows file API instead ++ WIN32_FIND_DATA hFindData; ++ while (!subfolders.empty()) ++ { ++ auto dirname = std::move(subfolders.front()); ++ subfolders.pop(); ++ HANDLE hFind = FindFirstFile((dirname + "\\\\*").c_str(), &hFindData); ++ if (hFind == INVALID_HANDLE_VALUE) ++ { ++ continue; ++ } ++ ++ do ++ { ++ // Ignore these files: ++ if (hFindData.cFileName[0] == '.' || // curdir, prevdir, hidden ++ memcmp(hFindData.cFileName, "posix", 5) == 0 || // starts with posix ++ strcmp(hFindData.cFileName, "Factory") == 0 || ++ strcmp(hFindData.cFileName, "iso3166.tab") == 0 || ++ strcmp(hFindData.cFileName, "right") == 0 || ++ strcmp(hFindData.cFileName, "+VERSION") == 0 || ++ strcmp(hFindData.cFileName, "zone.tab") == 0 || ++ strcmp(hFindData.cFileName, "zone1970.tab") == 0 || ++ strcmp(hFindData.cFileName, "tzdata.zi") == 0 || ++ strcmp(hFindData.cFileName, "leapseconds") == 0 || ++ strcmp(hFindData.cFileName, "leap-seconds.list") == 0 ) ++ { ++ continue; ++ } ++ auto subname = dirname + folder_delimiter + hFindData.cFileName; ++ if(stat(subname.c_str(), &s) == 0) ++ { ++ if(S_ISDIR(s.st_mode)) ++ { ++ subfolders.push(subname); ++ } ++ else ++ { ++ std::string zone = subname.substr(get_tz_dir().size()+1); ++ db->zones.emplace_back(zone, ++ detail::undocumented{}); ++ } ++ } ++ } ++ while (FindNextFile(hFind, &hFindData )); ++ FindClose(hFind); ++ } ++#endif // WIN32 + db->zones.shrink_to_fit(); + std::sort(db->zones.begin(), db->zones.end()); + # if !MISSING_LEAP_SECONDS +@@ -3558,7 +3644,17 @@ locate_zone(std::string_view tz_name) + locate_zone(const std::string& tz_name) + #endif + { +- return get_tzdb().locate_zone(tz_name); ++ return get_tzdb().locate_zone( ++#if WIN32 && USE_OS_TZDB ++ // When USE_OS_TZDB=ON, the tz_name needs to be a valid path. ++ // For timezone labeling consistency in Windows, switch "/" to "\". ++ // This will allow for tz_name to have consistent naming regardless of ++ // if USE_OS_TZDB is ON. ++ std::regex_replace(tz_name, std::regex("\\/"), "\\") ++#else ++ tz_name ++#endif ++ ); + } + + #if USE_OS_TZDB +-- +2.24.3 (Apple Git-128) +