diff --git a/doc/syntax.rst b/doc/syntax.rst index 74b64c5a..be3b5c77 100644 --- a/doc/syntax.rst +++ b/doc/syntax.rst @@ -315,8 +315,8 @@ Format specifications for chrono duration and time point types as well as modifier: "E" | "O" chrono_type: "a" | "A" | "b" | "B" | "c" | "C" | "d" | "D" | "e" | "F" | : "g" | "G" | "h" | "H" | "I" | "j" | "m" | "M" | "n" | "p" | - : "q" | "Q" | "r" | "R" | "S" | "t" | "T" | "u" | "U" | "V" | - : "w" | "W" | "x" | "X" | "y" | "Y" | "z" | "Z" | "%" + : "P" | "q" | "Q" | "r" | "R" | "S" | "t" | "T" | "u" | "U" | + : "V" | "w" | "W" | "x" | "X" | "y" | "Y" | "z" | "Z" | "%" Literal chars are copied unchanged to the output. Precision is valid only for ``std::chrono::duration`` types with a floating-point representation type. @@ -396,6 +396,8 @@ The available presentation types (*chrono_type*) are: +---------+--------------------------------------------------------------------+ | ``'p'`` | The AM/PM designations associated with a 12-hour clock. | +---------+--------------------------------------------------------------------+ +| ``'P'`` | ``%p``, but lowercase (am/pm). (GNU/glibc extension.) | ++---------+--------------------------------------------------------------------+ | ``'q'`` | The duration's unit suffix. | +---------+--------------------------------------------------------------------+ | ``'Q'`` | The duration's numeric value (as if extracted via ``.count()``). | diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 50d90c3c..797ecf52 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -838,6 +838,9 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, case 'p': handler.on_am_pm(); break; + case 'P': + handler.on_am_pm_lower(); + break; case 'Q': handler.on_duration_value(); break; @@ -976,6 +979,7 @@ template struct null_chrono_spec_handler { FMT_CONSTEXPR void on_24_hour_time() { unsupported(); } FMT_CONSTEXPR void on_iso_time() { unsupported(); } FMT_CONSTEXPR void on_am_pm() { unsupported(); } + FMT_CONSTEXPR void on_am_pm_lower() { unsupported(); } FMT_CONSTEXPR void on_duration_value() { unsupported(); } FMT_CONSTEXPR void on_duration_unit() { unsupported(); } FMT_CONSTEXPR void on_utc_offset(numeric_system) { unsupported(); } @@ -1019,6 +1023,7 @@ struct tm_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_24_hour_time() {} FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} + FMT_CONSTEXPR void on_am_pm_lower() {} FMT_CONSTEXPR void on_utc_offset(numeric_system) {} FMT_CONSTEXPR void on_tz_name() {} }; @@ -1636,6 +1641,15 @@ class tm_writer { } } + void on_am_pm_lower() { + if (is_classic_) { + *out_++ = tm_hour() < 12 ? 'a' : 'p'; + *out_++ = 'm'; + } else { + format_localized('P'); + } + } + // These apply to chrono durations but not tm. void on_duration_value() {} void on_duration_unit() {} @@ -1656,6 +1670,7 @@ struct chrono_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_24_hour_time() {} FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} + FMT_CONSTEXPR void on_am_pm_lower() {} FMT_CONSTEXPR void on_duration_value() const { if (has_precision_integral) { FMT_THROW(format_error("precision not allowed for this argument type")); @@ -2011,6 +2026,11 @@ struct chrono_formatter { format_tm(time(), &tm_writer_type::on_am_pm); } + void on_am_pm_lower() { + if (handle_nan_inf()) return; + format_tm(time(), &tm_writer_type::on_am_pm_lower); + } + void on_duration_value() { if (handle_nan_inf()) return; write_sign(); diff --git a/test/chrono-test.cc b/test/chrono-test.cc index cdd21319..c2d36606 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -973,4 +973,12 @@ TEST(chrono_test, glibc_extensions) { EXPECT_EQ(fmt::format("{:%_S}", d), " 3.140000"); EXPECT_EQ(fmt::format("{:%-S}", d), "3.140000"); } + + { + // %p is standard, %P is glibc + const auto tm_am = make_tm(1970, 1, 1, 0, 0, 0); + const auto tm_pm = make_tm(1970, 1, 1, 12, 0, 0); + EXPECT_EQ(fmt::format("{:%p,%P}", tm_am), "AM,am"); + EXPECT_EQ(fmt::format("{:%p,%P}", tm_pm), "PM,pm"); + } }