[libc++] Use __int128_t to represent file_time_type.

Summary:
The ``file_time_type`` time point is used to represent the write times for files.
Its job is to act as part of a C++ wrapper for less ideal system interfaces. The
underlying filesystem uses the ``timespec`` struct for the same purpose.

However, the initial implementation of ``file_time_type`` could not represent
either the range or resolution of ``timespec``, making it unsuitable. Fixing
this requires an implementation which uses more than 64 bits to store the
time point.

I primarily considered two solutions: Using ``__int128_t`` and using a
arithmetic emulation of ``timespec``. Each has its pros and cons, and both
come with more than one complication.

However, after a lot of consideration, I decided on using `__int128_t`. This patch implements that change.

Please see the [FileTimeType Design Document](http://libcxx.llvm.org/docs/DesignDocs/FileTimeType.html) for more information.

Reviewers: mclow.lists, ldionne, joerg, arthur.j.odwyer, EricWF

Reviewed By: EricWF

Subscribers: christof, K-ballo, cfe-commits, BillyONeal

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

llvm-svn: 337960
diff --git a/libcxx/src/experimental/filesystem/filesystem_common.h b/libcxx/src/experimental/filesystem/filesystem_common.h
index 104d0aa..7be1674 100644
--- a/libcxx/src/experimental/filesystem/filesystem_common.h
+++ b/libcxx/src/experimental/filesystem/filesystem_common.h
@@ -23,33 +23,17 @@
 
 #include <experimental/filesystem>
 
-#if (__APPLE__)
-#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
-#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101300
-#define _LIBCXX_USE_UTIMENSAT
-#endif
-#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
-#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 110000
-#define _LIBCXX_USE_UTIMENSAT
-#endif
-#elif defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__)
-#if __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ >= 110000
-#define _LIBCXX_USE_UTIMENSAT
-#endif
-#elif defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__)
-#if __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ >= 40000
-#define _LIBCXX_USE_UTIMENSAT
-#endif
-#endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__
-#else
+#include "../../include/apple_availability.h"
+
+#if !defined(__APPLE__)
 // We can use the presence of UTIME_OMIT to detect platforms that provide
 // utimensat.
 #if defined(UTIME_OMIT)
-#define _LIBCXX_USE_UTIMENSAT
+#define _LIBCPP_USE_UTIMENSAT
 #endif
-#endif // __APPLE__
+#endif
 
-#if !defined(_LIBCXX_USE_UTIMENSAT)
+#if !defined(_LIBCPP_USE_UTIMENSAT)
 #include <sys/time.h> // for ::utimes as used in __last_write_time
 #endif
 
@@ -212,76 +196,119 @@
   ErrorHandler& operator=(ErrorHandler const&) = delete;
 };
 
