Skip to content

Don't require std::locale in chrono.h when FMT_USE_LOCALE is 0 - #4921

Open
nebkat wants to merge 1 commit into
fmtlib:mainfrom
nebkat:chrono-no-locale
Open

Don't require std::locale in chrono.h when FMT_USE_LOCALE is 0#4921
nebkat wants to merge 1 commit into
fmtlib:mainfrom
nebkat:chrono-no-locale

Conversation

@nebkat

@nebkat nebkat commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

tm_writer takes a locale_ref and the localized flag instead of a std::locale reference, and resolves the locale the same way numeric {:L} does: classic unless localized, otherwise the one passed to the formatting function or the global one. The facet calls sit behind three shims, so with FMT_USE_LOCALE=0 nothing in chrono.h pulls in std::locale, and get_locale, which existed only to materialize and own one, is gone.

Re the global-locale review comment: the std::tm formatter treated a missing locale argument as the classic locale rather than the global one, and since #4935 routes {:L} on weekday/month through it, on current main fmt::format("{:L}", weekday) after std::locale::global(...) produces the classic name regardless of this PR. This PR makes the std::tm formatter pass specs.localized() like every other path, so {:L} without a locale argument uses the global locale for all chrono types, as numeric {:L} does. That is the only output change with FMT_USE_LOCALE=1: formatting with an explicit locale is byte-identical across std::tm, weekday, month, year_month_day, durations and time_point, char and wchar_t. With FMT_USE_LOCALE=0, {:L} no longer consults the global locale, consistent with numeric formatting under that option. A header-only -Os test program goes from 16 std::locale/time_put symbols to 0 and from 180 kB to 140 kB.

Overlaps #4940 (year_month_day no longer constructs a locale for its default format) and, for the chrono side, takes the "ignore" option from #4941.

@vitaut vitaut left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. Reducing locale use is a good idea but we shouldn't introduce too much conditional compilation. locale_ref might help with that.

@nebkat

nebkat commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Reworked along those lines. tm_writer now takes a locale_ref, which is already empty when locale support is off, so the conditional compilation collapses to one block of shims next to write(), and get_locale — the only reason chrono.h owned a std::locale — is gone entirely. Rebased on main.

One gap if you want it covered: nolocale-test only compiles src/format.cc, so nothing exercises chrono.h with FMT_USE_LOCALE=0. Happy to add a TU that includes it.

@vitaut vitaut left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the behavior of {:L} when no locale is passed explicitly. Previously, an empty locale_ref with localized == true resulted in std::locale(), i.e. the current global locale. Now an empty locale_ref is treated as the classic locale.

For example, after std::locale::global(...), fmt::format("{:L}", weekday) should use that global locale, but this change appears to produce the classic English name instead.

@vitaut vitaut left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also please rebase and fix merge conflicts.

tm_writer takes a locale_ref and the localized flag instead of a std::locale
reference, and resolves the locale the same way numeric {:L} does: classic
unless localized, otherwise the one passed to the formatting function or the
global one. The facet calls are confined to three shims, so with locale
support disabled nothing pulls in std::locale. get_locale, which existed only
to materialize and own a std::locale, is no longer needed.

The std::tm formatter treated a missing locale argument as the classic locale
rather than the global one, and since fmtlib#4935 routes {:L} on weekday and month
through it, {:L} without a locale argument ignored the global locale for all
calendar types. It now uses the global locale, as numeric {:L} does. Output
with an explicit locale is unchanged. With FMT_USE_LOCALE=0, {:L} no longer
consults the global locale, matching numeric formatting under that option. An
-Os test program loses all 16 of its std::locale and time_put symbols.
@nebkat

nebkat commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

Rebased and fixed the conflicts with #4935.

One thing I ran into while doing that: #4935 dropped the L parsing from weekday/month, so {:L} on them now goes through the std::tm formatter — and that one has always treated a missing locale as the classic locale (the get_locale(static_cast<bool>(loc_ref), loc_ref) line predates std::tm having an L flag). So your std::locale::global(...); fmt::format("{:L}", weekday) example actually already gives Sat on main, independent of this PR.

I've made the std::tm formatter pass specs.localized() like the other paths do, so {:L} without a locale argument uses the global locale everywhere in chrono, same as it does for numbers. That's the only output difference with FMT_USE_LOCALE=1; anything formatted with an explicit locale is unchanged (I diffed the output across all the chrono types, char and wchar_t, to make sure). If you'd prefer std::tm to keep the classic fallback that's a one-liner, but then weekday/month lose the global locale too after #4935.

With FMT_USE_LOCALE=0, {:L} now ignores the global locale, like numeric formatting already does with that option. That's effectively the "ignore" option from #4941 for the chrono side; if you settle on a different contract there, the three shims next to write() are the only place that would need to change. This also covers the year_month_day change in #4940. Updated the description accordingly.

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 this pull request may close these issues.

2 participants