Skip to content

[libc++][chrono] implements UTC clock. #90393

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx20Issues.csv
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@
"`LWG3313 <https://wg21.link/LWG3313>`__","``join_view::iterator::operator--``\ is incorrectly constrained","2020-02 (Prague)","|Complete|","14",""
"`LWG3314 <https://wg21.link/LWG3314>`__","Is stream insertion behavior locale dependent when ``Period::type``\ is ``micro``\ ?","2020-02 (Prague)","|Complete|","16",""
"`LWG3315 <https://wg21.link/LWG3315>`__","LWG3315: Correct Allocator Default Behavior","2020-02 (Prague)","|Complete|","",""
"`LWG3316 <https://wg21.link/LWG3316>`__","Correctly define epoch for ``utc_clock``\ / ``utc_timepoint``\ ","2020-02 (Prague)","","",""
"`LWG3316 <https://wg21.link/LWG3316>`__","Correctly define epoch for ``utc_clock``\ / ``utc_timepoint``\ ","2020-02 (Prague)","|Nothing To Do|","",""
"`LWG3317 <https://wg21.link/LWG3317>`__","Incorrect ``operator<<``\ for floating-point durations","2020-02 (Prague)","|Complete|","16",""
"`LWG3318 <https://wg21.link/LWG3318>`__","Clarify whether clocks can represent time before their epoch","2020-02 (Prague)","","",""
"`LWG3319 <https://wg21.link/LWG3319>`__","Properly reference specification of IANA time zone database","2020-02 (Prague)","|Nothing To Do|","",""
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/FormatPaper.csv
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
`P1361 <https://wg21.link/P1361>`__ `P2372 <https://wg21.link/P2372>`__,"Formatting chrono"
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::duration<Rep, Period>``",,Mark de Wever,|Complete|,16
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_time<Duration>``",,Mark de Wever,|Complete|,17
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::utc_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::utc_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,|Complete|,20
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::tai_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::gps_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::file_time<Duration>``",,Mark de Wever,|Complete|,17
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ set(files
__chrono/time_zone_link.h
__chrono/tzdb.h
__chrono/tzdb_list.h
__chrono/utc_clock.h
__chrono/weekday.h
__chrono/year.h
__chrono/year_month.h
Expand Down
23 changes: 23 additions & 0 deletions libcxx/include/__chrono/convert_to_tm.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <__chrono/sys_info.h>
#include <__chrono/system_clock.h>
#include <__chrono/time_point.h>
#include <__chrono/utc_clock.h>
#include <__chrono/weekday.h>
#include <__chrono/year.h>
#include <__chrono/year_month.h>
Expand Down Expand Up @@ -98,6 +99,22 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const chrono::sys_time<_Duration> __tp
return __result;
}

# if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)

template <class _Tm, class _Duration>
_LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(chrono::utc_time<_Duration> __tp) {
_Tm __result = std::__convert_to_tm<_Tm>(chrono::utc_clock::to_sys(__tp));

if (chrono::get_leap_second_info(__tp).is_leap_second)
++__result.tm_sec;

return __result;
}

# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION

// Convert a chrono (calendar) time point, or dururation to the given _Tm type,
// which must have the same properties as std::tm.
template <class _Tm, class _ChronoT>
Expand All @@ -110,6 +127,12 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
if constexpr (__is_time_point<_ChronoT>) {
if constexpr (same_as<typename _ChronoT::clock, chrono::system_clock>)
return std::__convert_to_tm<_Tm>(__value);
# if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
else if constexpr (same_as<typename _ChronoT::clock, chrono::utc_clock>)
return std::__convert_to_tm<_Tm>(__value);
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
else if constexpr (same_as<typename _ChronoT::clock, chrono::file_clock>)
return std::__convert_to_tm<_Tm>(_ChronoT::clock::to_sys(__value));
else if constexpr (same_as<typename _ChronoT::clock, chrono::local_t>)
Expand Down
18 changes: 18 additions & 0 deletions libcxx/include/__chrono/formatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
# include <__chrono/sys_info.h>
# include <__chrono/system_clock.h>
# include <__chrono/time_point.h>
# include <__chrono/utc_clock.h>
# include <__chrono/weekday.h>
# include <__chrono/year.h>
# include <__chrono/year_month.h>
Expand Down Expand Up @@ -719,6 +720,23 @@ struct _LIBCPP_TEMPLATE_VIS formatter<chrono::sys_time<_Duration>, _CharT> : pub
}
};

