May 1, 2021·4 min read·engineeringcppchronoformattinglocale

P2372 makes chrono localization an explicit formatting choice

Chrono formatting inherited a locale-sensitive default inconsistent with other standard formatters. The proposal adds an L option and explains the compatibility cost of correcting that default.

A decimal separator can change when a formatter encounters a duration instead of an ordinary floating-point number. That is the inconsistency Victor Zverovich and Corentin Jabot identify in C++20's chrono formatting. Ordinary formatting is locale-independent by default, but the integrated chrono formatters use localization without giving the caller equivalent control over the default.

Their P2372R0 proposal, dated May 1, proposes making chrono formatting locale-independent unless the format string explicitly selects localization. It adds the L option used by other standard formatters. The change concerns the bytes produced by existing format strings, so the paper also addresses compatibility rather than treating this as an additive convenience.

I prefer formatting rules whose environmental dependencies are visible at the call site. A duration in diagnostic output should not unexpectedly follow a different locale policy from a number beside it. Consistent defaults help a reader understand a format string without reconstructing which library proposal originally supplied each argument's formatter.

Integration inherited two locale policies

The earlier P1361R2 integration paper brought chrono output into the general text-formatting facility. It replaced separate chrono formatting operations with standard formatter specializations and enabled a single format string to contain dates and other values. That integration also introduced locale-taking overloads into the general formatting interface.

P2372 identifies a policy mismatch left by that work. Formatting 4.2 with an ordinary replacement field is locale-independent, while {:L} requests localization. Formatting a floating-point duration's seconds with %S instead uses the locale's decimal separator under the C++20 wording. The same numeric quantity can therefore receive different punctuation depending on its type.

The proposal uses a Russian locale to illustrate the result. A seconds duration representing 4.2 can produce 04,200, while ordinary numeric formatting produces 4.2. The specific decimal separator makes the mismatch visible, but the design issue is the absence of an opt-out for chrono's locale dependency.

The alternative %OS conversion does not solve that absence. P2372 explains that %S can already localize its separator, while the alternative form concerns localized numeral representation. The presence of an explicitly alternative conversion therefore cannot be taken to mean that the ordinary conversion is independent of locale.

The proposed option controls the locale source

Under the proposal, {:%S} uses the locale-independent behavior described through the C locale. {:L%S} requests the localized form. If the formatting function receives an explicit locale, the localized operation uses it. Otherwise it uses the global locale.

This distinction preserves control at two levels. The function argument can supply a locale object, while the format specification determines whether localization applies. Merely having a locale available does not require every formatted value to depend on it. That is consistent with the ordinary formatter model the paper aims to follow.

The authors do not redesign every date and time conversion. Existing E and O modifiers retain their roles, and localized numeral-system choices remain a broader topic. The proposed change supplies a consistent default and an explicit localization switch without claiming to settle all internationalized date presentation across the different numeral systems and conventions a locale may provide.

I find that limited scope useful. A correction to environmental dependence should not require a caller to adopt an unrelated notation for every date component. The proposal changes how the formatting locale is selected while retaining the familiar chrono conversions inside that policy.

Correcting output is a compatibility change

The paper records strong SG16 agreement that the existing behavior is a design defect. It also records a lack of consensus for the proposed resolution in the cited discussion. Concern focused on breaking existing format strings, which can be difficult to locate and migrate. Recognizing a defect and agreeing on its repair are separate decisions.

Implementation history informs the authors' argument. They report that {fmt} already used locale-independent behavior for most chrono conversions and had done so for more than two years. The specification therefore diverged from the implementation experience used to motivate the integration. P2372 also discusses the recently merged Microsoft implementation and the fixes it still needs.

Those observations support an argument for correcting the wording early, but they do not make the change behaviorally invisible. Code relying on a localized default must request L to preserve that intent under the proposed rule. The migration is a semantic edit to the format string because the dependency that was previously implicit becomes explicit.

The C-locale wording also gives implementations a way to describe unlocalized output without requiring locale facilities for that operation. The paper mentions environments without locale support as a consequence of the model. Localized presentation still has its own requirements, while an ordinary chrono format can use the same explicit policy distinction already familiar for other formatted values.