Support %P (lowercase am/pm) glibc extension

Fun fact: in glibc you can also spell %P %#p, # meaning “swap case”.
I haven’t implemented glibc’s ^ or # flags.
This commit is contained in:
Chris Morgan 2023-03-06 13:47:52 +11:00
parent 3a69529e8b
commit b242b8f326
3 changed files with 32 additions and 2 deletions

View File

@ -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()``). |

View File

@ -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 <typename Derived> 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<tm_format_checker> {
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<chrono_format_checker> {
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();

View File

@ -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");
}
}