[libc++] Implement file_clock::{to,from}_sys

This is part of https://wg21.link/P0355R7. I am adding these methods
to provide an alternative for the {from,to}_time_t methods that were
removed in https://llvm.org/D113027.

Differential Revision: https://reviews.llvm.org/D113430

GitOrigin-RevId: dce5fc56b6191ec63fd1d2bed7d3c815bacc49b7
diff --git a/docs/ReleaseNotes.rst b/docs/ReleaseNotes.rst
index 9ccb211..62e5a0c 100644
--- a/docs/ReleaseNotes.rst
+++ b/docs/ReleaseNotes.rst
@@ -80,7 +80,9 @@
 
 - Removed the nonstandard methods ``std::chrono::file_clock::to_time_t`` and
   ``std::chrono::file_clock::from_time_t``; neither libstdc++ nor MSVC STL
-  had such methods.
+  had such methods. Instead, in C++20, you can use ``std::chrono::file_clock::from_sys``
+  and ``std::chrono::file_clock::to_sys``, which are specified in the Standard.
+  If you are not using C++20, you should move to it.
 
 - The declarations of functions ``declare_reachable``, ``undeclare_reachable``, ``declare_no_pointers``,
   ``undeclare_no_pointers``, and ``get_pointer_safety`` have been removed not only from C++2b but
diff --git a/include/chrono b/include/chrono
index ec510a9..f703d6c 100644
--- a/include/chrono
+++ b/include/chrono
@@ -276,7 +276,23 @@
 using sys_seconds = sys_time<seconds>;                  // C++20
 using sys_days    = sys_time<days>;                     // C++20
 
-class file_clock;                                       // C++20
+class file_clock                                        // C++20
+{
+public:
+    typedef see-below                      rep;
+    typedef nano                           period;
+    typedef chrono::duration<rep, period>  duration;
+    typedef chrono::time_point<file_clock> time_point;
+    static constexpr bool is_steady =      false;
+
+    static time_point now() noexcept;
+
+    template<class Duration>
+    static sys_time<see-below> to_sys(const file_time<Duration>&);
+
+    template<class Duration>
+    static file_time<see-below> from_sys(const sys_time<Duration>&);
+};
 
 template<class Duration>
   using file_time = time_point<file_clock, Duration>;   // C++20
@@ -2798,6 +2814,20 @@
   static _LIBCPP_CONSTEXPR_AFTER_CXX11 const bool is_steady = false;
 
   _LIBCPP_AVAILABILITY_FILESYSTEM _LIBCPP_FUNC_VIS static time_point now() noexcept;
+
+#if _LIBCPP_STD_VER > 17
+  template <class _Duration>
+  _LIBCPP_HIDE_FROM_ABI
+  static chrono::sys_time<_Duration> to_sys(const chrono::file_time<_Duration>& __t) {
+    return chrono::sys_time<_Duration>(__t.time_since_epoch());
+  }
+
+  template <class _Duration>
+  _LIBCPP_HIDE_FROM_ABI
+  static chrono::file_time<_Duration> from_sys(const chrono::sys_time<_Duration>& __t) {
+    return chrono::file_time<_Duration>(__t.time_since_epoch());
+  }
+#endif // _LIBCPP_STD_VER > 17
 };
 _LIBCPP_END_NAMESPACE_FILESYSTEM
 #endif // !_LIBCPP_CXX03_LANG
diff --git a/test/std/utilities/time/time.clock/time.clock.file/to_from_sys.pass.cpp b/test/std/utilities/time/time.clock/time.clock.file/to_from_sys.pass.cpp
new file mode 100644
index 0000000..d8fecd9
--- /dev/null
+++ b/test/std/utilities/time/time.clock/time.clock.file/to_from_sys.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-filesystem-library
+
+// Filesystem is supported on Apple platforms starting with macosx10.15.
+// UNSUPPORTED: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12|13|14}}
+
+// TODO(ldionne): This test fails on Ubuntu Focal on our CI nodes (and only there), in 32 bit mode.
+// UNSUPPORTED: linux && 32bits-on-64bits
+
+// <chrono>
+//
+// file_clock
+//
+// template<class Duration>
+// static sys_time<see-below> to_sys(const file_time<Duration>&);
+//
+// template<class Duration>
+// static file_time<see-below> from_sys(const sys_time<Duration>&);
+
+#include <chrono>
+#include <cassert>
+
+int main(int, char**) {
+  // Test round-trip through the system clock, starting from file_clock::now()
+  {
+    std::chrono::file_clock::time_point const ft = std::chrono::file_clock::now();
+    auto st = std::chrono::file_clock::to_sys(ft);
+    assert(ft == std::chrono::file_clock::from_sys(st));
+  }
+
+  // Test round-trip through the system clock, starting from system_clock::now()
+  {
+    std::chrono::system_clock::time_point const st = std::chrono::system_clock::now();
+    auto ft = std::chrono::file_clock::from_sys(st);
+    assert(st == std::chrono::file_clock::to_sys(ft));
+  }
+
+  // Make sure the value we get is in the ballpark of something reasonable
+  {
+    std::chrono::file_clock::time_point const file_now = std::chrono::file_clock::now();
+    std::chrono::system_clock::time_point const sys_now = std::chrono::system_clock::now();
+    {
+      auto diff = sys_now - std::chrono::file_clock::to_sys(file_now);
+      assert(std::chrono::milliseconds(-500) < diff && diff < std::chrono::milliseconds(500));
+    }
+    {
+      auto diff = std::chrono::file_clock::from_sys(sys_now) - file_now;
+      assert(std::chrono::milliseconds(-500) < diff && diff < std::chrono::milliseconds(500));
+    }
+  }
+
+  // Make sure to_sys and from_sys are consistent with each other
+  {
+    std::chrono::file_clock::time_point const ft = std::chrono::file_clock::now();
+    std::chrono::system_clock::time_point const st = std::chrono::system_clock::now();
+    auto sys_diff = std::chrono::file_clock::to_sys(ft) - st;
+    auto file_diff = ft - std::chrono::file_clock::from_sys(st);
+    assert(sys_diff == file_diff);
+  }
+
+  return 0;
+}