Support formatting of std time_point with utc_clock (#3110)
diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h
index ed7f5f1..1a8d8d0 100644
--- a/include/fmt/chrono.h
+++ b/include/fmt/chrono.h
@@ -22,6 +22,15 @@
 
 FMT_BEGIN_NAMESPACE
 
+// Check if std::chrono::utc_timestamp is available.
+#ifndef FMT_USE_UTC_TIME
+#  ifdef __cpp_lib_chrono
+#    define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L)
+#  else
+#    define FMT_USE_UTC_TIME 0
+#  endif
+#endif
+
 // Enable tzset.
 #ifndef FMT_USE_TZSET
 // UWP doesn't provide _tzset.
@@ -2014,6 +2023,25 @@
   }
 };
 
+#if FMT_USE_UTC_TIME
+template <typename Char, typename Duration>
+struct formatter<std::chrono::time_point<std::chrono::utc_clock, Duration>,
+                 Char> : formatter<std::tm, Char> {
+  FMT_CONSTEXPR formatter() {
+    basic_string_view<Char> default_specs =
+        detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
+    this->do_parse(default_specs.begin(), default_specs.end());
+  }
+
+  template <typename FormatContext>
+  auto format(std::chrono::time_point<std::chrono::utc_clock> val,
+              FormatContext& ctx) const -> decltype(ctx.out()) {
+    return formatter<std::tm, Char>::format(
+        localtime(std::chrono::utc_clock::to_sys(val)), ctx);
+  }
+};
+#endif
+
 template <typename Char> struct formatter<std::tm, Char> {
  private:
   enum class spec {
diff --git a/test/chrono-test.cc b/test/chrono-test.cc
index 0f2a249..bc474a4 100644
--- a/test/chrono-test.cc
+++ b/test/chrono-test.cc
@@ -641,3 +641,14 @@
 }
 
 #endif  // FMT_STATIC_THOUSANDS_SEPARATOR
+
+// Disable the utc_clock test for windows, as the icu.dll used for tzdb
+// (time zone database) is not shipped with many windows versions.
+#if FMT_USE_UTC_TIME && !defined(_WIN32)
+TEST(chrono_test, utc_clock) {
+  auto t1 = std::chrono::system_clock::now();
+  auto t1_utc = std::chrono::utc_clock::from_sys(t1);
+  EXPECT_EQ(fmt::format("{:%Y-%m-%d %H:%M:%S}", t1),
+            fmt::format("{:%Y-%m-%d %H:%M:%S}", t1_utc));
+}
+#endif