Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

It's possible to get an exception when working with localised times #4350

Open
musshorn opened this issue Feb 18, 2025 · 5 comments · May be fixed by #4352
Open

It's possible to get an exception when working with localised times #4350

musshorn opened this issue Feb 18, 2025 · 5 comments · May be fixed by #4352

Comments

@musshorn
Copy link

Environment:
Windows 10 x64
MSVC Version 17.12.4

It;'s possible to pass a std::chrono::local_seconds object to format that has an ambiguous time, so for example ~2am on April 6th 2025 in Sydney daylight savings ends.

Sample Code (note, system running must be on AEDT)

#include <chrono>
#include <cstdlib>
#include <iostream>
#include <fmt/core.h>
#include <fmt/chrono.h>

int main()
{
    const auto& timeZoneDatabase = std::chrono::get_tzdb();
    const auto& currentZone = timeZoneDatabase.current_zone();
    std::cout << currentZone->name() << '\n'; // This needs to be Australia/Sydney

    uint64_t two_in_the_morning = 1743865205; // just after 2am on 2025/4/6 which is a daylight savings changeover point
    auto dur = std::chrono::duration<uint64_t>(two_in_the_morning);
    auto time_point = std::chrono::time_point<std::chrono::system_clock, std::chrono::duration<uint64_t>>(dur);
    auto time_point_as_local_seconds = currentZone->to_local(std::chrono::floor<std::chrono::seconds>(time_point));

    std::string text = fmt::format("{:%Y-%m-%d %H:%M:%S}", time_point_as_local_seconds);
    std::cout << text << '\n';
    return 0;
}

This block here seems to be the cause:

fmt/include/fmt/chrono.h

Lines 575 to 580 in 94ab51c

template <typename Duration,
FMT_ENABLE_IF(detail::has_current_zone<Duration>())>
inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
using namespace std::chrono;
using namespace fmt_detail;
return localtime(detail::to_time_t(current_zone()->to_sys<Duration>(time)));

to_sys can throw exceptions in these cases, and needs to be passed std::chrono::choose

@vitaut
Copy link
Contributor

vitaut commented Feb 18, 2025

Could you submit a PR to fix this?

@musshorn musshorn linked a pull request Feb 19, 2025 that will close this issue
@vitaut vitaut changed the title It's possible to crash fmt when working with localised times It's possible to get an exception when working with localised times Feb 19, 2025
@vitaut
Copy link
Contributor

vitaut commented Feb 19, 2025

BTW what do std::format and iostreams do in this case?

@musshorn
Copy link
Author

Analyzing that is possibly a bit outside my ability, but I'll have a go:

std::format at least on MSVC seems check if the value is localized? Not 100% clear what that imbued stream does
https://github.com/microsoft/STL/blob/dfdccda510737c1e5fe8f84d7101df5aec269451/stl/inc/chrono#L5433

That gets passed to
https://github.com/microsoft/STL/blob/dfdccda510737c1e5fe8f84d7101df5aec269451/stl/inc/chrono#L5502

Which builds out the string.

@vitaut
Copy link
Contributor

vitaut commented Feb 24, 2025

I meant does pick earliest or latest or throws an exception in your example?

@musshorn
Copy link
Author

Oh it doesn't throw an exception, it simply prints:

Australia/Sydney
2025-04-06 02:00:05

I tried to dive in again, it doesn't seem to call to_sys at all. That would suggest my proposed pull is wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants