-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Originally asked at : https://stackoverflow.com/questions/73668044/msvc-stdchronotime-zoneget-info-throws-an-exception-saying-the-operation
I was trying to convert utc time returned from std::chrono::system_clock to local time before displaying it using std::format but I got a surprising exception from std::chrono::time_zone::to_local and its what message says: The operation completed successfully. What a surprise ! I looked into this method and it called std::chrono::time_zone::get_info on the passed time parameter and did a simple conversion so I tested std::chrono::time_zone::get_info separately and it was the source of this exception. I don't know is it a bug in the implementation or my code is buggy ?
Here is a simple code which throws this error:
try
{
const std::chrono::time_zone* current_z = std::chrono::current_zone();
std::cout << "current timezone name: " << current_z->name() << "\n";
auto sys_time = std::chrono::system_clock::now();
std::cout << "current system time: " << sys_time << "\n";
auto sys_info = current_z->get_info(sys_time); // throws exception what(): The operation completed successfully.
std::cout << "sys_info: offset: " << sys_info.offset << ", abbrev" << sys_info.abbrev << "\n";
//auto local_time = current_z->to_local(sys_time); // throws exception since it calls get_info
//std::cout << "current local time: " << local_time << "\n";
}
catch (const std::exception& ex)
{
std::cout << "caught exception: " << ex.what();
}
After looking at std::chrono::time_zone::get_info source code it will invoke this method: std::chrono::time_zone::_Get_info and its source:
template <class _Duration>
_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, _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) {
_XGetLastError();
} else if (_Info->_Err == __std_tzdb_error::_Icu_error) {
_Xruntime_error("Internal error loading IANA database information");
}
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<sys_seconds::duration>(_Internal_duration{_Info->_Begin})};
const auto _End =
_Info->_End >= _Max_internal
? _Max_seconds
: sys_seconds{_CHRONO duration_cast<sys_seconds::duration>(_Internal_duration{_Info->_End})};
return {.begin = _Begin,
.end = _End,
.offset = _CHRONO duration_cast<seconds>(_Internal_duration{_Info->_Offset}),
.save = _CHRONO duration_cast<minutes>(_Internal_duration{_Info->_Save}),
.abbrev = _Info->_Abbrev};
}
After stepping with the debugger (although debugging this header is hard) the _Info pointer returned by __std_tzdb_get_sys_info is not nullptr but its _Err is set to 1 which equals __std_tzdb_error::_Win_error so _XGetLastError is called to throw an exception with the error code from GetLastError and it will return 0 indicating no error code so an exception is thrown indicating that no error has happened !
I examined __std_tzdb_get_sys_info body in tzdb.cpp and found that only _Acquire_icu_functions may cause error __std_tzdb_error::_Win_error but it is also called by __std_tzdb_get_current_zone which is called by current_zone without failure !