// Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include <__msvc_tzdb.hpp> #include #include #include #include #include #include #include #include #pragma comment(lib, "Advapi32") namespace { enum class _Icu_api_level : unsigned long { _Not_set, _Detecting, _Has_failed, _Has_icu_addresses, }; struct _Icu_functions_table { _STD atomic _Pfn_ucal_close{nullptr}; _STD atomic _Pfn_ucal_get{nullptr}; _STD atomic _Pfn_ucal_getCanonicalTimeZoneID{nullptr}; _STD atomic _Pfn_ucal_getDefaultTimeZone{nullptr}; _STD atomic _Pfn_ucal_getTimeZoneDisplayName{nullptr}; _STD atomic _Pfn_ucal_getTimeZoneTransitionDate{nullptr}; _STD atomic _Pfn_ucal_getTZDataVersion{nullptr}; _STD atomic _Pfn_ucal_inDaylightTime{nullptr}; _STD atomic _Pfn_ucal_open{nullptr}; _STD atomic _Pfn_ucal_openTimeZoneIDEnumeration{nullptr}; _STD atomic _Pfn_ucal_setMillis{nullptr}; _STD atomic _Pfn_uenum_close{nullptr}; _STD atomic _Pfn_uenum_count{nullptr}; _STD atomic _Pfn_uenum_unext{nullptr}; _STD atomic<_Icu_api_level> _Api_level{_Icu_api_level::_Not_set}; }; _Icu_functions_table _Icu_functions; template void _Load_address( const HMODULE _Module, _STD atomic<_Ty>& _Stored_Pfn, LPCSTR _Fn_name, DWORD& _Last_error) noexcept { const auto _Pfn = reinterpret_cast<_Ty>(GetProcAddress(_Module, _Fn_name)); if (_Pfn != nullptr) { _Stored_Pfn.store(_Pfn, _STD memory_order_relaxed); } else { _Last_error = GetLastError(); } } [[nodiscard]] _Icu_api_level _Init_icu_functions(_Icu_api_level _Level) noexcept { while (!_Icu_functions._Api_level.compare_exchange_weak( _Level, _Icu_api_level::_Detecting, _STD memory_order_acq_rel)) { if (_Level > _Icu_api_level::_Detecting) { return _Level; } } _Level = _Icu_api_level::_Has_failed; const HMODULE _Icu_module = LoadLibraryExW(L"icu.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); if (_Icu_module != nullptr) { // collect at least one error if any GetProcAddress call fails DWORD _Last_error{ERROR_SUCCESS}; _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_close, "ucal_close", _Last_error); _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_get, "ucal_get", _Last_error); _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_getCanonicalTimeZoneID, "ucal_getCanonicalTimeZoneID", _Last_error); _Load_address( _Icu_module, _Icu_functions._Pfn_ucal_getDefaultTimeZone, "ucal_getDefaultTimeZone", _Last_error); _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_getTimeZoneDisplayName, "ucal_getTimeZoneDisplayName", _Last_error); _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_getTimeZoneTransitionDate, "ucal_getTimeZoneTransitionDate", _Last_error); _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_getTZDataVersion, "ucal_getTZDataVersion", _Last_error); _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_inDaylightTime, "ucal_inDaylightTime", _Last_error); _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_open, "ucal_open", _Last_error); _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_openTimeZoneIDEnumeration, "ucal_openTimeZoneIDEnumeration", _Last_error); _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_setMillis, "ucal_setMillis", _Last_error); _Load_address(_Icu_module, _Icu_functions._Pfn_uenum_close, "uenum_close", _Last_error); _Load_address(_Icu_module, _Icu_functions._Pfn_uenum_count, "uenum_count", _Last_error); _Load_address(_Icu_module, _Icu_functions._Pfn_uenum_unext, "uenum_unext", _Last_error); if (_Last_error == ERROR_SUCCESS) { _Level = _Icu_api_level::_Has_icu_addresses; } else { // reset last error code for thread in case a later GetProcAddress resets it SetLastError(_Last_error); } } _Icu_functions._Api_level.store(_Level, _STD memory_order_release); return _Level; } [[nodiscard]] _Icu_api_level _Acquire_icu_functions() noexcept { auto _Level = _Icu_functions._Api_level.load(_STD memory_order_acquire); if (_Level <= _Icu_api_level::_Detecting) { _Level = _Init_icu_functions(_Level); } return _Level; } void __icu_ucal_close(UCalendar* cal) noexcept { const auto _Fun = _Icu_functions._Pfn_ucal_close.load(_STD memory_order_relaxed); _Fun(cal); } [[nodiscard]] int32_t __icu_ucal_get(const UCalendar* cal, UCalendarDateFields field, UErrorCode* status) noexcept { const auto _Fun = _Icu_functions._Pfn_ucal_get.load(_STD memory_order_relaxed); return _Fun(cal, field, status); } [[nodiscard]] int32_t __icu_ucal_getCanonicalTimeZoneID(const UChar* id, int32_t len, UChar* result, int32_t resultCapacity, UBool* isSystemID, UErrorCode* status) noexcept { const auto _Fun = _Icu_functions._Pfn_ucal_getCanonicalTimeZoneID.load(_STD memory_order_relaxed); return _Fun(id, len, result, resultCapacity, isSystemID, status); } [[nodiscard]] int32_t __icu_ucal_getDefaultTimeZone( UChar* result, int32_t resultCapacity, UErrorCode* ec) noexcept { const auto _Fun = _Icu_functions._Pfn_ucal_getDefaultTimeZone.load(_STD memory_order_relaxed); return _Fun(result, resultCapacity, ec); } [[nodiscard]] int32_t __icu_ucal_getTimeZoneDisplayName(const UCalendar* cal, UCalendarDisplayNameType type, const char* locale, UChar* result, int32_t resultLength, UErrorCode* status) noexcept { const auto _Fun = _Icu_functions._Pfn_ucal_getTimeZoneDisplayName.load(_STD memory_order_relaxed); return _Fun(cal, type, locale, result, resultLength, status); } [[nodiscard]] UBool __icu_ucal_getTimeZoneTransitionDate( const UCalendar* cal, UTimeZoneTransitionType type, UDate* transition, UErrorCode* status) noexcept { const auto _Fun = _Icu_functions._Pfn_ucal_getTimeZoneTransitionDate.load(_STD memory_order_relaxed); return _Fun(cal, type, transition, status); } [[nodiscard]] const char* __icu_ucal_getTZDataVersion(UErrorCode* status) noexcept { const auto _Fun = _Icu_functions._Pfn_ucal_getTZDataVersion.load(_STD memory_order_relaxed); return _Fun(status); } [[nodiscard]] UBool __icu_ucal_inDaylightTime(const UCalendar* cal, UErrorCode* status) noexcept { const auto _Fun = _Icu_functions._Pfn_ucal_inDaylightTime.load(_STD memory_order_relaxed); return _Fun(cal, status); } [[nodiscard]] UCalendar* __icu_ucal_open( const UChar* zoneID, int32_t len, const char* locale, UCalendarType type, UErrorCode* status) noexcept { const auto _Fun = _Icu_functions._Pfn_ucal_open.load(_STD memory_order_relaxed); return _Fun(zoneID, len, locale, type, status); } [[nodiscard]] UEnumeration* __icu_ucal_openTimeZoneIDEnumeration( USystemTimeZoneType zoneType, const char* region, const int32_t* rawOffset, UErrorCode* ec) noexcept { const auto _Fun = _Icu_functions._Pfn_ucal_openTimeZoneIDEnumeration.load(_STD memory_order_relaxed); return _Fun(zoneType, region, rawOffset, ec); } void __icu_ucal_setMillis(UCalendar* cal, UDate dateTime, UErrorCode* status) noexcept { const auto _Fun = _Icu_functions._Pfn_ucal_setMillis.load(_STD memory_order_relaxed); _Fun(cal, dateTime, status); } void __icu_uenum_close(UEnumeration* en) noexcept { const auto _Fun = _Icu_functions._Pfn_uenum_close.load(_STD memory_order_relaxed); _Fun(en); } [[nodiscard]] int32_t __icu_uenum_count(UEnumeration* en, UErrorCode* ec) noexcept { const auto _Fun = _Icu_functions._Pfn_uenum_count.load(_STD memory_order_relaxed); return _Fun(en, ec); } [[nodiscard]] const UChar* __icu_uenum_unext(UEnumeration* en, int32_t* resultLength, UErrorCode* status) noexcept { const auto _Fun = _Icu_functions._Pfn_uenum_unext.load(_STD memory_order_relaxed); return _Fun(en, resultLength, status); } struct _UEnumeration_deleter { void operator()(UEnumeration* _En) const noexcept { __icu_uenum_close(_En); } }; struct _UCalendar_deleter { void operator()(UCalendar* _Cal) const noexcept { __icu_ucal_close(_Cal); } }; [[nodiscard]] const char* _Allocate_wide_to_narrow( const char16_t* const _Input, const int _Input_len, __std_tzdb_error& _Err) noexcept { const auto _Code_page = __std_fs_code_page(); const auto _Input_as_wchar = reinterpret_cast(_Input); const auto _Count_result = __std_fs_convert_wide_to_narrow(_Code_page, _Input_as_wchar, _Input_len, nullptr, 0); if (_Count_result._Err != __std_win_error::_Success) { SetLastError(static_cast(_Count_result._Err)); _Err = __std_tzdb_error::_Win_error; return nullptr; } _STD unique_ptr _Data{new (_STD nothrow) char[_Count_result._Len + 1]}; if (_Data == nullptr) { return nullptr; } _Data[_Count_result._Len] = '\0'; const auto _Result = __std_fs_convert_wide_to_narrow(_Code_page, _Input_as_wchar, _Input_len, _Data.get(), _Count_result._Len); if (_Result._Err != __std_win_error::_Success) { SetLastError(static_cast(_Result._Err)); _Err = __std_tzdb_error::_Win_error; return nullptr; } return _Data.release(); } [[nodiscard]] _STD unique_ptr _Allocate_narrow_to_wide( const char* const _Input, const int _Input_len, __std_tzdb_error& _Err) noexcept { const auto _Code_page = __std_fs_code_page(); const auto _Count = __std_fs_convert_narrow_to_wide(_Code_page, _Input, _Input_len, nullptr, 0); if (_Count._Err != __std_win_error::_Success) { _Err = __std_tzdb_error::_Win_error; return nullptr; } _STD unique_ptr _Data{new (_STD nothrow) char16_t[_Count._Len + 1]}; if (_Data == nullptr) { return nullptr; } _Data[_Count._Len] = u'\0'; const auto _Output_as_wchar = reinterpret_cast(_Data.get()); const auto _Result = __std_fs_convert_narrow_to_wide(_Code_page, _Input, _Input_len, _Output_as_wchar, _Count._Len); if (_Result._Err != __std_win_error::_Success) { _Err = __std_tzdb_error::_Win_error; return nullptr; } return _Data; } template [[nodiscard]] _STD unique_ptr _Get_icu_string_impl(const _Function _Icu_fn, const int32_t _Initial_buf_len, int32_t& _Result_len, __std_tzdb_error& _Err) noexcept { _STD unique_ptr _Str_buf{new (_STD nothrow) char16_t[_Initial_buf_len]}; if (_Str_buf == nullptr) { return nullptr; } UErrorCode _UErr{U_ZERO_ERROR}; _Result_len = _Icu_fn(_Str_buf.get(), _Initial_buf_len, &_UErr); if (_UErr == U_BUFFER_OVERFLOW_ERROR && _Result_len > 0) { _Str_buf.reset(new (_STD nothrow) char16_t[_Result_len + 1]); if (_Str_buf == nullptr) { return nullptr; } _UErr = U_ZERO_ERROR; // reset error. _Result_len = _Icu_fn(_Str_buf.get(), _Result_len, &_UErr); if (U_FAILURE(_UErr)) { _Err = __std_tzdb_error::_Icu_error; return nullptr; } } else if (U_FAILURE(_UErr) || _Result_len <= 0) { _Err = __std_tzdb_error::_Icu_error; return nullptr; } return _Str_buf; } [[nodiscard]] _STD unique_ptr _Get_canonical_id( const char16_t* _Id, const int32_t _Len, int32_t& _Result_len, __std_tzdb_error& _Err) noexcept { const auto _Icu_fn = [_Id, _Len](UChar* _Result, int32_t _Result_capacity, UErrorCode* _UErr) { UBool _Is_system{}; return __icu_ucal_getCanonicalTimeZoneID(_Id, _Len, _Result, _Result_capacity, &_Is_system, _UErr); }; return _Get_icu_string_impl(_Icu_fn, 32, _Result_len, _Err); } [[nodiscard]] _STD unique_ptr _Get_default_timezone( int32_t& _Result_len, __std_tzdb_error& _Err) noexcept { const auto _Icu_fn = [](UChar* _Result, int32_t _Result_capacity, UErrorCode* _UErr) { return __icu_ucal_getDefaultTimeZone(_Result, _Result_capacity, _UErr); }; return _Get_icu_string_impl(_Icu_fn, 32, _Result_len, _Err); } [[nodiscard]] _STD unique_ptr _Get_timezone_short_id( UCalendar* const _Cal, const bool _Is_daylight, int32_t& _Result_len, __std_tzdb_error& _Err) noexcept { const auto _Display_type = _Is_daylight ? UCalendarDisplayNameType::UCAL_SHORT_DST : UCalendarDisplayNameType::UCAL_SHORT_STANDARD; const auto _Icu_fn = [_Cal, _Display_type](UChar* _Result, int32_t _Result_capacity, UErrorCode* _UErr) { return __icu_ucal_getTimeZoneDisplayName(_Cal, _Display_type, nullptr, _Result, _Result_capacity, _UErr); }; return _Get_icu_string_impl(_Icu_fn, 12, _Result_len, _Err); } [[nodiscard]] _STD unique_ptr _Get_cal( const char* _Tz, const size_t _Tz_len, __std_tzdb_error& _Err) noexcept { const auto _Tz_name = _Allocate_narrow_to_wide(_Tz, static_cast(_Tz_len), _Err); if (_Tz_name == nullptr) { return nullptr; } UErrorCode _UErr{U_ZERO_ERROR}; _STD unique_ptr _Cal{ __icu_ucal_open(_Tz_name.get(), -1, nullptr, UCalendarType::UCAL_DEFAULT, &_UErr)}; if (U_FAILURE(_UErr)) { _Err = __std_tzdb_error::_Icu_error; } return _Cal; } template [[nodiscard]] _Ty* _Report_error(_STD unique_ptr<_Ty, _Dx>& _Info, const __std_tzdb_error _Err) noexcept { _Info->_Err = _Err; return _Info.release(); } template [[nodiscard]] _Ty* _Propagate_error(_STD unique_ptr<_Ty, _Dx>& _Info) noexcept { // a bad_alloc returns nullptr and does not set __std_tzdb_error return _Info->_Err == __std_tzdb_error::_Success ? nullptr : _Info.release(); } } // unnamed namespace extern "C" { [[nodiscard]] __std_tzdb_time_zones_info* __stdcall __std_tzdb_get_time_zones() noexcept { // On exit--- // _Info == nullptr --> bad_alloc // _Info->_Err == _Win_error --> failed, call GetLastError() // _Info->_Err == _Icu_error --> runtime_error interacting with ICU #pragma warning(push) #pragma warning(disable : 28182) // TRANSITION, VSO-1558277: Dereferencing NULL pointer. _STD unique_ptr<__std_tzdb_time_zones_info, _STD _Tzdb_deleter<__std_tzdb_time_zones_info>> _Info{ new (_STD nothrow) __std_tzdb_time_zones_info{}}; #pragma warning(pop) if (_Info == nullptr) { return nullptr; } if (_Acquire_icu_functions() < _Icu_api_level::_Has_icu_addresses) { return _Report_error(_Info, __std_tzdb_error::_Win_error); } UErrorCode _UErr{U_ZERO_ERROR}; _Info->_Version = __icu_ucal_getTZDataVersion(&_UErr); if (U_FAILURE(_UErr)) { return _Report_error(_Info, __std_tzdb_error::_Icu_error); } _STD unique_ptr _Enum{ __icu_ucal_openTimeZoneIDEnumeration(USystemTimeZoneType::UCAL_ZONE_TYPE_ANY, nullptr, nullptr, &_UErr)}; if (U_FAILURE(_UErr)) { return _Report_error(_Info, __std_tzdb_error::_Icu_error); } // uenum_count may be expensive but is required to pre-allocate arrays. const int32_t _Num_time_zones = __icu_uenum_count(_Enum.get(), &_UErr); if (U_FAILURE(_UErr)) { return _Report_error(_Info, __std_tzdb_error::_Icu_error); } _Info->_Num_time_zones = static_cast(_Num_time_zones); // value-init to ensure __std_tzdb_delete_time_zones() cleanup is valid if (const auto _Names = new (_STD nothrow) const char* [_Info->_Num_time_zones] {}) { _Info->_Names = _Names; } else { return nullptr; } // value-init to ensure __std_tzdb_delete_time_zones() cleanup is valid if (const auto _Links = new (_STD nothrow) const char* [_Info->_Num_time_zones] {}) { _Info->_Links = _Links; } else { return nullptr; } for (size_t _Name_idx = 0; _Name_idx < _Info->_Num_time_zones; ++_Name_idx) { int32_t _Elem_len{}; const auto _Elem = __icu_uenum_unext(_Enum.get(), &_Elem_len, &_UErr); if (U_FAILURE(_UErr) || _Elem == nullptr) { return _Report_error(_Info, __std_tzdb_error::_Icu_error); } _Info->_Names[_Name_idx] = _Allocate_wide_to_narrow(_Elem, _Elem_len, _Info->_Err); if (_Info->_Names[_Name_idx] == nullptr) { return _Propagate_error(_Info); } int32_t _Link_len{}; const auto _Link = _Get_canonical_id(_Elem, _Elem_len, _Link_len, _Info->_Err); if (_Link == nullptr) { return _Propagate_error(_Info); } if (_STD u16string_view{_Elem, static_cast(_Elem_len)} != _STD u16string_view{_Link.get(), static_cast(_Link_len)}) { _Info->_Links[_Name_idx] = _Allocate_wide_to_narrow(_Link.get(), _Link_len, _Info->_Err); if (_Info->_Links[_Name_idx] == nullptr) { return _Propagate_error(_Info); } } } return _Info.release(); } void __stdcall __std_tzdb_delete_time_zones(__std_tzdb_time_zones_info* const _Info) noexcept { if (_Info != nullptr) { if (_Info->_Names != nullptr) { for (size_t _Idx = 0; _Idx < _Info->_Num_time_zones; ++_Idx) { delete[] _Info->_Names[_Idx]; } delete[] _Info->_Names; } if (_Info->_Links != nullptr) { for (size_t _Idx = 0; _Idx < _Info->_Num_time_zones; ++_Idx) { delete[] _Info->_Links[_Idx]; } delete[] _Info->_Links; } delete _Info; } } [[nodiscard]] __std_tzdb_current_zone_info* __stdcall __std_tzdb_get_current_zone() noexcept { // On exit--- // _Info == nullptr --> bad_alloc // _Info->_Err == _Win_error --> failed, call GetLastError() // _Info->_Err == _Icu_error --> runtime_error interacting with ICU #pragma warning(push) #pragma warning(disable : 28182) // TRANSITION, VSO-1558277: Dereferencing NULL pointer. _STD unique_ptr<__std_tzdb_current_zone_info, _STD _Tzdb_deleter<__std_tzdb_current_zone_info>> _Info{ new (_STD nothrow) __std_tzdb_current_zone_info{}}; #pragma warning(pop) if (_Info == nullptr) { return nullptr; } if (_Acquire_icu_functions() < _Icu_api_level::_Has_icu_addresses) { return _Report_error(_Info, __std_tzdb_error::_Win_error); } int32_t _Id_len{}; const auto _Id_name = _Get_default_timezone(_Id_len, _Info->_Err); if (_Id_name == nullptr) { return _Propagate_error(_Info); } _Info->_Tz_name = _Allocate_wide_to_narrow(_Id_name.get(), _Id_len, _Info->_Err); if (_Info->_Tz_name == nullptr) { return _Propagate_error(_Info); } return _Info.release(); } void __stdcall __std_tzdb_delete_current_zone(__std_tzdb_current_zone_info* const _Info) noexcept { if (_Info) { delete[] _Info->_Tz_name; delete _Info; } } [[nodiscard]] __std_tzdb_sys_info* __stdcall __std_tzdb_get_sys_info( const char* _Tz, const size_t _Tz_len, __std_tzdb_epoch_milli _Sys) noexcept { // On exit--- // _Info == nullptr --> bad_alloc // _Info->_Err == _Win_error --> failed, call GetLastError() // _Info->_Err == _Icu_error --> runtime_error interacting with ICU #pragma warning(push) #pragma warning(disable : 28182) // TRANSITION, VSO-1558277: Dereferencing NULL pointer. _STD unique_ptr<__std_tzdb_sys_info, _STD _Tzdb_deleter<__std_tzdb_sys_info>> _Info{ new (_STD nothrow) __std_tzdb_sys_info{}}; #pragma warning(pop) if (_Info == nullptr) { return nullptr; } if (_Acquire_icu_functions() < _Icu_api_level::_Has_icu_addresses) { return _Report_error(_Info, __std_tzdb_error::_Win_error); } // Get the option stored after the time zone name. If there's no option, _Tz[_Tz_len] is the null terminator in the // std::string, and will be treated the same as __std_tzdb_sys_info_type::_Full. const auto _Type = static_cast<__std_tzdb_sys_info_type>(_Tz[_Tz_len]); // TRANSITION, vNext // Profiling shows that _Get_cal is a hot path. Its result should be cached (preferably in the time_zone object). const auto _Cal = _Get_cal(_Tz, _Tz_len, _Info->_Err); if (_Cal == nullptr) { return _Propagate_error(_Info); } UErrorCode _UErr{}; __icu_ucal_setMillis(_Cal.get(), _Sys, &_UErr); if (U_FAILURE(_UErr)) { return _Report_error(_Info, __std_tzdb_error::_Icu_error); } const auto _Is_daylight = __icu_ucal_inDaylightTime(_Cal.get(), &_UErr); if (U_FAILURE(_UErr)) { return _Report_error(_Info, __std_tzdb_error::_Icu_error); } _Info->_Save = _Is_daylight ? __icu_ucal_get(_Cal.get(), UCalendarDateFields::UCAL_DST_OFFSET, &_UErr) : 0; if (U_FAILURE(_UErr)) { return _Report_error(_Info, __std_tzdb_error::_Icu_error); } _Info->_Offset = __icu_ucal_get(_Cal.get(), UCalendarDateFields::UCAL_ZONE_OFFSET, &_UErr) + _Info->_Save; if (U_FAILURE(_UErr)) { return _Report_error(_Info, __std_tzdb_error::_Icu_error); } if (_Type == __std_tzdb_sys_info_type::_Offset_only) { return _Info.release(); } UDate _Transition{}; _Info->_Begin = __icu_ucal_getTimeZoneTransitionDate(_Cal.get(), UTimeZoneTransitionType::UCAL_TZ_TRANSITION_PREVIOUS_INCLUSIVE, &_Transition, &_UErr) ? _Transition : U_DATE_MIN; if (U_FAILURE(_UErr)) { return _Report_error(_Info, __std_tzdb_error::_Icu_error); } _Info->_End = __icu_ucal_getTimeZoneTransitionDate( _Cal.get(), UTimeZoneTransitionType::UCAL_TZ_TRANSITION_NEXT, &_Transition, &_UErr) ? _Transition : U_DATE_MAX; if (U_FAILURE(_UErr)) { return _Report_error(_Info, __std_tzdb_error::_Icu_error); } if (_Type == __std_tzdb_sys_info_type::_Offset_and_range) { return _Info.release(); } int32_t _Abbrev_len{}; const auto _Abbrev = _Get_timezone_short_id(_Cal.get(), _Is_daylight, _Abbrev_len, _Info->_Err); if (_Abbrev == nullptr) { return _Propagate_error(_Info); } _Info->_Abbrev = _Allocate_wide_to_narrow(_Abbrev.get(), _Abbrev_len, _Info->_Err); if (_Info->_Abbrev == nullptr) { const auto _Abs_offset = _Info->_Offset < 0 ? -_Info->_Offset : _Info->_Offset; const auto _Offset_in_minutes = _Abs_offset / (60 * 1000); const auto _Hours = _Offset_in_minutes / 60; const auto _Mins = _Offset_in_minutes % 60; _STD unique_ptr _Fallback_abbrev{new (_STD nothrow) char[_Mins == 0 ? 4 : 6]}; if (_Fallback_abbrev == nullptr) { return nullptr; } _Fallback_abbrev[0] = _Info->_Offset < 0 ? '-' : '+'; _Fallback_abbrev[1] = static_cast('0' + _Hours / 10); _Fallback_abbrev[2] = static_cast('0' + _Hours % 10); if (_Mins == 0) { _Fallback_abbrev[3] = '\0'; } else { _Fallback_abbrev[3] = static_cast('0' + _Mins / 10); _Fallback_abbrev[4] = static_cast('0' + _Mins % 10); _Fallback_abbrev[5] = '\0'; } _Info->_Err = __std_tzdb_error::_Success; _Info->_Abbrev = _Fallback_abbrev.release(); } return _Info.release(); } void __stdcall __std_tzdb_delete_sys_info(__std_tzdb_sys_info* const _Info) noexcept { if (_Info) { delete[] _Info->_Abbrev; delete _Info; } } [[nodiscard]] __std_tzdb_leap_info* __stdcall __std_tzdb_get_leap_seconds( const size_t prev_reg_ls_size, size_t* const current_reg_ls_size) noexcept { // On exit--- // *current_reg_ls_size <= prev_reg_ls_size, reg_ls_data == nullptr --> no new data // *current_reg_ls_size > prev_reg_ls_size, reg_ls_data != nullptr --> new data, successfully read // *current_reg_ls_size == 0, reg_ls_data != nullptr --> new data, failed reading // *current_reg_ls_size > prev_reg_ls_size, reg_ls_data == nullptr --> new data, failed allocation constexpr auto reg_key_name = LR"(SYSTEM\CurrentControlSet\Control\LeapSecondInformation)"; constexpr auto reg_subkey_name = L"LeapSeconds"; *current_reg_ls_size = 0; HKEY leap_sec_key = nullptr; LSTATUS status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_key_name, 0, KEY_READ, &leap_sec_key); if (status != ERROR_SUCCESS) { // May not exist on older systems. Treat this as equivalent to the key existing but with no data. return nullptr; } DWORD byte_size = 0; status = RegQueryValueExW(leap_sec_key, reg_subkey_name, nullptr, nullptr, nullptr, &byte_size); static_assert(sizeof(__std_tzdb_leap_info) == 12); const auto ls_size = byte_size / 12; *current_reg_ls_size = ls_size; __std_tzdb_leap_info* reg_ls_data = nullptr; if ((status == ERROR_SUCCESS || status == ERROR_MORE_DATA) && ls_size > prev_reg_ls_size) { try { reg_ls_data = new __std_tzdb_leap_info[ls_size]; status = RegQueryValueExW( leap_sec_key, reg_subkey_name, nullptr, nullptr, reinterpret_cast(reg_ls_data), &byte_size); if (status != ERROR_SUCCESS) { *current_reg_ls_size = 0; } } catch (...) { } } RegCloseKey(leap_sec_key); if (status != ERROR_SUCCESS) { SetLastError(status); } return reg_ls_data; } void __stdcall __std_tzdb_delete_leap_seconds(__std_tzdb_leap_info* _Info) noexcept { delete[] _Info; } [[nodiscard]] void* __stdcall __std_calloc_crt(const size_t count, const size_t size) noexcept { return _calloc_crt(count, size); } void __stdcall __std_free_crt(void* p) noexcept { _free_crt(p); } } // extern "C"