-namespace time_util {
+using chrono::duration;
+using chrono::duration_cast;
 
-using namespace chrono;
+using TimeSpec = struct ::timespec;
+using StatT = struct ::stat;
 
-template <class FileTimeT,
+template <class FileTimeT, class TimeT,
           bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
-struct fs_time_util_base {
-  static constexpr seconds::rep max_seconds =
-      duration_cast<seconds>(FileTimeT::duration::max()).count();
+struct time_util_base {
+  using rep = typename FileTimeT::rep;
+  using fs_duration = typename FileTimeT::duration;
+  using fs_seconds = duration<rep>;
+  using fs_nanoseconds = duration<rep, nano>;
+  using fs_microseconds = duration<rep, micro>;
 
-  static constexpr nanoseconds::rep max_nsec =
-      duration_cast<nanoseconds>(FileTimeT::duration::max() -
-                                 seconds(max_seconds))
+  static constexpr rep max_seconds =
+      duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
+
+  static constexpr rep max_nsec =
+      duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
+                                    fs_seconds(max_seconds))
           .count();
 
-  static constexpr seconds::rep min_seconds =
-      duration_cast<seconds>(FileTimeT::duration::min()).count();
+  static constexpr rep min_seconds =
+      duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
 
-  static constexpr nanoseconds::rep min_nsec_timespec =
-      duration_cast<nanoseconds>(
-          (FileTimeT::duration::min() - seconds(min_seconds)) + seconds(1))
+  static constexpr rep min_nsec_timespec =
+      duration_cast<fs_nanoseconds>(
+          (FileTimeT::duration::min() - fs_seconds(min_seconds)) +
+          fs_seconds(1))
           .count();
 
+private:
+#if _LIBCPP_STD_VER > 11
+  static constexpr fs_duration get_min_nsecs() {
+    return duration_cast<fs_duration>(
+        fs_nanoseconds(min_nsec_timespec) -
+        duration_cast<fs_nanoseconds>(fs_seconds(1)));
+  }
   // Static assert that these values properly round trip.
-  static_assert((seconds(min_seconds) +
-                 duration_cast<microseconds>(nanoseconds(min_nsec_timespec))) -
-                        duration_cast<microseconds>(seconds(1)) ==
+  static_assert(fs_seconds(min_seconds) + get_min_nsecs() ==
                     FileTimeT::duration::min(),
-                "");
+                "value doesn't roundtrip");
+
+  static constexpr bool check_range() {
+    // This kinda sucks, but it's what happens when we don't have __int128_t.
+    if (sizeof(TimeT) == sizeof(rep)) {
+      typedef duration<long long, ratio<3600 * 24 * 365> > Years;
+      return duration_cast<Years>(fs_seconds(max_seconds)) > Years(250) &&
+             duration_cast<Years>(fs_seconds(min_seconds)) < Years(-250);
+    }
+    return max_seconds >= numeric_limits<TimeT>::max() &&
+           min_seconds <= numeric_limits<TimeT>::min();
+  }
+  static_assert(check_range(), "the representable range is unacceptable small");
+#endif
 };
 
-template <class FileTimeT>
-struct fs_time_util_base<FileTimeT, true> {
-  static const long long max_seconds;
-  static const long long max_nsec;
-  static const long long min_seconds;
-  static const long long min_nsec_timespec;
+template <class FileTimeT, class TimeT>
+struct time_util_base<FileTimeT, TimeT, true> {
+  using rep = typename FileTimeT::rep;
+  using fs_duration = typename FileTimeT::duration;
+  using fs_seconds = duration<rep>;
+  using fs_nanoseconds = duration<rep, nano>;
+  using fs_microseconds = duration<rep, micro>;
+
+  static const rep max_seconds;
+  static const rep max_nsec;
+  static const rep min_seconds;
+  static const rep min_nsec_timespec;
 };
 
-template <class FileTimeT>
-const long long fs_time_util_base<FileTimeT, true>::max_seconds =
-    duration_cast<seconds>(FileTimeT::duration::max()).count();
+template <class FileTimeT, class TimeT>
+const typename FileTimeT::rep
+    time_util_base<FileTimeT, TimeT, true>::max_seconds =
+        duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
 
-template <class FileTimeT>
-const long long fs_time_util_base<FileTimeT, true>::max_nsec =
-    duration_cast<nanoseconds>(FileTimeT::duration::max() -
-                               seconds(max_seconds))
+template <class FileTimeT, class TimeT>
+const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_nsec =
+    duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
+                                  fs_seconds(max_seconds))
         .count();
 
-template <class FileTimeT>
-const long long fs_time_util_base<FileTimeT, true>::min_seconds =
-    duration_cast<seconds>(FileTimeT::duration::min()).count();
+template <class FileTimeT, class TimeT>
+const typename FileTimeT::rep
+    time_util_base<FileTimeT, TimeT, true>::min_seconds =
+        duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
 
-template <class FileTimeT>
-const long long fs_time_util_base<FileTimeT, true>::min_nsec_timespec =
-    duration_cast<nanoseconds>(
-        (FileTimeT::duration::min() - seconds(min_seconds)) + seconds(1))
-        .count();
+template <class FileTimeT, class TimeT>
+const typename FileTimeT::rep
+    time_util_base<FileTimeT, TimeT, true>::min_nsec_timespec =
+        duration_cast<fs_nanoseconds>((FileTimeT::duration::min() -
+                                       fs_seconds(min_seconds)) +
+                                      fs_seconds(1))
+            .count();
 
 template <class FileTimeT, class TimeT, class TimeSpecT>
-struct fs_time_util : fs_time_util_base<FileTimeT> {
-  using Base = fs_time_util_base<FileTimeT>;
+struct time_util : time_util_base<FileTimeT, TimeT> {
+  using Base = time_util_base<FileTimeT, TimeT>;
   using Base::max_nsec;
   using Base::max_seconds;
   using Base::min_nsec_timespec;
   using Base::min_seconds;
 
+  using typename Base::fs_duration;
+  using typename Base::fs_microseconds;
+  using typename Base::fs_nanoseconds;
+  using typename Base::fs_seconds;
+
 public:
   template <class CType, class ChronoType>
-  static bool checked_set(CType* out, ChronoType time) {
+  static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool checked_set(CType* out,
+                                                        ChronoType time) {
     using Lim = numeric_limits<CType>;
     if (time > Lim::max() || time < Lim::min())
       return false;
@@ -291,21 +318,21 @@
 
   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
     if (tm.tv_sec >= 0) {
-      return (tm.tv_sec < max_seconds) ||
+      return tm.tv_sec < max_seconds ||
              (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
     } else if (tm.tv_sec == (min_seconds - 1)) {
       return tm.tv_nsec >= min_nsec_timespec;
     } else {
-      return (tm.tv_sec >= min_seconds);
+      return tm.tv_sec >= min_seconds;
     }
   }
 
   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
-    auto secs = duration_cast<seconds>(tm.time_since_epoch());
-    auto nsecs = duration_cast<nanoseconds>(tm.time_since_epoch() - secs);
+    auto secs = duration_cast<fs_seconds>(tm.time_since_epoch());
+    auto nsecs = duration_cast<fs_nanoseconds>(tm.time_since_epoch() - secs);
     if (nsecs.count() < 0) {
-      secs = secs + seconds(1);
-      nsecs = nsecs + seconds(1);
+      secs = secs + fs_seconds(1);
+      nsecs = nsecs + fs_seconds(1);
     }
     using TLim = numeric_limits<TimeT>;
     if (secs.count() >= 0)
@@ -314,49 +341,45 @@
   }
 
   static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
-  convert_timespec(TimeSpecT tm) {
-    auto adj_msec = duration_cast<microseconds>(nanoseconds(tm.tv_nsec));
-    if (tm.tv_sec >= 0) {
-      auto Dur = seconds(tm.tv_sec) + microseconds(adj_msec);
-      return FileTimeT(Dur);
-    } else if (duration_cast<microseconds>(nanoseconds(tm.tv_nsec)).count() ==
-               0) {
-      return FileTimeT(seconds(tm.tv_sec));
+  convert_from_timespec(TimeSpecT tm) {
+    if (tm.tv_sec >= 0 || tm.tv_nsec == 0) {
+      return FileTimeT(fs_seconds(tm.tv_sec) +
+                       duration_cast<fs_duration>(fs_nanoseconds(tm.tv_nsec)));
     } else { // tm.tv_sec < 0
-      auto adj_subsec =
-          duration_cast<microseconds>(seconds(1) - nanoseconds(tm.tv_nsec));
-      auto Dur = seconds(tm.tv_sec + 1) - adj_subsec;
+      auto adj_subsec = duration_cast<fs_duration>(fs_seconds(1) -
+                                                   fs_nanoseconds(tm.tv_nsec));
+      auto Dur = fs_seconds(tm.tv_sec + 1) - adj_subsec;
       return FileTimeT(Dur);
     }
   }
 
-  template <class SubSecDurT, class SubSecT>
-  static bool set_times_checked(TimeT* sec_out, SubSecT* subsec_out,
-                                FileTimeT tp) {
+  template <class SubSecT>
+  static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool
+  set_times_checked(TimeT* sec_out, SubSecT* subsec_out, FileTimeT tp) {
     auto dur = tp.time_since_epoch();
-    auto sec_dur = duration_cast<seconds>(dur);
-    auto subsec_dur = duration_cast<SubSecDurT>(dur - sec_dur);
+    auto sec_dur = duration_cast<fs_seconds>(dur);
+    auto subsec_dur = duration_cast<fs_nanoseconds>(dur - sec_dur);
     // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
     if (subsec_dur.count() < 0) {
       if (sec_dur.count() > min_seconds) {
-        sec_dur -= seconds(1);
-        subsec_dur += seconds(1);
+        sec_dur -= fs_seconds(1);
+        subsec_dur += fs_seconds(1);
       } else {
-        subsec_dur = SubSecDurT::zero();
+        subsec_dur = fs_nanoseconds::zero();
       }
     }
     return checked_set(sec_out, sec_dur.count()) &&
            checked_set(subsec_out, subsec_dur.count());
   }
+  static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool convert_to_timespec(TimeSpecT& dest,
+                                                                FileTimeT tp) {
+    if (!is_representable(tp))
+      return false;
+    return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp);
+  }
 };
 
-} // namespace time_util
-
-
-using TimeSpec = struct ::timespec;
-using StatT = struct ::stat;
-
-using FSTime = time_util::fs_time_util<file_time_type, time_t, struct timespec>;
+using fs_time = time_util<file_time_type, time_t, TimeSpec>;
 
 #if defined(__APPLE__)
 TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
@@ -366,20 +389,18 @@
 TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
 #endif
 
-#if !defined(_LIBCXX_USE_UTIMENSAT)
-using TimeStruct = struct ::timeval;
-using TimeStructArray = TimeStruct[2];
+bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
+                    error_code& ec) {
+#if !defined(_LIBCPP_USE_UTIMENSAT)
+  using namespace chrono;
+  auto Convert = [](long nsec) {
+    return duration_cast<microseconds>(nanoseconds(nsec)).count();
+  };
+  struct ::timeval ConvertedTS[2] = {{TS[0].tv_sec, Convert(TS[0].tv_nsec)},
+                                     {TS[1].tv_sec, Convert(TS[1].tv_nsec)}};
+  if (::utimes(p.c_str(), ConvertedTS) == -1)
 #else
-using TimeStruct = TimeSpec;
-using TimeStructArray = TimeStruct[2];
-#endif
-
-bool SetFileTimes(const path& p, TimeStructArray const& TS,
-                  error_code& ec) {
-#if !defined(_LIBCXX_USE_UTIMENSAT)
-  if (::utimes(p.c_str(), TS) == -1)
-#else
-  if (::utimensat(AT_FDCWD, p.c_str(), TS, 0) == -1)
+  if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1)
 #endif
   {
     ec = capture_errno();
@@ -388,25 +409,9 @@
   return false;
 }
 
-void SetTimeStructTo(TimeStruct& TS, TimeSpec ToTS) {
-  using namespace chrono;
-  TS.tv_sec = ToTS.tv_sec;
-#if !defined(_LIBCXX_USE_UTIMENSAT)
-  TS.tv_usec = duration_cast<microseconds>(nanoseconds(ToTS.tv_nsec)).count();
-#else
-  TS.tv_nsec = ToTS.tv_nsec;
-#endif
-}
-
-bool SetTimeStructTo(TimeStruct& TS, file_time_type NewTime) {
-  using namespace chrono;
-#if !defined(_LIBCXX_USE_UTIMENSAT)
-  return !FSTime::set_times_checked<microseconds>(&TS.tv_sec, &TS.tv_usec,
-                                                  NewTime);
-#else
-  return !FSTime::set_times_checked<nanoseconds>(&TS.tv_sec, &TS.tv_nsec,
-                                                 NewTime);
-#endif
+bool set_time_spec_to(TimeSpec& TS, file_time_type NewTime) {
+  return !fs_time::set_times_checked(
+      &TS.tv_sec, &TS.tv_nsec, NewTime);
 }
 
 } // namespace
diff --git a/libcxx/src/experimental/filesystem/operations.cpp b/libcxx/src/experimental/filesystem/operations.cpp
index dd8b434..d570668 100644
--- a/libcxx/src/experimental/filesystem/operations.cpp
+++ b/libcxx/src/experimental/filesystem/operations.cpp
@@ -23,6 +23,7 @@
 #include <unistd.h>
 #include <sys/stat.h>
 #include <sys/statvfs.h>
+#include <time.h>
 #include <fcntl.h>  /* values for fchmodat */
 
 #if defined(__linux__)
@@ -36,6 +37,14 @@
 # define _LIBCPP_USE_COPYFILE
 #endif
 
+#if !defined(__APPLE__)
+#define _LIBCPP_USE_CLOCK_GETTIME
+#endif
+
+#if !defined(CLOCK_REALTIME) || !defined(_LIBCPP_USE_CLOCK_GETTIME)
+#include <sys/time.h> // for gettimeofday and timeval
+#endif                // !defined(CLOCK_REALTIME)
+
 #if defined(_LIBCPP_COMPILER_GCC)
 #if _GNUC_VER < 500
 #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
@@ -44,9 +53,6 @@
 
 _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
 
-filesystem_error::~filesystem_error() {}
-
-
 namespace { namespace parser
 {
 
@@ -355,7 +361,7 @@
   explicit FileDescriptor(const path* p, int fd = -1) : name(*p), fd(fd) {}
 };
 
-perms posix_get_perms(const struct ::stat& st) noexcept {
+perms posix_get_perms(const StatT& st) noexcept {
   return static_cast<perms>(st.st_mode) & perms::mask;
 }
 
@@ -364,8 +370,7 @@
 }
 
 file_status create_file_status(error_code& m_ec, path const& p,
-                               const struct ::stat& path_stat,
-                               error_code* ec) {
+                               const StatT& path_stat, error_code* ec) {
   if (ec)
     *ec = m_ec;
   if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) {
@@ -400,8 +405,7 @@
   return fs_tmp;
 }
 
-file_status posix_stat(path const& p, struct ::stat& path_stat,
-                       error_code* ec) {
+file_status posix_stat(path const& p, StatT& path_stat, error_code* ec) {
   error_code m_ec;
   if (::stat(p.c_str(), &path_stat) == -1)
     m_ec = detail::capture_errno();
@@ -409,12 +413,11 @@
 }
 
 file_status posix_stat(path const& p, error_code* ec) {
-  struct ::stat path_stat;
+  StatT path_stat;
   return posix_stat(p, path_stat, ec);
 }
 
-file_status posix_lstat(path const& p, struct ::stat& path_stat,
-                        error_code* ec) {
+file_status posix_lstat(path const& p, StatT& path_stat, error_code* ec) {
   error_code m_ec;
   if (::lstat(p.c_str(), &path_stat) == -1)
     m_ec = detail::capture_errno();
@@ -422,7 +425,7 @@
 }
 
 file_status posix_lstat(path const& p, error_code* ec) {
-  struct ::stat path_stat;
+  StatT path_stat;
   return posix_lstat(p, path_stat, ec);
 }
 
@@ -464,10 +467,32 @@
 using detail::capture_errno;
 using detail::ErrorHandler;
 using detail::StatT;
+using detail::TimeSpec;
 using parser::createView;
 using parser::PathParser;
 using parser::string_view_t;
 
+const bool _FilesystemClock::is_steady;
+
+_FilesystemClock::time_point _FilesystemClock::now() noexcept {
+  typedef chrono::duration<rep> __secs;
+#if defined(_LIBCPP_USE_CLOCK_GETTIME) && defined(CLOCK_REALTIME)
+  typedef chrono::duration<rep, nano> __nsecs;
+  struct timespec tp;
+  if (0 != clock_gettime(CLOCK_REALTIME, &tp))
+    __throw_system_error(errno, "clock_gettime(CLOCK_REALTIME) failed");
+  return time_point(__secs(tp.tv_sec) +
+                    chrono::duration_cast<duration>(__nsecs(tp.tv_nsec)));
+#else
+  typedef chrono::duration<rep, micro> __microsecs;
+  timeval tv;
+  gettimeofday(&tv, 0);
+  return time_point(__secs(tv.tv_sec) + __microsecs(tv.tv_usec));
+#endif // _LIBCPP_USE_CLOCK_GETTIME && CLOCK_REALTIME
+}
+
+filesystem_error::~filesystem_error() {}
+
 void filesystem_error::__create_what(int __num_paths) {
   const char* derived_what = system_error::what();
   __storage_->__what_ = [&]() -> string {
@@ -525,14 +550,14 @@
   const bool sym_status2 = bool(options & copy_options::copy_symlinks);
 
   error_code m_ec1;
-  struct ::stat f_st = {};
+  StatT f_st = {};
   const file_status f = sym_status || sym_status2
                             ? detail::posix_lstat(from, f_st, &m_ec1)
                             : detail::posix_stat(from, f_st, &m_ec1);
   if (m_ec1)
     return err.report(m_ec1);
 
-  struct ::stat t_st = {};
+  StatT t_st = {};
   const file_status t = sym_status ? detail::posix_lstat(to, t_st, &m_ec1)
                                    : detail::posix_stat(to, t_st, &m_ec1);
 
@@ -916,7 +941,7 @@
   ErrorHandler<uintmax_t> err("file_size", ec, &p);
 
   error_code m_ec;
-  struct ::stat st;
+  StatT st;
   file_status fst = detail::posix_stat(p, st, &m_ec);
   if (!exists(fst) || !is_regular_file(fst)) {
     errc error_kind =
@@ -966,14 +991,14 @@
 
 static file_time_type __extract_last_write_time(const path& p, const StatT& st,
                                                 error_code* ec) {
-  using detail::FSTime;
+  using detail::fs_time;
   ErrorHandler<file_time_type> err("last_write_time", ec, &p);
 
   auto ts = detail::extract_mtime(st);
-  if (!FSTime::is_representable(ts))
+  if (!fs_time::is_representable(ts))
     return err.report(errc::value_too_large);
 
-  return FSTime::convert_timespec(ts);
+  return fs_time::convert_from_timespec(ts);
 }
 
 file_time_type __last_write_time(const path& p, error_code *ec)
@@ -992,30 +1017,27 @@
 void __last_write_time(const path& p, file_time_type new_time,
                        error_code *ec)
 {
-    using namespace chrono;
-    using namespace detail;
-
     ErrorHandler<void> err("last_write_time", ec, &p);
 
     error_code m_ec;
-    TimeStructArray tbuf;
-#if !defined(_LIBCXX_USE_UTIMENSAT)
+    array<TimeSpec, 2> tbuf;
+#if !defined(_LIBCPP_USE_UTIMENSAT)
     // This implementation has a race condition between determining the
     // last access time and attempting to set it to the same value using
     // ::utimes
-    struct ::stat st;
+    StatT st;
     file_status fst = detail::posix_stat(p, st, &m_ec);
-    if (m_ec && !status_known(fst))
+    if (m_ec)
       return err.report(m_ec);
-    SetTimeStructTo(tbuf[0], detail::extract_atime(st));
+    tbuf[0] = detail::extract_atime(st);
 #else
     tbuf[0].tv_sec = 0;
     tbuf[0].tv_nsec = UTIME_OMIT;
 #endif
-    if (SetTimeStructTo(tbuf[1], new_time))
-      return err.report(errc::invalid_argument);
+    if (detail::set_time_spec_to(tbuf[1], new_time))
+      return err.report(errc::value_too_large);
 
-    SetFileTimes(p, tbuf, m_ec);
+    detail::set_file_times(p, tbuf, m_ec);
     if (m_ec)
       return err.report(m_ec);
 }
@@ -1591,7 +1613,7 @@
   __data_.__reset();
   error_code failure_ec;
 
-  struct ::stat full_st;
+  StatT full_st;
   file_status st = detail::posix_lstat(__p_, full_st, &failure_ec);
   if (!status_known(st)) {
     __data_.__reset();