# if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)

template <class _Duration, __fmt_char_type _CharT>
struct _LIBCPP_TEMPLATE_VIS formatter<chrono::utc_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
public:
using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;

template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
}
};

# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM

template <class _Duration, __fmt_char_type _CharT>
struct _LIBCPP_TEMPLATE_VIS formatter<chrono::file_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
public:
Expand Down
13 changes: 13 additions & 0 deletions libcxx/include/__chrono/ostream.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
# include <__chrono/statically_widen.h>
# include <__chrono/sys_info.h>
# include <__chrono/system_clock.h>
# include <__chrono/utc_clock.h>
# include <__chrono/weekday.h>
# include <__chrono/year.h>
# include <__chrono/year_month.h>
Expand Down Expand Up @@ -61,6 +62,18 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_days& __dp) {
return __os << year_month_day{__dp};
}

# if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)

template <class _CharT, class _Traits, class _Duration>
_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __os, const utc_time<_Duration>& __tp) {
return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%F %T}"), __tp);
}

# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM

template <class _CharT, class _Traits, class _Duration>
_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __os, const file_time<_Duration> __tp) {
Expand Down
163 changes: 163 additions & 0 deletions libcxx/include/__chrono/utc_clock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___CHRONO_UTC_CLOCK_H
#define _LIBCPP___CHRONO_UTC_CLOCK_H

#include <version>
// Enable the contents of the header only when libc++ was built with experimental features enabled.
#if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)

# include <__chrono/duration.h>
# include <__chrono/leap_second.h>
# include <__chrono/system_clock.h>
# include <__chrono/time_point.h>
# include <__chrono/tzdb.h>
# include <__chrono/tzdb_list.h>
# include <__config>
# include <__type_traits/common_type.h>

# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
# endif

_LIBCPP_BEGIN_NAMESPACE_STD

# if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION

namespace chrono {

class utc_clock;

template <class _Duration>
using utc_time = time_point<utc_clock, _Duration>;
using utc_seconds = utc_time<seconds>;

class utc_clock {
public:
using rep = system_clock::rep;
using period = system_clock::period;
using duration = chrono::duration<rep, period>;
using time_point = chrono::time_point<utc_clock>;
static constexpr bool is_steady = false; // The system_clock is not steady.

[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static time_point now() { return from_sys(system_clock::now()); }

template <class _Duration>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static sys_time<common_type_t<_Duration, seconds>>
to_sys(const utc_time<_Duration>& __time);

template <class _Duration>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static utc_time<common_type_t<_Duration, seconds>>
from_sys(const sys_time<_Duration>& __time) {
using _Rp = utc_time<common_type_t<_Duration, seconds>>;
// TODO TZDB investigate optimizations.
//
// The leap second database stores all transitions, this mean to calculate
// the current number of leap seconds the code needs to iterate over all
// leap seconds to accumulate the sum. Then the sum can be used to determine
// the sys_time. Accessing the database involves acquiring a mutex.
//
// The historic entries in the database are immutable. Hard-coding these
// values in a table would allow:
// - To store the sum, allowing a binary search on the data.
// - Avoid acquiring a mutex.
// The disadvantage are:
// - A slightly larger code size.
//
// There are two optimization directions
// - hard-code the database and do a linear search for future entries. This
// search can start at the back, and should probably contain very few
// entries. (Adding leap seconds is quite rare and new release of libc++
// can add the new entries; they are announced half a year before they are
// added.)
// - During parsing the leap seconds store an additional database in the
// dylib with the list of the sum of the leap seconds. In that case there
// can be a private function __get_utc_to_sys_table that returns the
// table.
//
// Note for to_sys there are no optimizations to be done; it uses
// get_leap_second_info. The function get_leap_second_info could benefit
// from optimizations as described above; again both options apply.

// Both UTC and the system clock use the same epoch. The Standard
// specifies from 1970-01-01 even when UTC starts at
// 1972-01-01 00:00:10 TAI. So when the sys_time is before epoch we can be
// sure there both clocks return the same value.

const tzdb& __tzdb = chrono::get_tzdb();
_Rp __result{__time.time_since_epoch()};
for (const auto& __leap_second : __tzdb.leap_seconds) {
if (__leap_second > __time)
return __result;

__result += __leap_second.value();
}
return __result;
}
};

struct leap_second_info {
bool is_leap_second;
seconds elapsed;
};

template <class _Duration>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI leap_second_info get_leap_second_info(const utc_time<_Duration>& __time) {
const tzdb& __tzdb = chrono::get_tzdb();
if (__tzdb.leap_seconds.empty()) [[unlikely]]
return {false, chrono::seconds{0}};

sys_seconds __sys{chrono::floor<seconds>(__time).time_since_epoch()};
seconds __elapsed{0};
for (const auto& __leap_second : __tzdb.leap_seconds) {
if (__sys == __leap_second.date() + __elapsed)
// A time point may only be a leap second during a positive leap second
// insertion, since time points that occur during a (theoretical)
// negative leap second don't exist.
return {__leap_second.value() > 0s, __elapsed + __leap_second.value()};

if (__sys < __leap_second.date() + __elapsed)
return {false, __elapsed};

__elapsed += __leap_second.value();
}

return {false, __elapsed};
}

template <class _Duration>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI sys_time<common_type_t<_Duration, seconds>>
utc_clock::to_sys(const utc_time<_Duration>& __time) {
using _Dp = common_type_t<_Duration, seconds>;
leap_second_info __info = chrono::get_leap_second_info(__time);

// [time.clock.utc.members]/2
// Returns: A sys_time t, such that from_sys(t) == u if such a mapping
// exists. Otherwise u represents a time_point during a positive leap
// second insertion, the conversion counts that leap second as not
// inserted, and the last representable value of sys_time prior to the
// insertion of the leap second is returned.
sys_time<common_type_t<_Duration, seconds>> __result{__time.time_since_epoch() - __info.elapsed};
if (__info.is_leap_second)
return chrono::floor<seconds>(__result) + chrono::seconds{1} - _Dp{1};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is right for floating point duration reps, where the ulp can be more or less than _Dp{1}. I think nextafter is needed for reps where treat_as_floating_point_v is true. (Assuming you have such an overload for extended-precision floats. I'm also uncertain if [time.duration.general]/2 allows a user-defined type to "emulate" a floating point type by specializing treat_as_floating_point.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I'll need to look into this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added some tests with floating point, but I don't see an issue with the existing code. Do you have an example of problematic values?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't use float because the ulp is not less than 1 in the relevant range, so it's difficult to observe the effect. Also, the test does the same thing as the product code, which is subtract 1, but I don't think that's correct. For example, with the first leap second (78796800s) and double precision, I think the answer should be S{D{nextafter(78796800.0, 0.0)}}, i.e. 0x1.2c95fffffffffp+26, for any UTC time during the leap second insertion. To me, that's the "last representable value" prior to the insertion.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Floating-point concerns aside, I don't understand how this works. My (naive) understanding is that _Dp can be smaller than seconds, for example if common_type_t<_Duration, seconds> is chrono::milliseconds. In that case, you'd be flooring the result, then adding 1 second and subtracting one millisecond. So basically you'd be adding 0.999 seconds to the result. I don't understand why that is correct. If that's a bug, let's add a test!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The relevant language is [time.clock.utc.members]/2: "[If u represents a time during a leap second insertion,] the last representable value of sys_time prior to the insertion of the leap second is returned."

Briefly, I think the part you might be missing is that leap_second_info::elapsed is being subtracted before adding seconds{1} - _Dp{1}, and elapsed is incremented as soon as the insertion begins. So in this branch, when is_leap_second is true, floor<seconds>(__result) is one second before the leap second insertion began.

To illustrate, suppose that we're examining a day with a positive leap second. To make things easier, assume that the UTC epoch is midnight of that day, this is the first leap second ever, and the _Dpratio isratio<1,4>`. Then:

UTC time __time __info __result floor<seconds>(__result) + seconds{1} - _Dp{1} Returned system time
23:59:59.75 345,599 {false, 0s} 345,599 N/A 23:59:59.75
23:59:60.00 345,600 {true,1s} 345,596 345,599 (=345,596+4-1) 23:59:59.75
23:59:60.25 345,601 {true,1s} 345,597 345,599 23:59:59.75
23:59:60.50 345,602 {true,1s} 345,598 345,599 23:59:59.75
23:59:60.75 345,603 {true,1s} 345,599 345,599 23:59:59.75
00:00:00.00 345,604 {false,1s} 345,600 N/A 00:00:00.00

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MattStephanson I read your comment, but I'm not sure what the issue is. "the last representable value of sys_time prior to the insertion of the leap second is returned." Looking at the floor<seconds>(__result) + seconds{1} - _Dp{1} and "Returned system time" columns these are the values returned. Could you show the values you would have expected?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mordante I think the returned values are correct. I was trying to explain to @ldionne why the code is correct (except, as I said earlier, for floating point).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for the explanation, this makes a lot more sense now!


return __result;
}

} // namespace chrono

# endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM &&
// _LIBCPP_HAS_LOCALIZATION

_LIBCPP_END_NAMESPACE_STD

#endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)

#endif // _LIBCPP___CHRONO_UTC_CLOCK_H
38 changes: 38 additions & 0 deletions libcxx/include/chrono
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,41 @@ template<class charT, class traits> // C++20
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& os, const sys_days& dp);

// [time.clock.utc], class utc_clock
class utc_clock { // C++20
public:
using rep = a signed arithmetic type;
using period = ratio<unspecified, unspecified>;
using duration = chrono::duration<rep, period>;
using time_point = chrono::time_point<utc_clock>;
static constexpr bool is_steady = unspecified;

static time_point now();

template<class Duration>
static sys_time<common_type_t<Duration, seconds>>
to_sys(const utc_time<Duration>& t);
template<class Duration>
static utc_time<common_type_t<Duration, seconds>>
from_sys(const sys_time<Duration>& t);
};

template<class Duration>
using utc_time = time_point<utc_clock, Duration>; // C++20
using utc_seconds = utc_time<seconds>; // C++20

template<class charT, class traits, class Duration> // C++20
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& os, const utc_time<Duration>& t);

struct leap_second_info { // C++20
bool is_leap_second;
seconds elapsed;
};

template<class Duration> // C++20
leap_second_info get_leap_second_info(const utc_time<Duration>& ut);

class file_clock // C++20
{
public:
Expand Down Expand Up @@ -861,6 +896,8 @@ strong_ordering operator<=>(const time_zone_link& x, const time_zone_link& y);
namespace std {
template<class Duration, class charT>
struct formatter<chrono::sys_time<Duration>, charT>; // C++20
template<class Duration, class charT>
struct formatter<chrono::utc_time<Duration>, charT>; // C++20
template<class Duration, class charT>
struct formatter<chrono::filetime<Duration>, charT>; // C++20
template<class Duration, class charT>
Expand Down Expand Up @@ -981,6 +1018,7 @@ constexpr chrono::year operator ""y(unsigned lo
# include <__chrono/time_zone_link.h>
# include <__chrono/tzdb.h>
# include <__chrono/tzdb_list.h>
# include <__chrono/utc_clock.h>
# include <__chrono/zoned_time.h>
# endif

Expand Down
4 changes: 4 additions & 0 deletions libcxx/include/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,10 @@ module std [system] {
export std.string // public data member of type std::string
export std.vector // public data members of type std::vector
}
module utc_clock {
header "__chrono/utc_clock.h"
export std.chrono.time_point
}
module weekday { header "__chrono/weekday.h" }
module year_month_day { header "__chrono/year_month_day.h" }
module year_month_weekday { header "__chrono/year_month_weekday.h" }
Expand Down
11 changes: 9 additions & 2 deletions libcxx/modules/std/chrono.inc
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ export namespace std {
using std::chrono::sys_seconds;
using std::chrono::sys_time;

#if 0
#if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
# ifdef _LIBCPP_ENABLE_EXPERIMENTAL

// [time.clock.utc], class utc_clock
using std::chrono::utc_clock;

Expand All @@ -94,6 +96,8 @@ export namespace std {
using std::chrono::leap_second_info;

using std::chrono::get_leap_second_info;

# if 0
// [time.clock.tai], class tai_clock
using std::chrono::tai_clock;

Expand All @@ -105,7 +109,10 @@ export namespace std {

using std::chrono::gps_seconds;
using std::chrono::gps_time;
#endif
# endif
# endif // _LIBCPP_ENABLE_EXPERIMENTAL
#endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION

// [time.clock.file], type file_clock
using std::chrono::file_clock;

Expand Down
Loading
Loading