From 26ae9fb72a5c6ad959a8eb0f9e82b0b5089b35d8 Mon Sep 17 00:00:00 2001
From: Ivan Shapovalov <intelfx@intelfx.name>
Date: Sat, 2 Dec 2023 19:12:36 +0400
Subject: [PATCH] Implement `%j` specifier for std::chrono::duration

This adds support for `%j` presentation type for duration types:

> "If the type being formatted is a specialization of duration, the decimal
number of days without padding."

Fixes #3643.
---
 include/fmt/chrono.h | 8 +++++++-
 test/chrono-test.cc  | 2 ++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h
index 9b4f9d4ed5ab4..57cd0b701cfef 100644
--- a/include/fmt/chrono.h
+++ b/include/fmt/chrono.h
@@ -1622,6 +1622,7 @@ struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
 
   template <typename Char>
   FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
+  FMT_CONSTEXPR void on_day_of_year() {}
   FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
   FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
   FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
@@ -1806,6 +1807,7 @@ struct chrono_formatter {
     return true;
   }
 
+  Rep days() const { return static_cast<Rep>(s.count() / 86400); }
   Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
 
   Rep hour12() const {
@@ -1884,10 +1886,14 @@ struct chrono_formatter {
   void on_dec0_week_of_year(numeric_system) {}
   void on_dec1_week_of_year(numeric_system) {}
   void on_iso_week_of_year(numeric_system) {}
-  void on_day_of_year() {}
   void on_day_of_month(numeric_system) {}
   void on_day_of_month_space(numeric_system) {}
 
+  void on_day_of_year() {
+    if (handle_nan_inf()) return;
+    write(days(), 0);
+  }
+
   void on_24_hour(numeric_system ns, pad_type pad) {
     if (handle_nan_inf()) return;
 
diff --git a/test/chrono-test.cc b/test/chrono-test.cc
index 1eb34118daeb1..c9f08f20c9e69 100644
--- a/test/chrono-test.cc
+++ b/test/chrono-test.cc
@@ -537,6 +537,8 @@ TEST(chrono_test, format_specs) {
   EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(24)));
   EXPECT_EQ("04", fmt::format("{:%I}", std::chrono::hours(4)));
   EXPECT_EQ("02", fmt::format("{:%I}", std::chrono::hours(14)));
+  EXPECT_EQ("12345", fmt::format("{:%j}", days(12345)));
+  EXPECT_EQ("12345", fmt::format("{:%j}", std::chrono::hours(12345*24+12)));
   EXPECT_EQ("03:25:45",
             fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345)));
   EXPECT_EQ("03:25", fmt::format("{:%R}", std::chrono::seconds(12345)));