diff --git a/include/fmt/time.h b/include/fmt/time.h index 1a72ebf6..5013914f 100644 --- a/include/fmt/time.h +++ b/include/fmt/time.h @@ -31,6 +31,7 @@ inline null<> gmtime_s(...) { return null<>(); } enum class numeric_system { standard, + // Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale. alternative }; @@ -64,10 +65,10 @@ FMT_CONSTEXPR const Char *parse_chrono_format( handler.on_full_weekday(); break; case 'w': - handler.on_dec0_weekday(); + handler.on_dec0_weekday(numeric_system::standard); break; case 'u': - handler.on_dec1_weekday(); + handler.on_dec1_weekday(numeric_system::standard); break; // Month: case 'b': @@ -95,6 +96,12 @@ FMT_CONSTEXPR const Char *parse_chrono_format( throw format_error("invalid format"); c = *ptr++; switch (c) { + case 'w': + handler.on_dec0_weekday(numeric_system::alternative); + break; + case 'u': + handler.on_dec1_weekday(numeric_system::alternative); + break; case 'H': handler.on_24_hour(numeric_system::alternative); break; @@ -123,8 +130,8 @@ struct chrono_format_checker { void on_text(const Char *, const Char *) {} void on_abbr_weekday() {} void on_full_weekday() {} - void on_dec0_weekday() {} - void on_dec1_weekday() {} + void on_dec0_weekday(numeric_system) {} + void on_dec1_weekday(numeric_system) {} void on_abbr_month() {} void on_full_month() {} void on_24_hour(numeric_system) {} @@ -158,8 +165,8 @@ struct chrono_formatter { void write(int value, int width) { typedef typename int_traits::main_type main_type; - main_type n = value; - int num_digits = internal::count_digits(n); + main_type n = to_unsigned(value); + int num_digits = static_cast(internal::count_digits(n)); if (width > num_digits) out = std::fill_n(out, width - num_digits, '0'); out = format_decimal(out, n, num_digits); @@ -182,8 +189,8 @@ struct chrono_formatter { void on_abbr_weekday() {} void on_full_weekday() {} - void on_dec0_weekday() {} - void on_dec1_weekday() {} + void on_dec0_weekday(numeric_system) {} + void on_dec1_weekday(numeric_system) {} void on_abbr_month() {} void on_full_month() {} @@ -211,16 +218,23 @@ struct chrono_formatter { if (ns == numeric_system::standard) return write(minute, 2); auto time = tm(); - time.tm_minute = minute; + time.tm_min = minute; format_localized(time, 'M'); } - void on_second(numeric_system) { - write(to_int(s.count() % 60), 2); - if (ms != std::chrono::milliseconds()) { - *out++ = '.'; - write(to_int(ms.count()), 3); + void on_second(numeric_system ns) { + auto second = to_int(s.count() % 60); + if (ns == numeric_system::standard) { + write(second, 2); + if (ms != std::chrono::milliseconds()) { + *out++ = '.'; + write(to_int(ms.count()), 3); + } + return; } + auto time = tm(); + time.tm_sec = second; + format_localized(time, 'S'); } }; } // namespace internal diff --git a/test/time-test.cc b/test/time-test.cc index 940ae252..b6fcc401 100644 --- a/test/time-test.cc +++ b/test/time-test.cc @@ -82,6 +82,22 @@ TEST(TimeTest, Chrono) { fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345))); } +std::string format_tm(const std::tm &time, const char *spec, + const std::locale &loc) { + std::ostringstream os; + os.imbue(loc); + os << std::put_time(&time, spec); + return os.str(); +} + +#define EXPECT_TIME(spec, field, value, duration) { \ + auto time = std::tm(); \ + time.field = value; \ + std::locale("ja_JP.utf8"); \ + EXPECT_EQ(format_tm(time, spec, loc), \ + fmt::format(loc, "{:" spec "}", std::chrono::duration(value))); \ + } + TEST(TimeTest, ChronoLocale) { const char *loc_name = "ja_JP.utf8"; bool has_locale = false; @@ -94,21 +110,9 @@ TEST(TimeTest, ChronoLocale) { fmt::print("{} locale is missing.\n", loc_name); return; } - std::ostringstream os; - auto str = [&] { - auto s = os.str(); - os.str(""); - return s; - }; - os.imbue(loc); - auto time = std::tm(); - time.tm_hour = 14; - os << std::put_time(&time, "%OH"); - EXPECT_EQ(str(), fmt::format(loc, "{:%OH}", std::chrono::hours(14))); - os << std::put_time(&time, "%OI"); - EXPECT_EQ(str(), fmt::format(loc, "{:%OI}", std::chrono::hours(14))); - time.tm_minute = 42; - os << std::put_time(&time, "%OM"); - EXPECT_EQ(str(), fmt::format(loc, "{:%OM}", std::chrono::minutes(42))); + EXPECT_TIME("%OH", tm_hour, 14, hours); + EXPECT_TIME("%OI", tm_hour, 14, hours); + EXPECT_TIME("%OM", tm_min, 42, minutes); + EXPECT_TIME("%OS", tm_min, 42, seconds); } #endif