diff --git a/stl/inc/chrono b/stl/inc/chrono index 3d13b2b8620..02af136b01e 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2377,9 +2377,8 @@ namespace chrono { _NODISCARD sys_info _Get_info(const _Duration& _Dur) const { using _Internal_duration = duration<__std_tzdb_epoch_milli, milli>; const auto _Internal_dur = _CHRONO duration_cast<_Internal_duration>(_Dur); - const unique_ptr<__std_tzdb_sys_info, decltype(&__std_tzdb_delete_sys_info)> _Info{ - __std_tzdb_get_sys_info(_Name.c_str(), _Name.length(), _Internal_dur.count()), - &__std_tzdb_delete_sys_info}; + const unique_ptr<__std_tzdb_sys_info, _Tzdb_deleter<__std_tzdb_sys_info>> _Info{ + __std_tzdb_get_sys_info(_Name.c_str(), _Name.length(), _Internal_dur.count())}; if (_Info == nullptr) { _Xbad_alloc(); } else if (_Info->_Err == __std_tzdb_error::_Win_error) { @@ -2550,8 +2549,8 @@ namespace chrono { // [time.zone.db] _NODISCARD inline string _Xtzdb_generate_current_zone() { - unique_ptr<__std_tzdb_current_zone_info, decltype(&__std_tzdb_delete_current_zone)> _Info{ - __std_tzdb_get_current_zone(), &__std_tzdb_delete_current_zone}; + unique_ptr<__std_tzdb_current_zone_info, _Tzdb_deleter<__std_tzdb_current_zone_info>> _Info{ + __std_tzdb_get_current_zone()}; if (_Info == nullptr) { _Xbad_alloc(); } else if (_Info->_Err == __std_tzdb_error::_Win_error) { @@ -2599,8 +2598,8 @@ namespace chrono { }; _NODISCARD inline tuple _Xtzdb_generate_time_zones() { - unique_ptr<__std_tzdb_time_zones_info, decltype(&__std_tzdb_delete_time_zones)> _Info{ - __std_tzdb_get_time_zones(), &__std_tzdb_delete_time_zones}; + unique_ptr<__std_tzdb_time_zones_info, _Tzdb_deleter<__std_tzdb_time_zones_info>> _Info{ + __std_tzdb_get_time_zones()}; if (_Info == nullptr) { _Xbad_alloc(); } else if (_Info->_Err == __std_tzdb_error::_Win_error) { @@ -2666,9 +2665,8 @@ namespace chrono { (_STD max)(_Current_size, _STD size(_Known_leap_seconds)) - _Pre_2018_count; size_t _Reg_post_2018_ls_size; // number of post-2018 LSs found in the registry - unique_ptr<__std_tzdb_registry_leap_info[], decltype(&__std_tzdb_delete_reg_leap_seconds)> _Reg_ls_data{ - __std_tzdb_get_reg_leap_seconds(_Known_post_2018_ls_size, &_Reg_post_2018_ls_size), - &__std_tzdb_delete_reg_leap_seconds}; + unique_ptr<__std_tzdb_registry_leap_info[], _Tzdb_deleter<__std_tzdb_registry_leap_info[]>> _Reg_ls_data{ + __std_tzdb_get_reg_leap_seconds(_Known_post_2018_ls_size, &_Reg_post_2018_ls_size)}; if (_Reg_post_2018_ls_size > _Known_post_2018_ls_size && !_Reg_ls_data) { _Xbad_alloc(); // registry has new data, but failed to allocate storage @@ -2700,6 +2698,11 @@ namespace chrono { return {_STD move(_Leap_sec_info), _All_ls_positive}; } + _NODISCARD inline string _Xtzdb_update_version(const string_view _Version, const size_t _Num_leap_seconds) { + string _Icu_version{_Version.substr(0, _Version.find_last_of('.'))}; + return _STD move(_Icu_version) + "." + _STD to_string(_Num_leap_seconds); + } + // TRANSITION: work in progress // CLASS tzdb_list class tzdb_list { @@ -2713,8 +2716,9 @@ namespace chrono { tzdb_list& operator=(const tzdb_list&) = delete; tzdb_list() { - auto [_Version, _Zones, _Links] = _Xtzdb_generate_time_zones(); - auto [_Leap_sec, _All_ls_positive] = _Xtzdb_generate_leap_seconds(0); + auto [_Icu_version, _Zones, _Links] = _Xtzdb_generate_time_zones(); + auto [_Leap_sec, _All_ls_positive] = _Xtzdb_generate_leap_seconds(0); + auto _Version = _Icu_version + "." + _STD to_string(_Leap_sec.size()); _Tzdb_list.emplace_front(tzdb{ _STD move(_Version), _STD move(_Zones), _STD move(_Links), _STD move(_Leap_sec), _All_ls_positive}); } @@ -2724,6 +2728,26 @@ namespace chrono { return _Tzdb_list.front(); } + const_iterator erase_after(const_iterator _Where) noexcept /* strengthened */ { + return _Tzdb_list.erase_after(_Where); + } + + _NODISCARD const_iterator begin() const noexcept { + return _Tzdb_list.begin(); + } + + _NODISCARD const_iterator end() const noexcept { + return _Tzdb_list.end(); + } + + _NODISCARD const_iterator cbegin() const noexcept { + return _Tzdb_list.cbegin(); + } + + _NODISCARD const_iterator cend() const noexcept { + return _Tzdb_list.cend(); + } + template void _Emplace_front(_ArgsTy&&... _Args) { _Unique_lock _Lk(_Tzdb_mutex); @@ -2743,8 +2767,9 @@ namespace chrono { _Tzdb.links.begin(), _Tzdb.links.end(), _STD back_inserter(_Links), [](const auto& _Link) { return time_zone_link{_Link.name(), _Link.target()}; }); - _Tzdb_list.emplace_front( - tzdb{_Tzdb.version, _STD move(_Zones), _STD move(_Links), _STD move(_Leap_sec), _All_ls_positive}); + auto _Version = _Xtzdb_update_version(_Tzdb.version, _Leap_sec.size()); + _Tzdb_list.emplace_front(tzdb{ + _STD move(_Version), _STD move(_Zones), _STD move(_Links), _STD move(_Leap_sec), _All_ls_positive}); } return _Tzdb_list.front(); } @@ -2827,6 +2852,16 @@ namespace chrono { return _CHRONO get_tzdb_list().front(); } + // FUNCTION locate_zone + _NODISCARD inline const time_zone* locate_zone(string_view _Tz_name) { + return _CHRONO get_tzdb().locate_zone(_Tz_name); + } + + // FUNCTION current_zone + _NODISCARD inline const time_zone* current_zone() { + return _CHRONO get_tzdb().current_zone(); + } + // FUNCTION reload_tzdb inline const tzdb& reload_tzdb() { try { @@ -2836,6 +2871,14 @@ namespace chrono { } } + // FUNCTION remote_version + _NODISCARD inline string remote_version() { + const auto& _Tzdb = _CHRONO get_tzdb(); + const auto& _Version = _Tzdb.version; + const auto [_Leap_sec, _Ignored] = _Xtzdb_generate_leap_seconds(_Tzdb.leap_seconds.size()); + return _Leap_sec.empty() ? _Version : _Xtzdb_update_version(_Version, _Leap_sec.size()); + } + // [time.zone.zonedtraits] // STRUCT TEMPLATE zoned_traits diff --git a/stl/inc/xtzdb.h b/stl/inc/xtzdb.h index a0f1ffb4d8e..73747054ced 100644 --- a/stl/inc/xtzdb.h +++ b/stl/inc/xtzdb.h @@ -78,7 +78,6 @@ void __stdcall __std_tzdb_delete_sys_info(__std_tzdb_sys_info* _Info) noexcept; __std_tzdb_registry_leap_info* __stdcall __std_tzdb_get_reg_leap_seconds( size_t _Prev_reg_ls_size, size_t* _Current_reg_ls_size) noexcept; - void __stdcall __std_tzdb_delete_reg_leap_seconds(__std_tzdb_registry_leap_info* _Rlsi) noexcept; _NODISCARD void* __stdcall __std_calloc_crt(size_t _Count, size_t _Size) noexcept; @@ -88,6 +87,37 @@ _END_EXTERN_C _STD_BEGIN +template +struct _Tzdb_deleter; + +template <> +struct _Tzdb_deleter<__std_tzdb_time_zones_info> { + void operator()(__std_tzdb_time_zones_info* _Info) const noexcept { + __std_tzdb_delete_time_zones(_Info); + } +}; + +template <> +struct _Tzdb_deleter<__std_tzdb_current_zone_info> { + void operator()(__std_tzdb_current_zone_info* _Info) const noexcept { + __std_tzdb_delete_current_zone(_Info); + } +}; + +template <> +struct _Tzdb_deleter<__std_tzdb_sys_info> { + void operator()(__std_tzdb_sys_info* _Info) const noexcept { + __std_tzdb_delete_sys_info(_Info); + } +}; + +template <> +struct _Tzdb_deleter<__std_tzdb_registry_leap_info[]> { + void operator()(__std_tzdb_registry_leap_info* _Info) const noexcept { + __std_tzdb_delete_reg_leap_seconds(_Info); + } +}; + template class _Crt_allocator { public: diff --git a/stl/src/tzdb.cpp b/stl/src/tzdb.cpp index 0e13a44636c..c68f6d61fff 100644 --- a/stl/src/tzdb.cpp +++ b/stl/src/tzdb.cpp @@ -182,6 +182,18 @@ namespace { 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(); @@ -293,16 +305,16 @@ namespace { return _Get_icu_string_impl(_Icu_fn, 12, _Result_len, _Err); } - _NODISCARD _STD unique_ptr _Get_cal( + _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, &__icu_ucal_close}; + return nullptr; } UErrorCode _UErr{U_ZERO_ERROR}; - _STD unique_ptr _Cal{ - __icu_ucal_open(_Tz_name.get(), -1, nullptr, UCalendarType::UCAL_DEFAULT, &_UErr), &__icu_ucal_close}; + _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; } @@ -330,8 +342,8 @@ _NODISCARD __std_tzdb_time_zones_info* __stdcall __std_tzdb_get_time_zones() noe // _Info == nullptr --> bad_alloc // _Info->_Err == _Win_error --> failed, call GetLastError() // _Info->_Err == _Icu_error --> runtime_error interacting with ICU - _STD unique_ptr<__std_tzdb_time_zones_info, decltype(&__std_tzdb_delete_time_zones)> _Info{ - new (_STD nothrow) __std_tzdb_time_zones_info{}, &__std_tzdb_delete_time_zones}; + _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{}}; if (_Info == nullptr) { return nullptr; } @@ -346,9 +358,8 @@ _NODISCARD __std_tzdb_time_zones_info* __stdcall __std_tzdb_get_time_zones() noe return _Report_error(_Info, __std_tzdb_error::_Icu_error); } - _STD unique_ptr _Enum{ - __icu_ucal_openTimeZoneIDEnumeration(USystemTimeZoneType::UCAL_ZONE_TYPE_ANY, nullptr, nullptr, &_UErr), - &__icu_uenum_close}; + _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); } @@ -429,8 +440,8 @@ _NODISCARD __std_tzdb_current_zone_info* __stdcall __std_tzdb_get_current_zone() // _Info == nullptr --> bad_alloc // _Info->_Err == _Win_error --> failed, call GetLastError() // _Info->_Err == _Icu_error --> runtime_error interacting with ICU - _STD unique_ptr<__std_tzdb_current_zone_info, decltype(&__std_tzdb_delete_current_zone)> _Info{ - new (_STD nothrow) __std_tzdb_current_zone_info{}, &__std_tzdb_delete_current_zone}; + _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{}}; if (_Info == nullptr) { return nullptr; } @@ -466,8 +477,8 @@ _NODISCARD __std_tzdb_sys_info* __stdcall __std_tzdb_get_sys_info( // _Info == nullptr --> bad_alloc // _Info->_Err == _Win_error --> failed, call GetLastError() // _Info->_Err == _Icu_error --> runtime_error interacting with ICU - _STD unique_ptr<__std_tzdb_sys_info, decltype(&__std_tzdb_delete_sys_info)> _Info{ - new (_STD nothrow) __std_tzdb_sys_info{}, &__std_tzdb_delete_sys_info}; + _STD unique_ptr<__std_tzdb_sys_info, _STD _Tzdb_deleter<__std_tzdb_sys_info>> _Info{ + new (_STD nothrow) __std_tzdb_sys_info{}}; if (_Info == nullptr) { return nullptr; } diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/test.cpp index f6423830a00..a2fa09ee3d5 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/test.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -55,17 +56,52 @@ void try_locate_invalid_zone(const tzdb& my_tzdb, string_view name) { } } -void timezone_names_test() { - const auto& my_tzdb = get_tzdb(); +void timezone_tzdb_list_test() { + const auto& my_tzdb_list = get_tzdb_list(); + + // only one entry in the list unless leap seconds were to change + assert(&my_tzdb_list.front() == &get_tzdb()); + assert(&my_tzdb_list.front() == &reload_tzdb()); + assert(++my_tzdb_list.begin() == my_tzdb_list.end()); + assert(++my_tzdb_list.cbegin() == my_tzdb_list.cend()); +} +void timezone_version_test() { + const auto& my_tzdb = get_tzdb(); assert(my_tzdb.version.empty() == false); + // version should end in .X where X == number of leap seconds + const auto pos = my_tzdb.version.find_last_of('.'); + assert(pos != decltype(my_tzdb.version)::npos); + const string leap_seconds{my_tzdb.version, pos + 1}; + assert(leap_seconds.empty() == false); + assert(leap_seconds == to_string(my_tzdb.leap_seconds.size())); + + // remote version will only differ if leap seconds info changes, will not occur in tests + const auto& reloaded_tzdb = reload_tzdb(); + assert(reloaded_tzdb.version.empty() == false); + assert(&reloaded_tzdb == &my_tzdb); + + const auto& remote_ver = remote_version(); + assert(remote_ver.empty() == false); + assert(remote_ver == my_tzdb.version); +} + +void timezone_names_test() { + const auto& my_tzdb = get_tzdb(); + test_time_zone_and_link(my_tzdb, "Asia/Thimphu", "Asia/Thimbu"); test_time_zone_and_link(my_tzdb, "America/Tijuana", "America/Ensenada"); - const auto current_zone = my_tzdb.current_zone(); - assert(current_zone != nullptr); - assert(current_zone->name().empty() == false); + const auto curr_zone = current_zone(); + assert(curr_zone != nullptr); + assert(curr_zone->name().empty() == false); + assert(curr_zone == my_tzdb.current_zone()); + + const auto located_zone = locate_zone("UTC"); + assert(located_zone != nullptr); + assert(located_zone->name() == "Etc/UTC"); + assert(located_zone == my_tzdb.locate_zone("UTC")); try_locate_invalid_zone(my_tzdb, "Non/Existent"); @@ -371,6 +407,8 @@ void timezone_precision_test() { bool test() { try { + timezone_tzdb_list_test(); + timezone_version_test(); timezone_names_test(); timezone_sys_info_test(); timezone_to_local_test();