From 74f5ae51d35de531f2c80581ff8f3ddd4718987d Mon Sep 17 00:00:00 2001 From: Daniel Winsor Date: Fri, 12 Mar 2021 14:32:42 -0800 Subject: [PATCH 1/3] Initial draft --- stl/inc/chrono | 174 +++++++++++ stl/inc/xtzdb.h | 15 + stl/src/msvcp_atomic_wait.src | 4 +- stl/src/tzdb.cpp | 256 +++++++++++++--- .../test.cpp | 285 ++++++++++++++++++ .../timezone_data.h | 133 ++++++++ 6 files changed, 826 insertions(+), 41 deletions(-) create mode 100644 tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/timezone_data.h diff --git a/stl/inc/chrono b/stl/inc/chrono index d62210e67ac..4538f51a27d 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2215,8 +2215,49 @@ namespace chrono { return hours{_Ret}; } + // [time.zone.info] + + // STRUCT sys_info + struct sys_info { + sys_seconds begin; + sys_seconds end; + seconds offset; + minutes save; + string abbrev; + }; + + // STRUCT local_info + struct local_info { + static constexpr int unique = 0; + static constexpr int nonexistent = 1; + static constexpr int ambiguous = 2; + + int result; + sys_info first; + sys_info second; + }; + + // CLASS nonexistent_local_time + class nonexistent_local_time : public runtime_error { + public: + template + explicit nonexistent_local_time(const local_time&, const local_info&) + : runtime_error("TRANSITION: work in progress") {} + }; + + // CLASS ambiguous_local_time + class ambiguous_local_time : public runtime_error { + public: + template + explicit ambiguous_local_time(const local_time&, const local_info&) + : runtime_error("TRANSITION: work in progress") {} + }; + // [time.zone.timezone] + // ENUM CLASS choose + enum class choose { earliest, latest }; + // CLASS time_zone class time_zone { public: @@ -2229,7 +2270,140 @@ namespace chrono { return _Name; } + template + _NODISCARD sys_info get_info(const sys_time<_Duration>& _Sys) const { + return _Get_info(_Sys.time_since_epoch()); + } + + template + _NODISCARD local_info get_info(const local_time<_Duration>& _Local) const { + local_info _Info{}; + _Info.first = _Get_info(_Local.time_since_epoch()); + + const sys_seconds _Local_sys{duration_cast(_Local.time_since_epoch())}; + const auto _Curr_sys = _Local_sys - _Info.first.offset; + if (_Info.first.begin != _Min_seconds && _Curr_sys < _Info.first.begin + days{1}) { + // get previous transition information + _Info.second = get_info(_Info.first.begin - seconds{1}); + + const auto _Transition = _Info.first.begin; + const auto _Prev_sys = _Local_sys - _Info.second.offset; + if (_Curr_sys >= _Transition) { + if (_Prev_sys < _Transition) { + _Info.result = local_info::ambiguous; + std::swap(_Info.first, _Info.second); + } else { + _Info.result = local_info::unique; + _Info.second = {}; + } + } else { + if (_Prev_sys >= _Transition) { + _Info.result = local_info::nonexistent; + std::swap(_Info.first, _Info.second); + } else { + _Info.result = local_info::unique; + _Info.first = std::move(_Info.second); + _Info.second = {}; + } + } + } else if (_Info.first.end != _Max_seconds && _Curr_sys > _Info.first.end - days{1}) { + // get next transition information + _Info.second = get_info(_Info.first.end + seconds{1}); + + const auto _Transition = _Info.first.end; + const auto _Next_sys = _Local_sys - _Info.second.offset; + if (_Curr_sys < _Transition) { + if (_Next_sys >= _Transition) { + _Info.result = local_info::ambiguous; + } else { + _Info.result = local_info::unique; + _Info.second = {}; + } + } else { + if (_Next_sys < _Transition) { + _Info.result = local_info::nonexistent; + } else { + _Info.result = local_info::unique; + _Info.first = std::move(_Info.second); + _Info.second = {}; + } + } + } else { + // local time is contained inside of first transition boundaries by at least 1 day + _Info.result = local_info::unique; + _Info.second = {}; + } + + return _Info; + } + + template + _NODISCARD sys_time> to_sys(const local_time<_Duration>& _Local) const { + auto _Info = get_info(_Local); + if (_Info.result == local_info::nonexistent) { + throw nonexistent_local_time(_Local, _Info); + } else if (_Info.result == local_info::ambiguous) { + throw ambiguous_local_time(_Local, _Info); + } + + return sys_time>{_Local.time_since_epoch()} - _Info.first.offset; + } + + template + _NODISCARD sys_time> to_sys( + const local_time<_Duration>& _Local, choose _Choose) const { + auto _Info = get_info(_Local); + auto _Result = sys_time<_Duration>{_Local.time_since_epoch()}; + if (_Info.result == local_info::nonexistent) { + return _Info.first.end; + } + + auto _Offset = (_Info.result == local_info::unique || _Choose == choose::earliest) ? _Info.first.offset + : _Info.second.offset; + return sys_time>{_Local.time_since_epoch()} - _Offset; + } + + template + _NODISCARD local_time> to_local(const sys_time<_Duration>& _Sys) const { + auto _Info = get_info(_Sys); + return local_time>{(_Sys + _Info.offset).time_since_epoch()}; + } + + // FIXME: year::max() + year::min() + static constexpr sys_seconds _Min_seconds{sys_days{year{-32767} / January / 1}}; + static constexpr sys_seconds _Max_seconds{sys_days{year{32767} / December / 31}}; + private: + template + _NODISCARD sys_info _Get_info(const _Duration& _Dur) const { + using _Internal_duration = duration<__std_tzdb_epoch_milli, std::milli>; + auto _Internal_dur = duration_cast<_Internal_duration>(_Dur); + std::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); + if (_Info == nullptr) { + _Xbad_alloc(); + } else if (_Info->_Err == __std_tzdb_error::_Win_error) { + _XGetLastError(); + } else if (_Info->_Err == __std_tzdb_error::_Icu_error) { + _Xruntime_error("Internal error loading IANA database information"); + } + + static constexpr auto _Min_internal = + duration_cast<_Internal_duration>(_Min_seconds.time_since_epoch()).count(); + static constexpr auto _Max_internal = + duration_cast<_Internal_duration>(_Max_seconds.time_since_epoch()).count(); + return {.begin = _Info->_Begin <= _Min_internal + ? _Min_seconds + : sys_seconds{duration_cast(_Internal_duration{_Info->_Begin})}, + .end = _Info->_End >= _Max_internal + ? _Max_seconds + : sys_seconds{duration_cast(_Internal_duration{_Info->_End})}, + .offset = duration_cast(_Internal_duration{_Info->_Offset}), + .save = duration_cast(_Internal_duration{_Info->_Save}), + .abbrev = std::string{_Info->_Abbrev}}; + } + string _Name; }; diff --git a/stl/inc/xtzdb.h b/stl/inc/xtzdb.h index abd6aeaeb82..59308ffedc4 100644 --- a/stl/inc/xtzdb.h +++ b/stl/inc/xtzdb.h @@ -20,6 +20,8 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new +typedef double __std_tzdb_epoch_milli; + struct __std_tzdb_registry_leap_info { uint16_t _Year; uint16_t _Month; @@ -53,6 +55,15 @@ struct __std_tzdb_current_zone_info { const char* _Tz_name; }; +struct __std_tzdb_sys_info { + __std_tzdb_error _Err; + __std_tzdb_epoch_milli _Begin; + __std_tzdb_epoch_milli _End; + int32_t _Offset; + int32_t _Save; + const char* _Abbrev; +}; + _EXTERN_C _NODISCARD __std_tzdb_time_zones_info* __stdcall __std_tzdb_get_time_zones() noexcept; @@ -61,6 +72,10 @@ void __stdcall __std_tzdb_delete_time_zones(__std_tzdb_time_zones_info* _Info) n _NODISCARD __std_tzdb_current_zone_info* __stdcall __std_tzdb_get_current_zone() noexcept; void __stdcall __std_tzdb_delete_current_zone(__std_tzdb_current_zone_info* _Info) noexcept; +_NODISCARD __std_tzdb_sys_info* __stdcall __std_tzdb_get_sys_info( + const char* _Tz, size_t _Tz_len, __std_tzdb_epoch_milli _Local) noexcept; +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; diff --git a/stl/src/msvcp_atomic_wait.src b/stl/src/msvcp_atomic_wait.src index cd8f13dcaf1..cbcd3153c49 100644 --- a/stl/src/msvcp_atomic_wait.src +++ b/stl/src/msvcp_atomic_wait.src @@ -23,7 +23,6 @@ EXPORTS __std_calloc_crt __std_close_threadpool_work __std_create_threadpool_work - __std_tzdb_delete_reg_leap_seconds __std_execution_wait_on_uchar __std_execution_wake_by_address_all __std_free_crt @@ -31,8 +30,11 @@ EXPORTS __std_release_shared_mutex_for_instance __std_submit_threadpool_work __std_tzdb_delete_current_zone + __std_tzdb_delete_reg_leap_seconds + __std_tzdb_delete_sys_info __std_tzdb_delete_time_zones __std_tzdb_get_current_zone __std_tzdb_get_reg_leap_seconds + __std_tzdb_get_sys_info __std_tzdb_get_time_zones __std_wait_for_threadpool_work_callbacks diff --git a/stl/src/tzdb.cpp b/stl/src/tzdb.cpp index ab065abdeec..93410ee7552 100644 --- a/stl/src/tzdb.cpp +++ b/stl/src/tzdb.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include +#include #include #include #include @@ -22,10 +23,17 @@ namespace { }; 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}; @@ -59,13 +67,22 @@ namespace { 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_getTZDataVersion, "ucal_getTZDataVersion", _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); @@ -90,6 +107,16 @@ namespace { return _Level; } + _NODISCARD 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); @@ -101,20 +128,48 @@ namespace { 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); } - _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 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); } _NODISCARD void __icu_uenum_close(UEnumeration* en) noexcept { const auto _Fun = _Icu_functions._Pfn_uenum_close.load(_STD memory_order_relaxed); - return _Fun(en); + _Fun(en); } _NODISCARD int32_t __icu_uenum_count(UEnumeration* en, UErrorCode* ec) noexcept { @@ -154,56 +209,52 @@ namespace { return _Data.release(); } - _NODISCARD _STD unique_ptr _Get_canonical_id( - const char16_t* _Id, const int32_t _Len, int32_t& _Result_len, __std_tzdb_error& _Err) noexcept { - constexpr int32_t _Link_buf_len = 32; - _STD unique_ptr _Link_buf{new (_STD nothrow) char16_t[_Link_buf_len]}; - if (_Link_buf == nullptr) { + _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; } - UErrorCode _UErr{U_ZERO_ERROR}; - UBool _Is_system{}; - _Result_len = __icu_ucal_getCanonicalTimeZoneID(_Id, _Len, _Link_buf.get(), _Link_buf_len, &_Is_system, &_UErr); - if (_UErr == U_BUFFER_OVERFLOW_ERROR && _Result_len > 0) { - _Link_buf.reset(new (_STD nothrow) char16_t[_Result_len + 1]); - if (_Link_buf == nullptr) { - return nullptr; - } + _STD unique_ptr _Data{new (_STD nothrow) char16_t[_Count._Len + 1]}; + if (_Data == nullptr) { + return nullptr; + } - _UErr = U_ZERO_ERROR; // reset error. - _Result_len = - __icu_ucal_getCanonicalTimeZoneID(_Id, _Len, _Link_buf.get(), _Result_len, &_Is_system, &_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; + _Data[_Count._Len] = u'\0'; + const auto _Ouput_as_wchar = reinterpret_cast(_Data.get()); + + const auto _Result = + __std_fs_convert_narrow_to_wide(_Code_page, _Input, _Input_len, _Ouput_as_wchar, _Count._Len); + if (_Result._Err != __std_win_error::_Success) { + _Err = __std_tzdb_error::_Win_error; return nullptr; } - return _Link_buf; + return _Data; } - _NODISCARD _STD unique_ptr _Get_default_timezone( - int32_t& _Result_len, __std_tzdb_error& _Err) noexcept { - constexpr int32_t _Name_buf_len = 32; - _STD unique_ptr _Name_buf{new (_STD nothrow) char16_t[_Name_buf_len]}; - if (_Name_buf == nullptr) { + using _Icu_get_string_fn = std::function; + + _NODISCARD _STD unique_ptr _Get_icu_string_impl(const _Icu_get_string_fn& _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_ucal_getDefaultTimeZone(_Name_buf.get(), _Name_buf_len, &_UErr); + _Result_len = _Icu_fn(_Str_buf.get(), _Initial_buf_len, &_UErr); if (_UErr == U_BUFFER_OVERFLOW_ERROR && _Result_len > 0) { - _Name_buf.reset(new (_STD nothrow) char16_t[_Result_len + 1]); - if (_Name_buf == nullptr) { + _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_ucal_getDefaultTimeZone(_Name_buf.get(), _Name_buf_len, &_UErr); + _Result_len = _Icu_fn(_Str_buf.get(), _Result_len, &_UErr); if (U_FAILURE(_UErr)) { _Err = __std_tzdb_error::_Icu_error; return nullptr; @@ -213,17 +264,62 @@ namespace { return nullptr; } - return _Name_buf; + 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 { + _Icu_get_string_fn _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 { + _Icu_get_string_fn _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; + _Icu_get_string_fn _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); + } + + _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, _Tz_len, _Err); + if (_Tz_name == nullptr) { + return {nullptr, &__icu_ucal_close}; + } + + UErrorCode _UErr{U_ZERO_ERROR}; + _STD unique_ptr _Cal{ + __icu_ucal_open(_Tz_name.get(), -1, nullptr, UCalendarType::UCAL_DEFAULT, &_UErr), &__icu_ucal_close}; + if (U_FAILURE(_UErr)) { + _Err = __std_tzdb_error::_Icu_error; + } + + return _Cal; } template - _NODISCARD _Ty* _Report_error(_STD unique_ptr<_Ty, _Dx>& _Info, __std_tzdb_error _Err) { + _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) { + _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(); } @@ -367,6 +463,86 @@ void __stdcall __std_tzdb_delete_current_zone(__std_tzdb_current_zone_info* cons } } +_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 + _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}; + if (_Info == nullptr) { + return nullptr; + } + + if (_Acquire_icu_functions() < _Icu_api_level::_Has_icu_addresses) { + return _Report_error(_Info, __std_tzdb_error::_Win_error); + } + + 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); + } + + 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); + } + + 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) { + return _Propagate_error(_Info); + } + + return _Info.release(); +} + +void __stdcall __std_tzdb_delete_sys_info(__std_tzdb_sys_info* const _Info) noexcept { + if (_Info) { + delete[] _Info->_Abbrev; + _Info->_Abbrev = nullptr; + } +} + __std_tzdb_registry_leap_info* __stdcall __std_tzdb_get_reg_leap_seconds( const size_t prev_reg_ls_size, size_t* const current_reg_ls_size) noexcept { // On exit--- 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 04ced25c095..4509b9fc904 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 @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include "timezone_data.h" #include #include #include @@ -10,6 +11,17 @@ using namespace std; using namespace std::chrono; +// NOTE: +// These test suites will assume all data from the IANA database is correct +// and will not test historical changes in transitions. Instead the focus +// will be on using a select sample of transitions in both a positive and +// negative UTC offset zone (different corner cases). + +bool operator==(const sys_info& _Left, const sys_info& _Right) { + return _Left.begin == _Right.begin && _Left.end == _Right.end && _Left.offset == _Right.offset + && _Left.save == _Right.save && _Left.abbrev == _Right.abbrev; +} + void test_time_zone_and_link(const tzdb& tzdb, string_view tz_name, string_view tz_link_name) { const auto orginal_tz = tzdb.locate_zone(tz_name); assert(orginal_tz != nullptr); @@ -80,6 +92,20 @@ void timezone_names_test() { assert(link3 <=> link1 == strong_ordering::greater); #endif // __cpp_lib_concepts + try { + // ensure locate_zone returns time_zone with given name + assert(all_of(tzdb.zones.begin(), tzdb.zones.end(), + [&](const auto& zone) { return tzdb.locate_zone(zone.name())->name() == zone.name(); })); + // ensure locate_zone returns target of time_zone + assert(all_of(tzdb.links.begin(), tzdb.links.end(), + [&](const auto& link) { return tzdb.locate_zone(link.name())->name() == link.target(); })); + // ensure locate_zone does NOT return time_zone that is also a time_zone_link + assert(all_of(tzdb.links.begin(), tzdb.links.end(), + [&](const auto& link) { return tzdb.locate_zone(link.name())->name() != link.name(); })); + } catch (runtime_error) { + assert(false); + } + // FIXME: add a link to an issue. These may change overtime and might have to be removed from tests. // these are some example in which the ICU.dll and IANA database diverge in what they consider a zone or a link @@ -91,9 +117,268 @@ void timezone_names_test() { assert(_Locate_zone_impl(tzdb.zones, "America/Nuuk") == nullptr); // does not exist in ICU (very rare) } +void validate_timezone_transitions(const time_zone* tz, const Transition& data) { + auto info = tz->get_info(data.begin()); + assert(info.begin == data.begin()); + assert(info.end == data.end()); + assert(info.offset == data.offset()); + assert(info.save == data.save()); + assert(info.abbrev == data.abbrev()); +} + +void timezone_sys_info_test() { + const auto& tzdb = get_tzdb(); + + auto sydney_tz = tzdb.locate_zone(Sydney::Tz_name); + assert(sydney_tz != nullptr); + validate_timezone_transitions(sydney_tz, Sydney::Day_1); + validate_timezone_transitions(sydney_tz, Sydney::Std_1); + validate_timezone_transitions(sydney_tz, Sydney::Day_2); + + auto la_tz = tzdb.locate_zone(LA::Tz_name); + assert(la_tz != nullptr); + validate_timezone_transitions(la_tz, LA::Day_1); + validate_timezone_transitions(la_tz, LA::Std_1); + validate_timezone_transitions(la_tz, LA::Day_2); + + auto begin = sydney_tz->get_info(Sydney::Std_1.begin()); + auto middle = sydney_tz->get_info(Sydney::Std_1.begin() + days{1}); + auto end = sydney_tz->get_info(Sydney::Std_1.end()); + assert(begin == middle); + assert(begin != end); + + auto min_info = sydney_tz->get_info(time_zone::_Min_seconds); + auto max_info = sydney_tz->get_info(time_zone::_Max_seconds - seconds{1}); + assert(min_info.begin == time_zone::_Min_seconds); + assert(min_info.end != time_zone::_Max_seconds); + assert(max_info.begin != time_zone::_Min_seconds); + assert(max_info.end == time_zone::_Max_seconds); + + auto utc_zone = tzdb.locate_zone("Etc/UTC"); + assert(utc_zone != nullptr); + + auto min_utc = utc_zone->get_info(time_zone::_Min_seconds); + auto max_utc = utc_zone->get_info(time_zone::_Max_seconds - seconds{1}); + // Only a single transition in UTC + assert(min_utc == max_utc); + assert(min_utc.begin < max_utc.end); + assert(min_utc.begin == time_zone::_Min_seconds); + assert(min_utc.end == time_zone::_Max_seconds); + + // Test abbrevations other than standard/daylight savings such as war time. + // These senarios are not handled correctly by icu.dll + auto war_time = la_tz->get_info(sys_days{year{1942} / April / day{1}}); + assert(war_time.abbrev == "PDT"); // IANA datbase == "PWT" +} + +void timezone_to_local_test() { + const auto& tzdb = get_tzdb(); + { + using namespace Sydney; + auto tz = tzdb.locate_zone(Tz_name); + assert(tz != nullptr); + + const auto& to_standard = Day_to_Std.first.end(); + assert(tz->to_local(to_standard) == Day_to_Std.second.local_begin()); + assert(tz->to_local(to_standard + minutes{30}) == Day_to_Std.second.local_begin() + minutes{30}); + assert(tz->to_local(to_standard - minutes{30}) == Day_to_Std.first.local_end() - minutes{30}); + + const auto& to_daylight = Std_to_Day.first.end(); + assert(tz->to_local(to_daylight) == Std_to_Day.second.local_begin()); + assert(tz->to_local(to_daylight + minutes{30}) == Std_to_Day.second.local_begin() + minutes{30}); + assert(tz->to_local(to_daylight - minutes{30}) == Std_to_Day.first.local_end() - minutes{30}); + } + { + using namespace LA; + auto tz = tzdb.locate_zone(Tz_name); + assert(tz != nullptr); + + const auto& to_standard = Day_to_Std.first.end(); + assert(tz->to_local(to_standard) == Day_to_Std.second.local_begin()); + assert(tz->to_local(to_standard + minutes{30}) == Day_to_Std.second.local_begin() + minutes{30}); + assert(tz->to_local(to_standard - minutes{30}) == Day_to_Std.first.local_end() - minutes{30}); + + const auto& to_daylight = Std_to_Day.first.end(); + assert(tz->to_local(to_daylight) == Std_to_Day.second.local_begin()); + assert(tz->to_local(to_daylight + minutes{30}) == Std_to_Day.second.local_begin() + minutes{30}); + assert(tz->to_local(to_daylight - minutes{30}) == Std_to_Day.first.local_end() - minutes{30}); + } +} + +void assert_local(const time_zone* tz, local_seconds time, int result, const sys_info& first, const sys_info& second) { + const auto info = tz->get_info(time); + assert(info.result == result); + assert(info.first == first); + assert(info.second == second); + + // time_zone::to_sys depends heavily on local_info so just test it here + // to exhaust all corner cases. + sys_seconds sys_earliest{time.time_since_epoch() - info.first.offset}; + sys_seconds sys_latest{time.time_since_epoch() - info.second.offset}; + try { + assert(tz->to_sys(time) == sys_earliest); + assert(result == local_info::unique); + } catch (nonexistent_local_time ex) { + assert(result == local_info::nonexistent); + } catch (ambiguous_local_time ex) { + assert(result == local_info::ambiguous); + } + + if (result == local_info::unique) { + assert(tz->to_sys(time, choose::earliest) == sys_earliest); + assert(tz->to_sys(time, choose::latest) == sys_earliest); + } else if (result == local_info::nonexistent) { + assert(tz->to_sys(time, choose::earliest) == info.first.end); + assert(tz->to_sys(time, choose::latest) == info.first.end); + } else if (result == local_info::ambiguous) { + assert(tz->to_sys(time, choose::earliest) == sys_earliest); + assert(tz->to_sys(time, choose::latest) == sys_latest); + } +} + +void validate_get_local_info(const time_zone* tz, const std::pair& transition, int result) { + sys_info empty{}; + sys_info first = tz->get_info(transition.first.begin()); + sys_info second = tz->get_info(transition.second.begin()); + + // Get the local time for the beginning of the ambiguous/nonexistent section + const auto danger_begin = get_danger_begin(transition.first, transition.second); + const auto danger_end = get_danger_end(transition.first, transition.second); + (void) result; + assert_local(tz, danger_begin - days{2}, local_info::unique, first, empty); // two days before + assert_local(tz, danger_begin - hours{1}, local_info::unique, first, empty); // one hour before + assert_local(tz, danger_begin, result, first, second); // danger begin + assert_local(tz, danger_begin + minutes{30}, result, first, second); // danger middle + assert_local(tz, danger_end, local_info::unique, second, empty); // danger end + assert_local(tz, danger_end + hours{2}, local_info::unique, second, empty); // one hour after + assert_local(tz, danger_end + days{2}, local_info::unique, second, empty); // two days after +} + +void timezone_local_info_test() { + const auto& tzdb = get_tzdb(); + { + // positive offset (UTC+10/+11) can fall in previous transition + using namespace Sydney; + auto tz = tzdb.locate_zone(Tz_name); + assert(tz != nullptr); + validate_get_local_info(tz, Day_to_Std, local_info::ambiguous); + validate_get_local_info(tz, Std_to_Day, local_info::nonexistent); + } + { + // negative offset (UTC-8/-7) can fall in next transition + using namespace LA; + auto tz = tzdb.locate_zone(Tz_name); + assert(tz != nullptr); + validate_get_local_info(tz, Day_to_Std, local_info::ambiguous); + validate_get_local_info(tz, Std_to_Day, local_info::nonexistent); + } +} + +template +void validate_precision(const time_zone* tz, const pair& transition_pair, _Dur precision) { + const auto& first = transition_pair.first; + const auto& second = transition_pair.second; + const auto transition = first.end<_Dur>(); + const auto danger_begin = get_danger_begin<_Dur>(first, second); + const auto danger_end = get_danger_end<_Dur>(first, second); + sys_info first_info = tz->get_info(first.begin<_Dur>()); + sys_info second_info = tz->get_info(second.begin<_Dur>()); + + // test correct transition is picked + assert(tz->get_info(transition) == second_info); + assert(tz->get_info(transition - precision) == first_info); + + // test ambiguous/nonexistent info is handled + assert(tz->get_info(danger_end).result == local_info::unique); // exact end of danger zone + assert(tz->get_info(danger_end - precision).result != local_info::unique); // just inside danger zone + assert(tz->get_info(danger_begin).result != local_info::unique); // exact start of danger zone + assert(tz->get_info(danger_begin - precision).result == local_info::unique); // just before danger zone + + // test precision is not lost when converting to local + assert(tz->to_local(transition) == second.local_begin<_Dur>()); + assert(tz->to_local(transition + precision) == second.local_begin<_Dur>() + precision); + assert(tz->to_local(transition - precision) == first.local_end<_Dur>() - precision); + + // test precision is not lost when converting to local + try { + const sys_time<_Dur> sys_danger_begin = transition - first.save(); + const sys_time<_Dur> sys_danger_end = transition + first.save(); + assert(tz->to_sys(danger_end) == sys_danger_end); + assert(tz->to_sys(danger_end + precision) == sys_danger_end + precision); + assert(tz->to_sys(danger_begin - precision) == sys_danger_begin - precision); + } catch (nonexistent_local_time&) { + assert(false); + } catch (ambiguous_local_time&) { + assert(false); + } + + try { + // test ambiguous/nonexistent info is found + (void) tz->to_sys(danger_end - precision); + assert(false); + } catch (nonexistent_local_time&) { + } catch (ambiguous_local_time&) { + } +} + +void timezone_precision_test() { + const auto& tzdb = get_tzdb(); + using Milli_Dur = duration; + using Micro_Dur = duration; + + { + using namespace Sydney; + auto tz = tzdb.locate_zone(Tz_name); + validate_precision(tz, Std_to_Day, sys_seconds::duration{1}); + validate_precision(tz, Std_to_Day, Milli_Dur{1}); + validate_precision(tz, Std_to_Day, Milli_Dur{0.5}); + validate_precision(tz, Std_to_Day, Milli_Dur{0.05}); + validate_precision(tz, Std_to_Day, Milli_Dur{0.005}); + validate_precision(tz, Std_to_Day, Milli_Dur{0.0005}); + // precision limit... + // validate_precision(tz, Std_to_Day, Milli_Dur{0.00005}); + // validate_precision(tz, Std_to_Day, Micro_Dur{(numeric_limits::min)()}); + + validate_precision(tz, Std_to_Day, Micro_Dur{1}); + validate_precision(tz, Std_to_Day, Micro_Dur{0.5}); + // precision limit... + // validate_precision(tz, Std_to_Day, Micro_Dur{0.05}); + // validate_precision(tz, Std_to_Day, Micro_Dur{(numeric_limits::min)()}); + + validate_precision(tz, Day_to_Std, Milli_Dur{0.0005}); + validate_precision(tz, Day_to_Std, Micro_Dur{0.5}); + } + { + using namespace LA; + auto tz = tzdb.locate_zone(Tz_name); + validate_precision(tz, Std_to_Day, sys_seconds::duration{1}); + validate_precision(tz, Std_to_Day, Milli_Dur{1}); + validate_precision(tz, Std_to_Day, Milli_Dur{0.5}); + validate_precision(tz, Std_to_Day, Milli_Dur{0.05}); + validate_precision(tz, Std_to_Day, Milli_Dur{0.005}); + validate_precision(tz, Std_to_Day, Milli_Dur{0.0005}); + // precision limit... + // validate_precision(tz, Std_to_Day, Milli_Dur{0.00005}); + // validate_precision(tz, Std_to_Day, Micro_Dur{(numeric_limits::min)()}); + + validate_precision(tz, Std_to_Day, Micro_Dur{1}); + validate_precision(tz, Std_to_Day, Micro_Dur{0.5}); + // precision limit... + // validate_precision(tz, Std_to_Day, Micro_Dur{0.05}); + // validate_precision(tz, Std_to_Day, Micro_Dur{(numeric_limits::min)()}); + + validate_precision(tz, Day_to_Std, Milli_Dur{0.0005}); + validate_precision(tz, Day_to_Std, Micro_Dur{0.5}); + } +} + bool test() { try { timezone_names_test(); + timezone_sys_info_test(); + timezone_to_local_test(); + timezone_local_info_test(); + timezone_precision_test(); } catch (exception& ex) { cerr << "Test threw exception: " << ex.what() << "\n"; assert(false); diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/timezone_data.h b/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/timezone_data.h new file mode 100644 index 00000000000..0d0fb5f3be8 --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/timezone_data.h @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +using namespace std; +using namespace std::chrono; + +class Transition { +public: + Transition( + string_view name, seconds offset, minutes save, string_view abbrev, sys_seconds _begin_, sys_seconds _end_) + : _name(name), _offset(offset), _save(save), _abbrev(abbrev), _begin(_begin_), _end(_end_) {} + + constexpr string_view name() const { + return _name; + } + + constexpr seconds offset() const { + return _offset; + } + + constexpr minutes save() const { + return _save; + } + + constexpr string_view abbrev() const { + return _abbrev; + } + + constexpr bool is_daylight() const { + return _save != minutes{0}; + } + + template + constexpr sys_time<_Duration> begin() const { + return sys_time<_Duration>{duration_cast<_Duration>(_begin.time_since_epoch())}; + } + + template + constexpr sys_time<_Duration> end() const { + return sys_time<_Duration>{duration_cast<_Duration>(_end.time_since_epoch())}; + } + + template + constexpr local_time<_Duration> local_begin() const { + return local_time<_Duration>{duration_cast<_Duration>(_begin.time_since_epoch() + _offset)}; + } + + template + constexpr local_time<_Duration> local_end() const { + return local_time<_Duration>{duration_cast<_Duration>(_end.time_since_epoch() + _offset)}; + } + +private: + string _name; + seconds _offset; + minutes _save; + string _abbrev; + sys_seconds _begin; + sys_seconds _end; +}; + +// start of ambiguous/nonexistent zone between transitions +template +constexpr local_time<_Duration> get_danger_begin(const Transition& first, const Transition& second) { + assert(first.end<_Duration>() == second.begin<_Duration>()); + return first.local_end<_Duration>() - first.save(); +} + +// end of ambiguous/nonexistent zone between transitions +template +constexpr local_time<_Duration> get_danger_end(const Transition& first, const Transition& second) { + assert(first.end<_Duration>() == second.begin<_Duration>()); + return second.local_begin<_Duration>() + first.save(); +} + +// Sydney +// Standard time (AEST : UTC+10) -1 @ 3am +// Daylight time (AEDT : UTC+11) +1 @ 2am +namespace Sydney { + static constexpr std::string_view Tz_name{"Australia/Sydney"sv}; + static constexpr std::string_view Standard_abbrev{"GMT+10"sv}; // IANA database == "AEST" + static constexpr std::string_view Daylight_abbrev{"GMT+11"sv}; // IANA database == "AEDT" + static constexpr seconds Standard_offset{hours{10}}; + static constexpr seconds Daylight_offset{hours{11}}; + static constexpr auto Daylight_begin_2019 = + sys_seconds{sys_days{year{2019} / October / day{6}}} + hours{2} - Standard_offset; + static constexpr auto Standard_begin_2020 = + sys_seconds{sys_days{year{2020} / April / day{5}}} + hours{3} - Daylight_offset; + static constexpr auto Daylight_begin_2020 = + sys_seconds{sys_days{year{2020} / October / day{4}}} + hours{2} - Standard_offset; + static constexpr auto Standard_begin_2021 = + sys_seconds{sys_days{year{2021} / April / day{4}}} + hours{3} - Daylight_offset; + + Transition Day_1{Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2019, Standard_begin_2020}; + Transition Std_1{Tz_name, Standard_offset, hours{0}, Standard_abbrev, Standard_begin_2020, Daylight_begin_2020}; + Transition Day_2{Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2020, Standard_begin_2021}; + + std::pair Day_to_Std{Day_1, Std_1}; + std::pair Std_to_Day{Std_1, Day_2}; + +} // namespace Sydney + +// Los Angeles +// Standard time (PST : UTC-8) +1 @ 2am +// Daylight time (PDT : UTC-7) -1 @ 2am +namespace LA { + static constexpr std::string_view Tz_name{"America/Los_Angeles"sv}; + static constexpr std::string_view Standard_abbrev{"PST"sv}; + static constexpr std::string_view Daylight_abbrev{"PDT"sv}; + static constexpr seconds Standard_offset{hours{-8}}; + static constexpr seconds Daylight_offset{hours{-7}}; + static constexpr auto Daylight_begin_2020 = + sys_seconds{sys_days{year{2020} / March / day{8}}} + hours{2} - Standard_offset; + static constexpr auto Standard_begin_2020 = + sys_seconds{sys_days{year{2020} / November / day{1}}} + hours{2} - Daylight_offset; + static constexpr auto Daylight_begin_2021 = + sys_seconds{sys_days{year{2021} / March / day{14}}} + hours{2} - Standard_offset; + static constexpr auto Standard_begin_2021 = + sys_seconds{sys_days{year{2021} / November / day{7}}} + hours{2} - Daylight_offset; + + Transition Day_1{Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2020, Standard_begin_2020}; + Transition Std_1{Tz_name, Standard_offset, hours{0}, Standard_abbrev, Standard_begin_2020, Daylight_begin_2021}; + Transition Day_2{Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2021, Standard_begin_2021}; + + std::pair Day_to_Std{Day_1, Std_1}; + std::pair Std_to_Day{Std_1, Day_2}; + +} // namespace LA From f8b8b2e4cb21f82a68d836514f9799d34c96aacc Mon Sep 17 00:00:00 2001 From: Daniel Winsor Date: Mon, 15 Mar 2021 11:10:04 -0700 Subject: [PATCH 2/3] Addressed feedback --- stl/inc/chrono | 72 ++--- stl/inc/xtzdb.h | 2 +- stl/src/tzdb.cpp | 37 ++- .../test.cpp | 273 +++++++++--------- .../timezone_data.h | 114 ++++---- 5 files changed, 255 insertions(+), 243 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 4538f51a27d..453085aa6a4 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2240,16 +2240,16 @@ namespace chrono { // CLASS nonexistent_local_time class nonexistent_local_time : public runtime_error { public: - template - explicit nonexistent_local_time(const local_time&, const local_info&) + template + nonexistent_local_time(const local_time<_Duration>&, const local_info&) : runtime_error("TRANSITION: work in progress") {} }; // CLASS ambiguous_local_time class ambiguous_local_time : public runtime_error { public: - template - explicit ambiguous_local_time(const local_time&, const local_info&) + template + ambiguous_local_time(const local_time<_Duration>&, const local_info&) : runtime_error("TRANSITION: work in progress") {} }; @@ -2280,7 +2280,7 @@ namespace chrono { local_info _Info{}; _Info.first = _Get_info(_Local.time_since_epoch()); - const sys_seconds _Local_sys{duration_cast(_Local.time_since_epoch())}; + const sys_seconds _Local_sys{_CHRONO duration_cast(_Local.time_since_epoch())}; const auto _Curr_sys = _Local_sys - _Info.first.offset; if (_Info.first.begin != _Min_seconds && _Curr_sys < _Info.first.begin + days{1}) { // get previous transition information @@ -2291,7 +2291,7 @@ namespace chrono { if (_Curr_sys >= _Transition) { if (_Prev_sys < _Transition) { _Info.result = local_info::ambiguous; - std::swap(_Info.first, _Info.second); + _STD swap(_Info.first, _Info.second); } else { _Info.result = local_info::unique; _Info.second = {}; @@ -2299,10 +2299,10 @@ namespace chrono { } else { if (_Prev_sys >= _Transition) { _Info.result = local_info::nonexistent; - std::swap(_Info.first, _Info.second); + _STD swap(_Info.first, _Info.second); } else { _Info.result = local_info::unique; - _Info.first = std::move(_Info.second); + _Info.first = _STD move(_Info.second); _Info.second = {}; } } @@ -2324,7 +2324,7 @@ namespace chrono { _Info.result = local_info::nonexistent; } else { _Info.result = local_info::unique; - _Info.first = std::move(_Info.second); + _Info.first = _STD move(_Info.second); _Info.second = {}; } } @@ -2339,11 +2339,11 @@ namespace chrono { template _NODISCARD sys_time> to_sys(const local_time<_Duration>& _Local) const { - auto _Info = get_info(_Local); + const auto _Info = get_info(_Local); if (_Info.result == local_info::nonexistent) { - throw nonexistent_local_time(_Local, _Info); + _THROW(nonexistent_local_time(_Local, _Info)); } else if (_Info.result == local_info::ambiguous) { - throw ambiguous_local_time(_Local, _Info); + _THROW(ambiguous_local_time(_Local, _Info)); } return sys_time>{_Local.time_since_epoch()} - _Info.first.offset; @@ -2351,21 +2351,21 @@ namespace chrono { template _NODISCARD sys_time> to_sys( - const local_time<_Duration>& _Local, choose _Choose) const { - auto _Info = get_info(_Local); - auto _Result = sys_time<_Duration>{_Local.time_since_epoch()}; + const local_time<_Duration>& _Local, const choose _Choose) const { + const auto _Info = get_info(_Local); if (_Info.result == local_info::nonexistent) { return _Info.first.end; } - auto _Offset = (_Info.result == local_info::unique || _Choose == choose::earliest) ? _Info.first.offset - : _Info.second.offset; + const auto _Offset = (_Info.result == local_info::unique || _Choose == choose::earliest) + ? _Info.first.offset + : _Info.second.offset; return sys_time>{_Local.time_since_epoch()} - _Offset; } template _NODISCARD local_time> to_local(const sys_time<_Duration>& _Sys) const { - auto _Info = get_info(_Sys); + const auto _Info = get_info(_Sys); return local_time>{(_Sys + _Info.offset).time_since_epoch()}; } @@ -2376,9 +2376,9 @@ namespace chrono { private: template _NODISCARD sys_info _Get_info(const _Duration& _Dur) const { - using _Internal_duration = duration<__std_tzdb_epoch_milli, std::milli>; - auto _Internal_dur = duration_cast<_Internal_duration>(_Dur); - std::unique_ptr<__std_tzdb_sys_info, decltype(&__std_tzdb_delete_sys_info)> _Info( + 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); if (_Info == nullptr) { @@ -2389,19 +2389,23 @@ namespace chrono { _Xruntime_error("Internal error loading IANA database information"); } - static constexpr auto _Min_internal = - duration_cast<_Internal_duration>(_Min_seconds.time_since_epoch()).count(); - static constexpr auto _Max_internal = - duration_cast<_Internal_duration>(_Max_seconds.time_since_epoch()).count(); - return {.begin = _Info->_Begin <= _Min_internal - ? _Min_seconds - : sys_seconds{duration_cast(_Internal_duration{_Info->_Begin})}, - .end = _Info->_End >= _Max_internal - ? _Max_seconds - : sys_seconds{duration_cast(_Internal_duration{_Info->_End})}, - .offset = duration_cast(_Internal_duration{_Info->_Offset}), - .save = duration_cast(_Internal_duration{_Info->_Save}), - .abbrev = std::string{_Info->_Abbrev}}; + constexpr auto _Min_internal = + _CHRONO duration_cast<_Internal_duration>(_Min_seconds.time_since_epoch()).count(); + constexpr auto _Max_internal = + _CHRONO duration_cast<_Internal_duration>(_Max_seconds.time_since_epoch()).count(); + const auto _Begin = + _Info->_Begin <= _Min_internal + ? _Min_seconds + : sys_seconds{_CHRONO duration_cast(_Internal_duration{_Info->_Begin})}; + const auto _End = + _Info->_End >= _Max_internal + ? _Max_seconds + : sys_seconds{_CHRONO duration_cast(_Internal_duration{_Info->_End})}; + return {.begin = _Begin, + .end = _End, + .offset = _CHRONO duration_cast(_Internal_duration{_Info->_Offset}), + .save = _CHRONO duration_cast(_Internal_duration{_Info->_Save}), + .abbrev = _Info->_Abbrev}; } string _Name; diff --git a/stl/inc/xtzdb.h b/stl/inc/xtzdb.h index 59308ffedc4..a0f1ffb4d8e 100644 --- a/stl/inc/xtzdb.h +++ b/stl/inc/xtzdb.h @@ -20,7 +20,7 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new -typedef double __std_tzdb_epoch_milli; +using __std_tzdb_epoch_milli = double; struct __std_tzdb_registry_leap_info { uint16_t _Year; diff --git a/stl/src/tzdb.cpp b/stl/src/tzdb.cpp index 93410ee7552..80a39caaf1d 100644 --- a/stl/src/tzdb.cpp +++ b/stl/src/tzdb.cpp @@ -107,7 +107,7 @@ namespace { return _Level; } - _NODISCARD void __icu_ucal_close(UCalendar* cal) noexcept { + void __icu_ucal_close(UCalendar* cal) noexcept { const auto _Fun = _Icu_functions._Pfn_ucal_close.load(_STD memory_order_relaxed); _Fun(cal); } @@ -162,12 +162,12 @@ namespace { return _Fun(zoneType, region, rawOffset, ec); } - _NODISCARD void __icu_ucal_setMillis(UCalendar* cal, UDate dateTime, UErrorCode* status) noexcept { + 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); } - _NODISCARD void __icu_uenum_close(UEnumeration* en) noexcept { + void __icu_uenum_close(UEnumeration* en) noexcept { const auto _Fun = _Icu_functions._Pfn_uenum_close.load(_STD memory_order_relaxed); _Fun(en); } @@ -223,11 +223,11 @@ namespace { return nullptr; } - _Data[_Count._Len] = u'\0'; - const auto _Ouput_as_wchar = reinterpret_cast(_Data.get()); + _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, _Ouput_as_wchar, _Count._Len); + __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; @@ -236,9 +236,8 @@ namespace { return _Data; } - using _Icu_get_string_fn = std::function; - - _NODISCARD _STD unique_ptr _Get_icu_string_impl(const _Icu_get_string_fn& _Icu_fn, + 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) { @@ -267,37 +266,36 @@ namespace { return _Str_buf; } - _NODISCARD _STD unique_ptr _Get_canonical_id( + _NODISCARD _STD unique_ptr _Get_canonical_id( const char16_t* _Id, const int32_t _Len, int32_t& _Result_len, __std_tzdb_error& _Err) noexcept { - _Icu_get_string_fn _Icu_fn = [_Id, _Len](UChar* _Result, int32_t _Result_capacity, UErrorCode* _UErr) { + 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( + _NODISCARD _STD unique_ptr _Get_default_timezone( int32_t& _Result_len, __std_tzdb_error& _Err) noexcept { - _Icu_get_string_fn _Icu_fn = [](UChar* _Result, int32_t _Result_capacity, UErrorCode* _UErr) { + 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( + _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; - _Icu_get_string_fn _Icu_fn = [_Cal, _Display_type]( - UChar* _Result, int32_t _Result_capacity, UErrorCode* _UErr) { + 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); } - _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, _Tz_len, _Err); + const auto _Tz_name = _Allocate_narrow_to_wide(_Tz, static_cast(_Tz_len), _Err); if (_Tz_name == nullptr) { return {nullptr, &__icu_ucal_close}; } @@ -323,7 +321,6 @@ namespace { // 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 @@ -466,7 +463,7 @@ void __stdcall __std_tzdb_delete_current_zone(__std_tzdb_current_zone_info* cons _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 == 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{ 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 4509b9fc904..1dbbd880781 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 @@ -6,7 +6,10 @@ #include #include #include +#include +#include #include +#include using namespace std; using namespace std::chrono; @@ -17,57 +20,57 @@ using namespace std::chrono; // will be on using a select sample of transitions in both a positive and // negative UTC offset zone (different corner cases). -bool operator==(const sys_info& _Left, const sys_info& _Right) { - return _Left.begin == _Right.begin && _Left.end == _Right.end && _Left.offset == _Right.offset - && _Left.save == _Right.save && _Left.abbrev == _Right.abbrev; +constexpr bool sys_equal(const sys_info& left, const sys_info& right) { + return left.begin == right.begin && left.end == right.end && left.offset == right.offset && left.save == right.save + && left.abbrev == right.abbrev; } -void test_time_zone_and_link(const tzdb& tzdb, string_view tz_name, string_view tz_link_name) { - const auto orginal_tz = tzdb.locate_zone(tz_name); +void test_time_zone_and_link(const tzdb& my_tzdb, string_view tz_name, string_view tz_link_name) { + const auto orginal_tz = my_tzdb.locate_zone(tz_name); assert(orginal_tz != nullptr); assert(orginal_tz->name() == tz_name); - const auto linked_tz = tzdb.locate_zone(tz_link_name); + const auto linked_tz = my_tzdb.locate_zone(tz_link_name); assert(linked_tz != nullptr); assert(linked_tz->name() == tz_name); assert(orginal_tz == linked_tz); - const auto tz_link = _Locate_zone_impl(tzdb.links, tz_link_name); + const auto tz_link = _Locate_zone_impl(my_tzdb.links, tz_link_name); assert(tz_link != nullptr); assert(tz_link->name() == tz_link_name); assert(tz_link->target() == tz_name); - assert(tzdb.locate_zone(tz_link->target()) == orginal_tz); + assert(my_tzdb.locate_zone(tz_link->target()) == orginal_tz); - assert(_Locate_zone_impl(tzdb.zones, tz_name) != nullptr); - assert(_Locate_zone_impl(tzdb.zones, tz_link_name) == nullptr); - assert(_Locate_zone_impl(tzdb.links, tz_name) == nullptr); + assert(_Locate_zone_impl(my_tzdb.zones, tz_name) != nullptr); + assert(_Locate_zone_impl(my_tzdb.zones, tz_link_name) == nullptr); + assert(_Locate_zone_impl(my_tzdb.links, tz_name) == nullptr); } -void try_locate_invalid_zone(const tzdb& tzdb, string_view name) { +void try_locate_invalid_zone(const tzdb& my_tzdb, string_view name) { try { - (void) tzdb.locate_zone(name); + (void) my_tzdb.locate_zone(name); assert(false); - } catch (runtime_error) { + } catch (const runtime_error&) { } } void timezone_names_test() { - const auto& tzdb = get_tzdb(); + const auto& my_tzdb = get_tzdb(); - assert(tzdb.version.empty() == false); + assert(my_tzdb.version.empty() == false); - test_time_zone_and_link(tzdb, "Asia/Thimphu", "Asia/Thimbu"); - test_time_zone_and_link(tzdb, "America/Tijuana", "America/Ensenada"); + 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 = tzdb.current_zone(); + const auto current_zone = my_tzdb.current_zone(); assert(current_zone != nullptr); assert(current_zone->name().empty() == false); - try_locate_invalid_zone(tzdb, "Non/Existent"); + try_locate_invalid_zone(my_tzdb, "Non/Existent"); // Abbreviations should not be time_zones or time_zone_links - try_locate_invalid_zone(tzdb, "PDT"); - try_locate_invalid_zone(tzdb, "AEST"); + try_locate_invalid_zone(my_tzdb, "PDT"); + try_locate_invalid_zone(my_tzdb, "AEST"); // Comparison operators const time_zone tz1{"Earlier"}; @@ -94,58 +97,58 @@ void timezone_names_test() { try { // ensure locate_zone returns time_zone with given name - assert(all_of(tzdb.zones.begin(), tzdb.zones.end(), - [&](const auto& zone) { return tzdb.locate_zone(zone.name())->name() == zone.name(); })); - // ensure locate_zone returns target of time_zone - assert(all_of(tzdb.links.begin(), tzdb.links.end(), - [&](const auto& link) { return tzdb.locate_zone(link.name())->name() == link.target(); })); + assert(all_of(my_tzdb.zones.begin(), my_tzdb.zones.end(), + [&](const auto& zone) { return my_tzdb.locate_zone(zone.name())->name() == zone.name(); })); + // ensure locate_zone returns correct target of time_zone_link + assert(all_of(my_tzdb.links.begin(), my_tzdb.links.end(), + [&](const auto& link) { return my_tzdb.locate_zone(link.name())->name() == link.target(); })); // ensure locate_zone does NOT return time_zone that is also a time_zone_link - assert(all_of(tzdb.links.begin(), tzdb.links.end(), - [&](const auto& link) { return tzdb.locate_zone(link.name())->name() != link.name(); })); - } catch (runtime_error) { + assert(all_of(my_tzdb.links.begin(), my_tzdb.links.end(), + [&](const auto& link) { return my_tzdb.locate_zone(link.name())->name() != link.name(); })); + } catch (const runtime_error&) { assert(false); } // FIXME: add a link to an issue. These may change overtime and might have to be removed from tests. // these are some example in which the ICU.dll and IANA database diverge in what they consider a zone or a link - assert(_Locate_zone_impl(tzdb.links, "Atlantic/Faroe") != nullptr); // is a time_zone in IANA - assert(_Locate_zone_impl(tzdb.zones, "Africa/Addis_Ababa") != nullptr); // is a time_zone_link in IANA - assert(_Locate_zone_impl(tzdb.links, "PST") != nullptr); // time_zone_link does not exist in IANA - assert(_Locate_zone_impl(tzdb.links, "Africa/Asmara") != nullptr); // matches IANA but target is wrong - assert(_Locate_zone_impl(tzdb.links, "Africa/Asmara")->target() == "Africa/Asmera"); // target == Africa/Nairobi - assert(_Locate_zone_impl(tzdb.zones, "America/Nuuk") == nullptr); // does not exist in ICU (very rare) + assert(_Locate_zone_impl(my_tzdb.links, "Atlantic/Faroe") != nullptr); // is a time_zone in IANA + assert(_Locate_zone_impl(my_tzdb.zones, "Africa/Addis_Ababa") != nullptr); // is a time_zone_link in IANA + assert(_Locate_zone_impl(my_tzdb.links, "PST") != nullptr); // time_zone_link does not exist in IANA + assert(_Locate_zone_impl(my_tzdb.links, "Africa/Asmara") != nullptr); // matches IANA but target is wrong + assert(_Locate_zone_impl(my_tzdb.links, "Africa/Asmara")->target() == "Africa/Asmera"); // target == Africa/Nairobi + assert(_Locate_zone_impl(my_tzdb.zones, "America/Nuuk") == nullptr); // does not exist in ICU (very rare) } -void validate_timezone_transitions(const time_zone* tz, const Transition& data) { - auto info = tz->get_info(data.begin()); - assert(info.begin == data.begin()); - assert(info.end == data.end()); - assert(info.offset == data.offset()); - assert(info.save == data.save()); - assert(info.abbrev == data.abbrev()); +void validate_timezone_transitions(const time_zone* tz, const Transition& transition) { + auto info = tz->get_info(transition.begin()); + assert(info.begin == transition.begin()); + assert(info.end == transition.end()); + assert(info.offset == transition.offset()); + assert(info.save == transition.save()); + assert(info.abbrev == transition.abbrev()); } void timezone_sys_info_test() { - const auto& tzdb = get_tzdb(); + const auto& my_tzdb = get_tzdb(); - auto sydney_tz = tzdb.locate_zone(Sydney::Tz_name); + auto sydney_tz = my_tzdb.locate_zone(Sydney::Tz_name); assert(sydney_tz != nullptr); validate_timezone_transitions(sydney_tz, Sydney::Day_1); validate_timezone_transitions(sydney_tz, Sydney::Std_1); validate_timezone_transitions(sydney_tz, Sydney::Day_2); - auto la_tz = tzdb.locate_zone(LA::Tz_name); + auto la_tz = my_tzdb.locate_zone(LA::Tz_name); assert(la_tz != nullptr); validate_timezone_transitions(la_tz, LA::Day_1); validate_timezone_transitions(la_tz, LA::Std_1); validate_timezone_transitions(la_tz, LA::Day_2); - auto begin = sydney_tz->get_info(Sydney::Std_1.begin()); - auto middle = sydney_tz->get_info(Sydney::Std_1.begin() + days{1}); - auto end = sydney_tz->get_info(Sydney::Std_1.end()); - assert(begin == middle); - assert(begin != end); + auto begin_info = sydney_tz->get_info(Sydney::Std_1.begin()); + auto middle_info = sydney_tz->get_info(Sydney::Std_1.begin() + days{1}); + auto end_info = sydney_tz->get_info(Sydney::Std_1.end()); + assert(sys_equal(begin_info, middle_info)); + assert(!sys_equal(begin_info, end_info)); auto min_info = sydney_tz->get_info(time_zone::_Min_seconds); auto max_info = sydney_tz->get_info(time_zone::_Max_seconds - seconds{1}); @@ -154,28 +157,28 @@ void timezone_sys_info_test() { assert(max_info.begin != time_zone::_Min_seconds); assert(max_info.end == time_zone::_Max_seconds); - auto utc_zone = tzdb.locate_zone("Etc/UTC"); + auto utc_zone = my_tzdb.locate_zone("Etc/UTC"); assert(utc_zone != nullptr); auto min_utc = utc_zone->get_info(time_zone::_Min_seconds); auto max_utc = utc_zone->get_info(time_zone::_Max_seconds - seconds{1}); // Only a single transition in UTC - assert(min_utc == max_utc); + assert(sys_equal(min_utc, max_utc)); assert(min_utc.begin < max_utc.end); assert(min_utc.begin == time_zone::_Min_seconds); assert(min_utc.end == time_zone::_Max_seconds); - // Test abbrevations other than standard/daylight savings such as war time. - // These senarios are not handled correctly by icu.dll + // Test abbreviations other than standard/daylight savings such as war time. + // These scenarios are not handled correctly by icu.dll auto war_time = la_tz->get_info(sys_days{year{1942} / April / day{1}}); - assert(war_time.abbrev == "PDT"); // IANA datbase == "PWT" + assert(war_time.abbrev == "PDT"); // IANA database == "PWT" } void timezone_to_local_test() { - const auto& tzdb = get_tzdb(); + const auto& my_tzdb = get_tzdb(); { using namespace Sydney; - auto tz = tzdb.locate_zone(Tz_name); + auto tz = my_tzdb.locate_zone(Tz_name); assert(tz != nullptr); const auto& to_standard = Day_to_Std.first.end(); @@ -190,7 +193,7 @@ void timezone_to_local_test() { } { using namespace LA; - auto tz = tzdb.locate_zone(Tz_name); + auto tz = my_tzdb.locate_zone(Tz_name); assert(tz != nullptr); const auto& to_standard = Day_to_Std.first.end(); @@ -205,39 +208,39 @@ void timezone_to_local_test() { } } -void assert_local(const time_zone* tz, local_seconds time, int result, const sys_info& first, const sys_info& second) { - const auto info = tz->get_info(time); +void assert_local(const time_zone* tz, local_seconds local, int result, const sys_info& first, const sys_info& second) { + const auto info = tz->get_info(local); assert(info.result == result); - assert(info.first == first); - assert(info.second == second); + assert(sys_equal(info.first, first)); + assert(sys_equal(info.second, second)); // time_zone::to_sys depends heavily on local_info so just test it here // to exhaust all corner cases. - sys_seconds sys_earliest{time.time_since_epoch() - info.first.offset}; - sys_seconds sys_latest{time.time_since_epoch() - info.second.offset}; + sys_seconds sys_earliest{local.time_since_epoch() - info.first.offset}; + sys_seconds sys_latest{local.time_since_epoch() - info.second.offset}; try { - assert(tz->to_sys(time) == sys_earliest); + assert(tz->to_sys(local) == sys_earliest); assert(result == local_info::unique); - } catch (nonexistent_local_time ex) { + } catch (const nonexistent_local_time&) { assert(result == local_info::nonexistent); - } catch (ambiguous_local_time ex) { + } catch (const ambiguous_local_time&) { assert(result == local_info::ambiguous); } if (result == local_info::unique) { - assert(tz->to_sys(time, choose::earliest) == sys_earliest); - assert(tz->to_sys(time, choose::latest) == sys_earliest); + assert(tz->to_sys(local, choose::earliest) == sys_earliest); + assert(tz->to_sys(local, choose::latest) == sys_earliest); } else if (result == local_info::nonexistent) { - assert(tz->to_sys(time, choose::earliest) == info.first.end); - assert(tz->to_sys(time, choose::latest) == info.first.end); + assert(tz->to_sys(local, choose::earliest) == info.first.end); + assert(tz->to_sys(local, choose::latest) == info.first.end); } else if (result == local_info::ambiguous) { - assert(tz->to_sys(time, choose::earliest) == sys_earliest); - assert(tz->to_sys(time, choose::latest) == sys_latest); + assert(tz->to_sys(local, choose::earliest) == sys_earliest); + assert(tz->to_sys(local, choose::latest) == sys_latest); } } -void validate_get_local_info(const time_zone* tz, const std::pair& transition, int result) { - sys_info empty{}; +void validate_get_local_info(const time_zone* tz, const pair& transition, int result) { + sys_info default_info{}; sys_info first = tz->get_info(transition.first.begin()); sys_info second = tz->get_info(transition.second.begin()); @@ -245,21 +248,21 @@ void validate_get_local_info(const time_zone* tz, const std::pair -void validate_precision(const time_zone* tz, const pair& transition_pair, _Dur precision) { +template +void validate_precision(const time_zone* tz, const pair& transition_pair, Dur precision) { const auto& first = transition_pair.first; const auto& second = transition_pair.second; - const auto transition = first.end<_Dur>(); - const auto danger_begin = get_danger_begin<_Dur>(first, second); - const auto danger_end = get_danger_end<_Dur>(first, second); - sys_info first_info = tz->get_info(first.begin<_Dur>()); - sys_info second_info = tz->get_info(second.begin<_Dur>()); + const auto transition = first.end(); + const auto danger_begin = get_danger_begin(first, second); + const auto danger_end = get_danger_end(first, second); + sys_info first_info = tz->get_info(first.begin()); + sys_info second_info = tz->get_info(second.begin()); // test correct transition is picked - assert(tz->get_info(transition) == second_info); - assert(tz->get_info(transition - precision) == first_info); + assert(sys_equal(tz->get_info(transition), second_info)); + assert(sys_equal(tz->get_info(transition - precision), first_info)); // test ambiguous/nonexistent info is handled assert(tz->get_info(danger_end).result == local_info::unique); // exact end of danger zone @@ -295,20 +298,20 @@ void validate_precision(const time_zone* tz, const pair& assert(tz->get_info(danger_begin - precision).result == local_info::unique); // just before danger zone // test precision is not lost when converting to local - assert(tz->to_local(transition) == second.local_begin<_Dur>()); - assert(tz->to_local(transition + precision) == second.local_begin<_Dur>() + precision); - assert(tz->to_local(transition - precision) == first.local_end<_Dur>() - precision); + assert(tz->to_local(transition) == second.local_begin()); + assert(tz->to_local(transition + precision) == second.local_begin() + precision); + assert(tz->to_local(transition - precision) == first.local_end() - precision); - // test precision is not lost when converting to local + // test precision is not lost when converting to sys try { - const sys_time<_Dur> sys_danger_begin = transition - first.save(); - const sys_time<_Dur> sys_danger_end = transition + first.save(); + const sys_time sys_danger_begin = transition - first.save(); + const sys_time sys_danger_end = transition + first.save(); assert(tz->to_sys(danger_end) == sys_danger_end); assert(tz->to_sys(danger_end + precision) == sys_danger_end + precision); assert(tz->to_sys(danger_begin - precision) == sys_danger_begin - precision); - } catch (nonexistent_local_time&) { + } catch (const nonexistent_local_time&) { assert(false); - } catch (ambiguous_local_time&) { + } catch (const ambiguous_local_time&) { assert(false); } @@ -316,59 +319,59 @@ void validate_precision(const time_zone* tz, const pair& // test ambiguous/nonexistent info is found (void) tz->to_sys(danger_end - precision); assert(false); - } catch (nonexistent_local_time&) { - } catch (ambiguous_local_time&) { + } catch (const nonexistent_local_time&) { + } catch (const ambiguous_local_time&) { } } void timezone_precision_test() { - const auto& tzdb = get_tzdb(); - using Milli_Dur = duration; - using Micro_Dur = duration; + const auto& my_tzdb = get_tzdb(); + using MilliDur = duration; + using MicroDur = duration; { using namespace Sydney; - auto tz = tzdb.locate_zone(Tz_name); + auto tz = my_tzdb.locate_zone(Tz_name); validate_precision(tz, Std_to_Day, sys_seconds::duration{1}); - validate_precision(tz, Std_to_Day, Milli_Dur{1}); - validate_precision(tz, Std_to_Day, Milli_Dur{0.5}); - validate_precision(tz, Std_to_Day, Milli_Dur{0.05}); - validate_precision(tz, Std_to_Day, Milli_Dur{0.005}); - validate_precision(tz, Std_to_Day, Milli_Dur{0.0005}); + validate_precision(tz, Std_to_Day, MilliDur{1}); + validate_precision(tz, Std_to_Day, MilliDur{0.5}); + validate_precision(tz, Std_to_Day, MilliDur{0.05}); + validate_precision(tz, Std_to_Day, MilliDur{0.005}); + validate_precision(tz, Std_to_Day, MilliDur{0.0005}); // precision limit... - // validate_precision(tz, Std_to_Day, Milli_Dur{0.00005}); - // validate_precision(tz, Std_to_Day, Micro_Dur{(numeric_limits::min)()}); + // validate_precision(tz, Std_to_Day, MilliDur{0.00005}); + // validate_precision(tz, Std_to_Day, MicroDur{(numeric_limits::min)()}); - validate_precision(tz, Std_to_Day, Micro_Dur{1}); - validate_precision(tz, Std_to_Day, Micro_Dur{0.5}); + validate_precision(tz, Std_to_Day, MicroDur{1}); + validate_precision(tz, Std_to_Day, MicroDur{0.5}); // precision limit... - // validate_precision(tz, Std_to_Day, Micro_Dur{0.05}); - // validate_precision(tz, Std_to_Day, Micro_Dur{(numeric_limits::min)()}); + // validate_precision(tz, Std_to_Day, MicroDur{0.05}); + // validate_precision(tz, Std_to_Day, MicroDur{(numeric_limits::min)()}); - validate_precision(tz, Day_to_Std, Milli_Dur{0.0005}); - validate_precision(tz, Day_to_Std, Micro_Dur{0.5}); + validate_precision(tz, Day_to_Std, MilliDur{0.0005}); + validate_precision(tz, Day_to_Std, MicroDur{0.5}); } { using namespace LA; - auto tz = tzdb.locate_zone(Tz_name); + auto tz = my_tzdb.locate_zone(Tz_name); validate_precision(tz, Std_to_Day, sys_seconds::duration{1}); - validate_precision(tz, Std_to_Day, Milli_Dur{1}); - validate_precision(tz, Std_to_Day, Milli_Dur{0.5}); - validate_precision(tz, Std_to_Day, Milli_Dur{0.05}); - validate_precision(tz, Std_to_Day, Milli_Dur{0.005}); - validate_precision(tz, Std_to_Day, Milli_Dur{0.0005}); + validate_precision(tz, Std_to_Day, MilliDur{1}); + validate_precision(tz, Std_to_Day, MilliDur{0.5}); + validate_precision(tz, Std_to_Day, MilliDur{0.05}); + validate_precision(tz, Std_to_Day, MilliDur{0.005}); + validate_precision(tz, Std_to_Day, MilliDur{0.0005}); // precision limit... - // validate_precision(tz, Std_to_Day, Milli_Dur{0.00005}); - // validate_precision(tz, Std_to_Day, Micro_Dur{(numeric_limits::min)()}); + // validate_precision(tz, Std_to_Day, MilliDur{0.00005}); + // validate_precision(tz, Std_to_Day, MicroDur{(numeric_limits::min)()}); - validate_precision(tz, Std_to_Day, Micro_Dur{1}); - validate_precision(tz, Std_to_Day, Micro_Dur{0.5}); + validate_precision(tz, Std_to_Day, MicroDur{1}); + validate_precision(tz, Std_to_Day, MicroDur{0.5}); // precision limit... - // validate_precision(tz, Std_to_Day, Micro_Dur{0.05}); - // validate_precision(tz, Std_to_Day, Micro_Dur{(numeric_limits::min)()}); + // validate_precision(tz, Std_to_Day, MicroDur{0.05}); + // validate_precision(tz, Std_to_Day, MicroDur{(numeric_limits::min)()}); - validate_precision(tz, Day_to_Std, Milli_Dur{0.0005}); - validate_precision(tz, Day_to_Std, Micro_Dur{0.5}); + validate_precision(tz, Day_to_Std, MilliDur{0.0005}); + validate_precision(tz, Day_to_Std, MicroDur{0.5}); } } @@ -379,7 +382,7 @@ bool test() { timezone_to_local_test(); timezone_local_info_test(); timezone_precision_test(); - } catch (exception& ex) { + } catch (const exception& ex) { cerr << "Test threw exception: " << ex.what() << "\n"; assert(false); } diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/timezone_data.h b/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/timezone_data.h index 0d0fb5f3be8..9f726660135 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/timezone_data.h +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/timezone_data.h @@ -1,19 +1,21 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#pragma once #include #include #include #include +#include using namespace std; using namespace std::chrono; class Transition { public: - Transition( - string_view name, seconds offset, minutes save, string_view abbrev, sys_seconds _begin_, sys_seconds _end_) - : _name(name), _offset(offset), _save(save), _abbrev(abbrev), _begin(_begin_), _end(_end_) {} + constexpr Transition( + string_view name, seconds offset, minutes save, string_view abbrev, sys_seconds sys_begin, sys_seconds sys_end) + : _name(name), _offset(offset), _save(save), _abbrev(abbrev), _begin(sys_begin), _end(sys_end) {} constexpr string_view name() const { return _name; @@ -35,73 +37,76 @@ class Transition { return _save != minutes{0}; } - template - constexpr sys_time<_Duration> begin() const { - return sys_time<_Duration>{duration_cast<_Duration>(_begin.time_since_epoch())}; + template + constexpr sys_time begin() const { + return sys_time{duration_cast(_begin.time_since_epoch())}; } - template - constexpr sys_time<_Duration> end() const { - return sys_time<_Duration>{duration_cast<_Duration>(_end.time_since_epoch())}; + template + constexpr sys_time end() const { + return sys_time{duration_cast(_end.time_since_epoch())}; } - template - constexpr local_time<_Duration> local_begin() const { - return local_time<_Duration>{duration_cast<_Duration>(_begin.time_since_epoch() + _offset)}; + template + constexpr local_time local_begin() const { + return local_time{duration_cast(_begin.time_since_epoch() + _offset)}; } - template - constexpr local_time<_Duration> local_end() const { - return local_time<_Duration>{duration_cast<_Duration>(_end.time_since_epoch() + _offset)}; + template + constexpr local_time local_end() const { + return local_time{duration_cast(_end.time_since_epoch() + _offset)}; } private: - string _name; + string_view _name; seconds _offset; minutes _save; - string _abbrev; + string_view _abbrev; sys_seconds _begin; sys_seconds _end; }; // start of ambiguous/nonexistent zone between transitions -template -constexpr local_time<_Duration> get_danger_begin(const Transition& first, const Transition& second) { - assert(first.end<_Duration>() == second.begin<_Duration>()); - return first.local_end<_Duration>() - first.save(); +template +constexpr local_time get_danger_begin(const Transition& first, const Transition& second) { + assert(first.end() == second.begin()); + return first.local_end() - first.save(); } // end of ambiguous/nonexistent zone between transitions -template -constexpr local_time<_Duration> get_danger_end(const Transition& first, const Transition& second) { - assert(first.end<_Duration>() == second.begin<_Duration>()); - return second.local_begin<_Duration>() + first.save(); +template +constexpr local_time get_danger_end(const Transition& first, const Transition& second) { + assert(first.end() == second.begin()); + return second.local_begin() + first.save(); } // Sydney // Standard time (AEST : UTC+10) -1 @ 3am // Daylight time (AEDT : UTC+11) +1 @ 2am namespace Sydney { - static constexpr std::string_view Tz_name{"Australia/Sydney"sv}; - static constexpr std::string_view Standard_abbrev{"GMT+10"sv}; // IANA database == "AEST" - static constexpr std::string_view Daylight_abbrev{"GMT+11"sv}; // IANA database == "AEDT" - static constexpr seconds Standard_offset{hours{10}}; - static constexpr seconds Daylight_offset{hours{11}}; - static constexpr auto Daylight_begin_2019 = + inline constexpr string_view Tz_name{"Australia/Sydney"sv}; + inline constexpr string_view Standard_abbrev{"GMT+10"sv}; // IANA database == "AEST" + inline constexpr string_view Daylight_abbrev{"GMT+11"sv}; // IANA database == "AEDT" + inline constexpr seconds Standard_offset{hours{10}}; + inline constexpr seconds Daylight_offset{hours{11}}; + inline constexpr auto Daylight_begin_2019 = sys_seconds{sys_days{year{2019} / October / day{6}}} + hours{2} - Standard_offset; - static constexpr auto Standard_begin_2020 = + inline constexpr auto Standard_begin_2020 = sys_seconds{sys_days{year{2020} / April / day{5}}} + hours{3} - Daylight_offset; - static constexpr auto Daylight_begin_2020 = + inline constexpr auto Daylight_begin_2020 = sys_seconds{sys_days{year{2020} / October / day{4}}} + hours{2} - Standard_offset; - static constexpr auto Standard_begin_2021 = + inline constexpr auto Standard_begin_2021 = sys_seconds{sys_days{year{2021} / April / day{4}}} + hours{3} - Daylight_offset; - Transition Day_1{Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2019, Standard_begin_2020}; - Transition Std_1{Tz_name, Standard_offset, hours{0}, Standard_abbrev, Standard_begin_2020, Daylight_begin_2020}; - Transition Day_2{Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2020, Standard_begin_2021}; + inline constexpr Transition Day_1{ + Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2019, Standard_begin_2020}; + inline constexpr Transition Std_1{ + Tz_name, Standard_offset, hours{0}, Standard_abbrev, Standard_begin_2020, Daylight_begin_2020}; + inline constexpr Transition Day_2{ + Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2020, Standard_begin_2021}; - std::pair Day_to_Std{Day_1, Std_1}; - std::pair Std_to_Day{Std_1, Day_2}; + inline constexpr pair Day_to_Std{Day_1, Std_1}; + inline constexpr pair Std_to_Day{Std_1, Day_2}; } // namespace Sydney @@ -109,25 +114,28 @@ namespace Sydney { // Standard time (PST : UTC-8) +1 @ 2am // Daylight time (PDT : UTC-7) -1 @ 2am namespace LA { - static constexpr std::string_view Tz_name{"America/Los_Angeles"sv}; - static constexpr std::string_view Standard_abbrev{"PST"sv}; - static constexpr std::string_view Daylight_abbrev{"PDT"sv}; - static constexpr seconds Standard_offset{hours{-8}}; - static constexpr seconds Daylight_offset{hours{-7}}; - static constexpr auto Daylight_begin_2020 = + inline constexpr string_view Tz_name{"America/Los_Angeles"sv}; + inline constexpr string_view Standard_abbrev{"PST"sv}; + inline constexpr string_view Daylight_abbrev{"PDT"sv}; + inline constexpr seconds Standard_offset{hours{-8}}; + inline constexpr seconds Daylight_offset{hours{-7}}; + inline constexpr auto Daylight_begin_2020 = sys_seconds{sys_days{year{2020} / March / day{8}}} + hours{2} - Standard_offset; - static constexpr auto Standard_begin_2020 = + inline constexpr auto Standard_begin_2020 = sys_seconds{sys_days{year{2020} / November / day{1}}} + hours{2} - Daylight_offset; - static constexpr auto Daylight_begin_2021 = + inline constexpr auto Daylight_begin_2021 = sys_seconds{sys_days{year{2021} / March / day{14}}} + hours{2} - Standard_offset; - static constexpr auto Standard_begin_2021 = + inline constexpr auto Standard_begin_2021 = sys_seconds{sys_days{year{2021} / November / day{7}}} + hours{2} - Daylight_offset; - Transition Day_1{Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2020, Standard_begin_2020}; - Transition Std_1{Tz_name, Standard_offset, hours{0}, Standard_abbrev, Standard_begin_2020, Daylight_begin_2021}; - Transition Day_2{Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2021, Standard_begin_2021}; + inline constexpr Transition Day_1{ + Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2020, Standard_begin_2020}; + inline constexpr Transition Std_1{ + Tz_name, Standard_offset, hours{0}, Standard_abbrev, Standard_begin_2020, Daylight_begin_2021}; + inline constexpr Transition Day_2{ + Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2021, Standard_begin_2021}; - std::pair Day_to_Std{Day_1, Std_1}; - std::pair Std_to_Day{Std_1, Day_2}; + inline constexpr pair Day_to_Std{Day_1, Std_1}; + inline constexpr pair Std_to_Day{Std_1, Day_2}; } // namespace LA From 2ece3d0a4a50704ed9fc782a31be3af09ea91d2f Mon Sep 17 00:00:00 2001 From: Daniel Winsor Date: Tue, 16 Mar 2021 12:10:29 -0700 Subject: [PATCH 3/3] Final feedback --- stl/inc/chrono | 15 ++++++----- stl/src/tzdb.cpp | 2 +- .../test.cpp | 25 +++++++------------ 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 453085aa6a4..92d380c3d61 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -2346,7 +2346,7 @@ namespace chrono { _THROW(ambiguous_local_time(_Local, _Info)); } - return sys_time>{_Local.time_since_epoch()} - _Info.first.offset; + return sys_time>{_Local.time_since_epoch() - _Info.first.offset}; } template @@ -2360,27 +2360,26 @@ namespace chrono { const auto _Offset = (_Info.result == local_info::unique || _Choose == choose::earliest) ? _Info.first.offset : _Info.second.offset; - return sys_time>{_Local.time_since_epoch()} - _Offset; + return sys_time>{_Local.time_since_epoch() - _Offset}; } template _NODISCARD local_time> to_local(const sys_time<_Duration>& _Sys) const { const auto _Info = get_info(_Sys); - return local_time>{(_Sys + _Info.offset).time_since_epoch()}; + return local_time>{_Sys.time_since_epoch() + _Info.offset}; } - // FIXME: year::max() + year::min() - static constexpr sys_seconds _Min_seconds{sys_days{year{-32767} / January / 1}}; - static constexpr sys_seconds _Max_seconds{sys_days{year{32767} / December / 31}}; + static constexpr sys_seconds _Min_seconds{sys_days{(year::min)() / January / 1}}; + static constexpr sys_seconds _Max_seconds{sys_seconds{sys_days{(year::max)() / December / 32}} - seconds{1}}; private: template _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( + 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); + &__std_tzdb_delete_sys_info}; if (_Info == nullptr) { _Xbad_alloc(); } else if (_Info->_Err == __std_tzdb_error::_Win_error) { diff --git a/stl/src/tzdb.cpp b/stl/src/tzdb.cpp index 80a39caaf1d..0e13a44636c 100644 --- a/stl/src/tzdb.cpp +++ b/stl/src/tzdb.cpp @@ -463,7 +463,7 @@ void __stdcall __std_tzdb_delete_current_zone(__std_tzdb_current_zone_info* cons _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 == 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{ 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 1dbbd880781..ba6b4bd14aa 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 @@ -26,20 +26,20 @@ constexpr bool sys_equal(const sys_info& left, const sys_info& right) { } void test_time_zone_and_link(const tzdb& my_tzdb, string_view tz_name, string_view tz_link_name) { - const auto orginal_tz = my_tzdb.locate_zone(tz_name); - assert(orginal_tz != nullptr); - assert(orginal_tz->name() == tz_name); + const auto original_tz = my_tzdb.locate_zone(tz_name); + assert(original_tz != nullptr); + assert(original_tz->name() == tz_name); const auto linked_tz = my_tzdb.locate_zone(tz_link_name); assert(linked_tz != nullptr); assert(linked_tz->name() == tz_name); - assert(orginal_tz == linked_tz); + assert(original_tz == linked_tz); const auto tz_link = _Locate_zone_impl(my_tzdb.links, tz_link_name); assert(tz_link != nullptr); assert(tz_link->name() == tz_link_name); assert(tz_link->target() == tz_name); - assert(my_tzdb.locate_zone(tz_link->target()) == orginal_tz); + assert(my_tzdb.locate_zone(tz_link->target()) == original_tz); assert(_Locate_zone_impl(my_tzdb.zones, tz_name) != nullptr); assert(_Locate_zone_impl(my_tzdb.zones, tz_link_name) == nullptr); @@ -247,7 +247,6 @@ void validate_get_local_info(const time_zone* tz, const pair::min)()}); validate_precision(tz, Std_to_Day, MicroDur{1}); validate_precision(tz, Std_to_Day, MicroDur{0.5}); // precision limit... - // validate_precision(tz, Std_to_Day, MicroDur{0.05}); - // validate_precision(tz, Std_to_Day, MicroDur{(numeric_limits::min)()}); - validate_precision(tz, Day_to_Std, MilliDur{0.0005}); + // validate opposite transition validate_precision(tz, Day_to_Std, MicroDur{0.5}); + validate_precision(tz, Day_to_Std, MilliDur{0.0005}); } { using namespace LA; @@ -361,17 +357,14 @@ void timezone_precision_test() { validate_precision(tz, Std_to_Day, MilliDur{0.005}); validate_precision(tz, Std_to_Day, MilliDur{0.0005}); // precision limit... - // validate_precision(tz, Std_to_Day, MilliDur{0.00005}); - // validate_precision(tz, Std_to_Day, MicroDur{(numeric_limits::min)()}); validate_precision(tz, Std_to_Day, MicroDur{1}); validate_precision(tz, Std_to_Day, MicroDur{0.5}); // precision limit... - // validate_precision(tz, Std_to_Day, MicroDur{0.05}); - // validate_precision(tz, Std_to_Day, MicroDur{(numeric_limits::min)()}); - validate_precision(tz, Day_to_Std, MilliDur{0.0005}); + // validate opposite transition validate_precision(tz, Day_to_Std, MicroDur{0.5}); + validate_precision(tz, Day_to_Std, MilliDur{0.0005}); } }