1 //===----------------------------------------------------------------------===////
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===////
9 #ifndef FILESYSTEM_TIME_UTILS_H
10 #define FILESYSTEM_TIME_UTILS_H
18 #include <system_error>
19 #include <type_traits>
23 #include "format_string.h"
25 #if defined(_LIBCPP_WIN32API)
26 # define WIN32_LEAN_AND_MEAN
31 # include <sys/stat.h>
32 # include <sys/time.h> // for ::utimes as used in __last_write_time
35 // We can use the presence of UTIME_OMIT to detect platforms that provide utimensat.
36 #if defined(UTIME_OMIT)
37 # define _LIBCPP_USE_UTIMENSAT
40 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
44 #if defined(_LIBCPP_WIN32API)
45 // Various C runtime versions (UCRT, or the legacy msvcrt.dll used by
46 // some mingw toolchains) provide different stat function implementations,
47 // with a number of limitations with respect to what we want from the
48 // stat function. Instead provide our own which does exactly what we want,
49 // along with our own stat structure and flag macros.
59 uint64_t st_dev
; // FILE_ID_INFO::VolumeSerialNumber
61 unsigned char id
[16]; // FILE_ID_INFO::FileId
62 bool operator==(const FileIdStruct
&other
) const {
63 for (int i
= 0; i
< 16; i
++)
64 if (id
[i
] != other
.id
[i
])
73 // There were 369 years and 89 leap days from the Windows epoch
74 // (1601) to the Unix epoch (1970).
75 #define FILE_TIME_OFFSET_SECS (uint64_t(369 * 365 + 89) * (24 * 60 * 60))
77 inline TimeSpec
filetime_to_timespec(LARGE_INTEGER li
) {
79 ret
.tv_sec
= li
.QuadPart
/ 10000000 - FILE_TIME_OFFSET_SECS
;
80 ret
.tv_nsec
= (li
.QuadPart
% 10000000) * 100;
84 inline TimeSpec
filetime_to_timespec(FILETIME ft
) {
86 li
.LowPart
= ft
.dwLowDateTime
;
87 li
.HighPart
= ft
.dwHighDateTime
;
88 return filetime_to_timespec(li
);
91 inline FILETIME
timespec_to_filetime(TimeSpec ts
) {
94 ts
.tv_nsec
/ 100 + (ts
.tv_sec
+ FILE_TIME_OFFSET_SECS
) * 10000000;
96 ft
.dwLowDateTime
= li
.LowPart
;
97 ft
.dwHighDateTime
= li
.HighPart
;
102 using TimeSpec
= struct timespec
;
103 using TimeVal
= struct timeval
;
104 using StatT
= struct stat
;
106 inline TimeVal
make_timeval(TimeSpec
const& ts
) {
107 using namespace chrono
;
108 auto Convert
= [](long nsec
) {
109 using int_type
= decltype(std::declval
<TimeVal
>().tv_usec
);
110 auto dur
= duration_cast
<microseconds
>(nanoseconds(nsec
)).count();
111 return static_cast<int_type
>(dur
);
114 TV
.tv_sec
= ts
.tv_sec
;
115 TV
.tv_usec
= Convert(ts
.tv_nsec
);
120 using chrono::duration
;
121 using chrono::duration_cast
;
123 template <class FileTimeT
, class TimeT
,
124 bool IsFloat
= is_floating_point
<typename
FileTimeT::rep
>::value
>
125 struct time_util_base
{
126 using rep
= typename
FileTimeT::rep
;
127 using fs_duration
= typename
FileTimeT::duration
;
128 using fs_seconds
= duration
<rep
>;
129 using fs_nanoseconds
= duration
<rep
, nano
>;
130 using fs_microseconds
= duration
<rep
, micro
>;
132 static constexpr rep max_seconds
=
133 duration_cast
<fs_seconds
>(FileTimeT::duration::max()).count();
135 static constexpr rep max_nsec
=
136 duration_cast
<fs_nanoseconds
>(FileTimeT::duration::max() -
137 fs_seconds(max_seconds
))
140 static constexpr rep min_seconds
=
141 duration_cast
<fs_seconds
>(FileTimeT::duration::min()).count();
143 static constexpr rep min_nsec_timespec
=
144 duration_cast
<fs_nanoseconds
>(
145 (FileTimeT::duration::min() - fs_seconds(min_seconds
)) +
150 static constexpr fs_duration
get_min_nsecs() {
151 return duration_cast
<fs_duration
>(
152 fs_nanoseconds(min_nsec_timespec
) -
153 duration_cast
<fs_nanoseconds
>(fs_seconds(1)));
155 // Static assert that these values properly round trip.
156 static_assert(fs_seconds(min_seconds
) + get_min_nsecs() ==
157 FileTimeT::duration::min(),
158 "value doesn't roundtrip");
160 static constexpr bool check_range() {
161 // This kinda sucks, but it's what happens when we don't have __int128_t.
162 if (sizeof(TimeT
) == sizeof(rep
)) {
163 typedef duration
<long long, ratio
<3600 * 24 * 365> > Years
;
164 return duration_cast
<Years
>(fs_seconds(max_seconds
)) > Years(250) &&
165 duration_cast
<Years
>(fs_seconds(min_seconds
)) < Years(-250);
167 return max_seconds
>= numeric_limits
<TimeT
>::max() &&
168 min_seconds
<= numeric_limits
<TimeT
>::min();
170 #if _LIBCPP_STD_VER >= 14
171 static_assert(check_range(), "the representable range is unacceptable small");
175 template <class FileTimeT
, class TimeT
>
176 struct time_util_base
<FileTimeT
, TimeT
, true> {
177 using rep
= typename
FileTimeT::rep
;
178 using fs_duration
= typename
FileTimeT::duration
;
179 using fs_seconds
= duration
<rep
>;
180 using fs_nanoseconds
= duration
<rep
, nano
>;
181 using fs_microseconds
= duration
<rep
, micro
>;
183 static const rep max_seconds
;
184 static const rep max_nsec
;
185 static const rep min_seconds
;
186 static const rep min_nsec_timespec
;
189 template <class FileTimeT
, class TimeT
>
190 const typename
FileTimeT::rep
191 time_util_base
<FileTimeT
, TimeT
, true>::max_seconds
=
192 duration_cast
<fs_seconds
>(FileTimeT::duration::max()).count();
194 template <class FileTimeT
, class TimeT
>
195 const typename
FileTimeT::rep time_util_base
<FileTimeT
, TimeT
, true>::max_nsec
=
196 duration_cast
<fs_nanoseconds
>(FileTimeT::duration::max() -
197 fs_seconds(max_seconds
))
200 template <class FileTimeT
, class TimeT
>
201 const typename
FileTimeT::rep
202 time_util_base
<FileTimeT
, TimeT
, true>::min_seconds
=
203 duration_cast
<fs_seconds
>(FileTimeT::duration::min()).count();
205 template <class FileTimeT
, class TimeT
>
206 const typename
FileTimeT::rep
207 time_util_base
<FileTimeT
, TimeT
, true>::min_nsec_timespec
=
208 duration_cast
<fs_nanoseconds
>((FileTimeT::duration::min() -
209 fs_seconds(min_seconds
)) +
213 template <class FileTimeT
, class TimeT
, class TimeSpecT
>
214 struct time_util
: time_util_base
<FileTimeT
, TimeT
> {
215 using Base
= time_util_base
<FileTimeT
, TimeT
>;
216 using Base::max_nsec
;
217 using Base::max_seconds
;
218 using Base::min_nsec_timespec
;
219 using Base::min_seconds
;
221 using typename
Base::fs_duration
;
222 using typename
Base::fs_microseconds
;
223 using typename
Base::fs_nanoseconds
;
224 using typename
Base::fs_seconds
;
227 template <class CType
, class ChronoType
>
228 static constexpr bool checked_set(CType
* out
,
230 using Lim
= numeric_limits
<CType
>;
231 if (time
> Lim::max() || time
< Lim::min())
233 *out
= static_cast<CType
>(time
);
237 static constexpr bool is_representable(TimeSpecT tm
) {
238 if (tm
.tv_sec
>= 0) {
239 return tm
.tv_sec
< max_seconds
||
240 (tm
.tv_sec
== max_seconds
&& tm
.tv_nsec
<= max_nsec
);
241 } else if (tm
.tv_sec
== (min_seconds
- 1)) {
242 return tm
.tv_nsec
>= min_nsec_timespec
;
244 return tm
.tv_sec
>= min_seconds
;
248 static constexpr bool is_representable(FileTimeT tm
) {
249 auto secs
= duration_cast
<fs_seconds
>(tm
.time_since_epoch());
250 auto nsecs
= duration_cast
<fs_nanoseconds
>(tm
.time_since_epoch() - secs
);
251 if (nsecs
.count() < 0) {
252 secs
= secs
+ fs_seconds(1);
253 nsecs
= nsecs
+ fs_seconds(1);
255 using TLim
= numeric_limits
<TimeT
>;
256 if (secs
.count() >= 0)
257 return secs
.count() <= TLim::max();
258 return secs
.count() >= TLim::min();
261 static constexpr FileTimeT
262 convert_from_timespec(TimeSpecT tm
) {
263 if (tm
.tv_sec
>= 0 || tm
.tv_nsec
== 0) {
264 return FileTimeT(fs_seconds(tm
.tv_sec
) +
265 duration_cast
<fs_duration
>(fs_nanoseconds(tm
.tv_nsec
)));
266 } else { // tm.tv_sec < 0
267 auto adj_subsec
= duration_cast
<fs_duration
>(fs_seconds(1) -
268 fs_nanoseconds(tm
.tv_nsec
));
269 auto Dur
= fs_seconds(tm
.tv_sec
+ 1) - adj_subsec
;
270 return FileTimeT(Dur
);
274 template <class SubSecT
>
275 static constexpr bool
276 set_times_checked(TimeT
* sec_out
, SubSecT
* subsec_out
, FileTimeT tp
) {
277 auto dur
= tp
.time_since_epoch();
278 auto sec_dur
= duration_cast
<fs_seconds
>(dur
);
279 auto subsec_dur
= duration_cast
<fs_nanoseconds
>(dur
- sec_dur
);
280 // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
281 if (subsec_dur
.count() < 0) {
282 if (sec_dur
.count() > min_seconds
) {
283 sec_dur
= sec_dur
- fs_seconds(1);
284 subsec_dur
= subsec_dur
+ fs_seconds(1);
286 subsec_dur
= fs_nanoseconds::zero();
289 return checked_set(sec_out
, sec_dur
.count()) &&
290 checked_set(subsec_out
, subsec_dur
.count());
292 static constexpr bool convert_to_timespec(TimeSpecT
& dest
,
294 if (!is_representable(tp
))
296 return set_times_checked(&dest
.tv_sec
, &dest
.tv_nsec
, tp
);
300 #if defined(_LIBCPP_WIN32API)
301 using fs_time
= time_util
<file_time_type
, int64_t, TimeSpec
>;
303 using fs_time
= time_util
<file_time_type
, time_t, TimeSpec
>;
306 #if defined(__APPLE__)
307 inline TimeSpec
extract_mtime(StatT
const& st
) { return st
.st_mtimespec
; }
308 inline TimeSpec
extract_atime(StatT
const& st
) { return st
.st_atimespec
; }
309 #elif defined(__MVS__)
310 inline TimeSpec
extract_mtime(StatT
const& st
) {
311 TimeSpec TS
= {st
.st_mtime
, 0};
314 inline TimeSpec
extract_atime(StatT
const& st
) {
315 TimeSpec TS
= {st
.st_atime
, 0};
319 inline TimeSpec
extract_mtime(StatT
const& st
) {
320 TimeSpec TS
= {st
.st_mtime
, st
.st_mtime_n
};
323 inline TimeSpec
extract_atime(StatT
const& st
) {
324 TimeSpec TS
= {st
.st_atime
, st
.st_atime_n
};
328 inline TimeSpec
extract_mtime(StatT
const& st
) { return st
.st_mtim
; }
329 inline TimeSpec
extract_atime(StatT
const& st
) { return st
.st_atim
; }
332 #ifndef _LIBCPP_HAS_NO_FILESYSTEM
334 #if !defined(_LIBCPP_WIN32API)
335 inline bool posix_utimes(const path
& p
, std::array
<TimeSpec
, 2> const& TS
,
337 TimeVal ConvertedTS
[2] = {make_timeval(TS
[0]), make_timeval(TS
[1])};
338 if (::utimes(p
.c_str(), ConvertedTS
) == -1) {
339 ec
= capture_errno();
345 #if defined(_LIBCPP_USE_UTIMENSAT)
346 inline bool posix_utimensat(const path
& p
, std::array
<TimeSpec
, 2> const& TS
,
348 if (::utimensat(AT_FDCWD
, p
.c_str(), TS
.data(), 0) == -1) {
349 ec
= capture_errno();
356 inline bool set_file_times(const path
& p
, std::array
<TimeSpec
, 2> const& TS
,
358 #if !defined(_LIBCPP_USE_UTIMENSAT)
359 return posix_utimes(p
, TS
, ec
);
361 return posix_utimensat(p
, TS
, ec
);
365 #endif // !_LIBCPP_WIN32API
367 inline file_time_type
__extract_last_write_time(const path
& p
, const StatT
& st
,
369 using detail::fs_time
;
370 ErrorHandler
<file_time_type
> err("last_write_time", ec
, &p
);
372 auto ts
= detail::extract_mtime(st
);
373 if (!fs_time::is_representable(ts
))
374 return err
.report(errc::value_too_large
);
376 return fs_time::convert_from_timespec(ts
);
379 #endif // !_LIBCPP_HAS_NO_FILESYSTEM
381 } // end namespace detail
383 _LIBCPP_END_NAMESPACE_FILESYSTEM
385 #endif // FILESYSTEM_TIME_UTILS_H