Skip to content

std::chrono::time_zone::get_info throws an exception saying: The operation completed successfully #3097

@cppdev123

Description

@cppdev123

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 !

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfixedSomething works now, yay!not reproducibleWe can’t reproduce the described behavior

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions