Skip to content

Commit cf50936

Browse files
authored
[libc++][chrono] implements GPS clock. (#125921)
Completes: - LWG3359 <chrono> leap second support should allow for negative leap seconds - P1361R2 Integration of chrono with text formatting Implements parts of: - P0355 Extending <chrono> to Calendars and Time Zones Fixes: #100432 Fixes: #100014
1 parent 5a7ee43 commit cf50936

File tree

21 files changed

+1870
-5
lines changed

21 files changed

+1870
-5
lines changed

libcxx/docs/ReleaseNotes/21.rst

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Implemented Papers
3939
------------------
4040

4141
- N4258: Cleaning-up noexcept in the Library (`Github <https://github.com/llvm/llvm-project/issues/99937>`__)
42+
- P1361R2: Integration of chrono with text formatting (`Github <https://github.com/llvm/llvm-project/issues/100014>`__)
4243

4344
Improvements and New Features
4445
-----------------------------

libcxx/docs/Status/Cxx20Issues.csv

+1-1
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@
269269
"`LWG3355 <https://wg21.link/LWG3355>`__","The memory algorithms should support move-only input iterators introduced by P1207","2020-02 (Prague)","|Complete|","15",""
270270
"`LWG3356 <https://wg21.link/LWG3356>`__","``__cpp_lib_nothrow_convertible``\ should be ``__cpp_lib_is_nothrow_convertible``\ ","2020-02 (Prague)","|Complete|","12",""
271271
"`LWG3358 <https://wg21.link/LWG3358>`__","|sect|\ [span.cons] is mistaken that ``to_address``\ can throw","2020-02 (Prague)","|Complete|","17",""
272-
"`LWG3359 <https://wg21.link/LWG3359>`__","``<chrono>``\ leap second support should allow for negative leap seconds","2020-02 (Prague)","|In Progress|","",""
272+
"`LWG3359 <https://wg21.link/LWG3359>`__","``<chrono>``\ leap second support should allow for negative leap seconds","2020-02 (Prague)","|Complete|","21",""
273273
"`LWG3360 <https://wg21.link/LWG3360>`__","``three_way_comparable_with``\ is inconsistent with similar concepts","2020-02 (Prague)","|Nothing To Do|","",""
274274
"`LWG3362 <https://wg21.link/LWG3362>`__","Strike ``stop_source``\ 's ``operator!=``\ ","2020-02 (Prague)","|Complete|","17",""
275275
"`LWG3363 <https://wg21.link/LWG3363>`__","``drop_while_view``\ should opt-out of ``sized_range``\ ","2020-02 (Prague)","|Nothing To Do|","",""

libcxx/docs/Status/Cxx20Papers.csv

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
"`P1207R4 <https://wg21.link/P1207R4>`__","Movability of Single-pass Iterators","2019-07 (Cologne)","|Complete|","15",""
116116
"`P1208R6 <https://wg21.link/P1208R6>`__","Adopt source_location for C++20","2019-07 (Cologne)","|Complete|","16",""
117117
"`P1355R2 <https://wg21.link/P1355R2>`__","Exposing a narrow contract for ceil2","2019-07 (Cologne)","|Complete|","9",""
118-
"`P1361R2 <https://wg21.link/P1361R2>`__","Integration of chrono with text formatting","2019-07 (Cologne)","|Partial|","",""
118+
"`P1361R2 <https://wg21.link/P1361R2>`__","Integration of chrono with text formatting","2019-07 (Cologne)","|Complete|","21",""
119119
"`P1423R3 <https://wg21.link/P1423R3>`__","char8_t backward compatibility remediation","2019-07 (Cologne)","|Complete|","15",""
120120
"`P1424R1 <https://wg21.link/P1424R1>`__","'constexpr' feature macro concerns","2019-07 (Cologne)","|Nothing To Do|","","Superseded by `P1902 <https://wg21.link/P1902>`__"
121121
"`P1466R3 <https://wg21.link/P1466R3>`__","Miscellaneous minor fixes for chrono","2019-07 (Cologne)","|Partial|","",""

libcxx/docs/Status/FormatPaper.csv

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
44
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_time<Duration>``",,Mark de Wever,|Complete|,17
55
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::utc_time<Duration>``",,Mark de Wever,|Complete|,20
66
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::tai_time<Duration>``",,Mark de Wever,|Complete|,21
7-
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::gps_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
7+
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::gps_time<Duration>``",,Mark de Wever,|Complete|,21
88
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::file_time<Duration>``",,Mark de Wever,|Complete|,17
99
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_time<Duration>``",,Mark de Wever,|Complete|,17
1010
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local-time-format-t<Duration>``",,,|Nothing To Do|,

libcxx/include/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ set(files
256256
__chrono/exception.h
257257
__chrono/file_clock.h
258258
__chrono/formatter.h
259+
__chrono/gps_clock.h
259260
__chrono/hh_mm_ss.h
260261
__chrono/high_resolution_clock.h
261262
__chrono/leap_second.h

libcxx/include/__chrono/convert_to_tm.h

+6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <__chrono/day.h>
1616
#include <__chrono/duration.h>
1717
#include <__chrono/file_clock.h>
18+
#include <__chrono/gps_clock.h>
1819
#include <__chrono/hh_mm_ss.h>
1920
#include <__chrono/local_info.h>
2021
#include <__chrono/month.h>
@@ -124,6 +125,11 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(chrono::tai_time<_Duration> __tp) {
124125
return std::__convert_to_tm<_Tm>(chrono::sys_time<_Rp>{__tp.time_since_epoch() - __offset});
125126
}
126127

128+
template <class _Tm, class _Duration>
129+
_LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(chrono::gps_time<_Duration> __tp) {
130+
return std::__convert_to_tm<_Tm>(chrono::utc_clock::to_sys(chrono::gps_clock::to_utc(__tp)));
131+
}
132+
127133
# endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
128134
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
129135

libcxx/include/__chrono/formatter.h

+14
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
# include <__chrono/day.h>
2222
# include <__chrono/duration.h>
2323
# include <__chrono/file_clock.h>
24+
# include <__chrono/gps_clock.h>
2425
# include <__chrono/hh_mm_ss.h>
2526
# include <__chrono/local_info.h>
2627
# include <__chrono/month.h>
@@ -235,6 +236,8 @@ _LIBCPP_HIDE_FROM_ABI __time_zone __convert_to_time_zone([[maybe_unused]] const
235236
# if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
236237
else if constexpr (__is_time_point<_Tp> && requires { requires same_as<typename _Tp::clock, chrono::tai_clock>; })
237238
return {"TAI", chrono::seconds{0}};
239+
else if constexpr (__is_time_point<_Tp> && requires { requires same_as<typename _Tp::clock, chrono::gps_clock>; })
240+
return {"GPS", chrono::seconds{0}};
238241
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
239242
return __formatter::__convert_to_time_zone(__value.get_info());
240243
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
@@ -748,6 +751,17 @@ struct _LIBCPP_TEMPLATE_VIS formatter<chrono::tai_time<_Duration>, _CharT> : pub
748751
}
749752
};
750753

754+
template <class _Duration, __fmt_char_type _CharT>
755+
struct _LIBCPP_TEMPLATE_VIS formatter<chrono::gps_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
756+
public:
757+
using _Base _LIBCPP_NODEBUG = __formatter_chrono<_CharT>;
758+
759+
template <class _ParseContext>
760+
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
761+
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
762+
}
763+
};
764+
751765
# endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
752766
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
753767

libcxx/include/__chrono/gps_clock.h

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// -*- C++ -*-
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef _LIBCPP___CHRONO_GPS_CLOCK_H
11+
#define _LIBCPP___CHRONO_GPS_CLOCK_H
12+
13+
#include <version>
14+
// Enable the contents of the header only when libc++ was built with experimental features enabled.
15+
#if _LIBCPP_HAS_EXPERIMENTAL_TZDB
16+
17+
# include <__assert>
18+
# include <__chrono/duration.h>
19+
# include <__chrono/time_point.h>
20+
# include <__chrono/utc_clock.h>
21+
# include <__config>
22+
# include <__type_traits/common_type.h>
23+
24+
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
25+
# pragma GCC system_header
26+
# endif
27+
28+
_LIBCPP_PUSH_MACROS
29+
# include <__undef_macros>
30+
31+
_LIBCPP_BEGIN_NAMESPACE_STD
32+
33+
# if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
34+
35+
namespace chrono {
36+
37+
class gps_clock;
38+
39+
template <class _Duration>
40+
using gps_time = time_point<gps_clock, _Duration>;
41+
using gps_seconds = gps_time<seconds>;
42+
43+
class gps_clock {
44+
public:
45+
using rep = utc_clock::rep;
46+
using period = utc_clock::period;
47+
using duration = chrono::duration<rep, period>;
48+
using time_point = chrono::time_point<gps_clock>;
49+
static constexpr bool is_steady = false; // The utc_clock is not steady.
50+
51+
// The static difference between UTC and GPS time as specified in the Standard.
52+
static constexpr chrono::seconds __offset{315964809};
53+
54+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static time_point now() { return from_utc(utc_clock::now()); }
55+
56+
template <class _Duration>
57+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static utc_time<common_type_t<_Duration, seconds>>
58+
to_utc(const gps_time<_Duration>& __time) noexcept {
59+
using _Rp = common_type_t<_Duration, seconds>;
60+
_Duration __time_since_epoch = __time.time_since_epoch();
61+
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__time_since_epoch >= utc_time<_Rp>::min().time_since_epoch() + __offset,
62+
"the GPS to UTC conversion would underflow");
63+
64+
return utc_time<_Rp>{__time_since_epoch + __offset};
65+
}
66+
67+
template <class _Duration>
68+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static gps_time<common_type_t<_Duration, seconds>>
69+
from_utc(const utc_time<_Duration>& __time) noexcept {
70+
using _Rp = common_type_t<_Duration, seconds>;
71+
_Duration __time_since_epoch = __time.time_since_epoch();
72+
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__time_since_epoch <= utc_time<_Rp>::max().time_since_epoch() - __offset,
73+
"the UTC to GPS conversion would overflow");
74+
75+
return gps_time<_Rp>{__time_since_epoch - __offset};
76+
}
77+
};
78+
79+
} // namespace chrono
80+
81+
# endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM &&
82+
// _LIBCPP_HAS_LOCALIZATION
83+
84+
_LIBCPP_END_NAMESPACE_STD
85+
86+
_LIBCPP_POP_MACROS
87+
88+
#endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
89+
90+
#endif // _LIBCPP___CHRONO_GPS_CLOCK_H

libcxx/include/__chrono/ostream.h

+7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
# include <__chrono/day.h>
1919
# include <__chrono/duration.h>
2020
# include <__chrono/file_clock.h>
21+
# include <__chrono/gps_clock.h>
2122
# include <__chrono/hh_mm_ss.h>
2223
# include <__chrono/local_info.h>
2324
# include <__chrono/month.h>
@@ -78,6 +79,12 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const tai_time<_Duration>& __tp
7879
return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%F %T}"), __tp);
7980
}
8081

82+
template <class _CharT, class _Traits, class _Duration>
83+
_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
84+
operator<<(basic_ostream<_CharT, _Traits>& __os, const gps_time<_Duration>& __tp) {
85+
return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%F %T}"), __tp);
86+
}
87+
8188
# endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
8289
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
8390

libcxx/include/chrono

+29
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,33 @@ template<class charT, class traits, class Duration> // C++20
363363
basic_ostream<charT, traits>&
364364
operator<<(basic_ostream<charT, traits>& os, const tai_time<Duration>& t);
365365
366+
// [time.clock.gps], class gps_clock
367+
class gps_clock { // C++20
368+
public:
369+
using rep = a signed arithmetic type;
370+
using period = ratio<unspecified, unspecified>;
371+
using duration = chrono::duration<rep, period>;
372+
using time_point = chrono::time_point<gps_clock>;
373+
static constexpr bool is_steady = unspecified;
374+
375+
static time_point now();
376+
377+
template<class Duration>
378+
static utc_time<common_type_t<Duration, seconds>>
379+
to_utc(const gps_time<Duration>& t);
380+
template<class Duration>
381+
static gps_time<common_type_t<Duration, seconds>>
382+
from_utc(const utc_time<Duration>& t);
383+
};
384+
385+
template<class Duration>
386+
using gps_time = time_point<gps_clock, Duration>; // C++20
387+
using gps_seconds = gps_time<seconds>; // C++20
388+
389+
template<class charT, class traits, class Duration> // C++20
390+
basic_ostream<charT, traits>&
391+
operator<<(basic_ostream<charT, traits>& os, const gps_time<Duration>& t);
392+
366393
class file_clock // C++20
367394
{
368395
public:
@@ -930,6 +957,8 @@ namespace std {
930957
struct formatter<chrono::utc_time<Duration>, charT>; // C++20
931958
template<class Duration, class charT>
932959
struct formatter<chrono::tai_time<Duration>, charT>; // C++20
960+
template<class Duration, class charT>
961+
struct formatter<chrono::gps_time<Duration>, charT>; // C++20
933962
template<class Duration, class charT>
934963
struct formatter<chrono::file_time<Duration>, charT>; // C++20
935964
template<class Duration, class charT>

libcxx/include/module.modulemap

+4
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,10 @@ module std [system] {
935935
module exception { header "__chrono/exception.h" }
936936
module file_clock { header "__chrono/file_clock.h" }
937937
module formatter { header "__chrono/formatter.h" }
938+
module gps_clock {
939+
header "__chrono/gps_clock.h"
940+
export std.chrono.time_point
941+
}
938942
module hh_mm_ss { header "__chrono/hh_mm_ss.h" }
939943
module high_resolution_clock {
940944
header "__chrono/high_resolution_clock.h"

libcxx/modules/std/chrono.inc

-2
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,11 @@ export namespace std {
103103
using std::chrono::tai_seconds;
104104
using std::chrono::tai_time;
105105

106-
# if 0
107106
// [time.clock.gps], class gps_clock
108107
using std::chrono::gps_clock;
109108

110109
using std::chrono::gps_seconds;
111110
using std::chrono::gps_time;
112-
# endif
113111
# endif // _LIBCPP_ENABLE_EXPERIMENTAL
114112
#endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
115113

libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,15 @@ void test(std::chrono::time_zone tz, std::chrono::time_zone_link link, std::chro
113113
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
114114
std::chrono::tai_clock::from_utc(std::chrono::utc_seconds{});
115115
}
116+
117+
{ // [time.clock.gps]
118+
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
119+
std::chrono::gps_clock::now();
120+
121+
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
122+
std::chrono::gps_clock::to_utc(std::chrono::gps_seconds{});
123+
124+
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
125+
std::chrono::gps_clock::from_utc(std::chrono::utc_seconds{});
126+
}
116127
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//===----------------------------------------------------------------------===//
2+
//
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
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// REQUIRES: std-at-least-c++20
10+
// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
11+
12+
// XFAIL: libcpp-has-no-experimental-tzdb
13+
// XFAIL: availability-tzdb-missing
14+
15+
// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
16+
// REQUIRES: has-unix-headers
17+
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
18+
19+
// <chrono>
20+
//
21+
// class gps_clock;
22+
23+
// static gps_time<common_type_t<_Duration, seconds>>
24+
// from_utc(const utc_time<_Duration>& t) noexcept;
25+
26+
#include <chrono>
27+
28+
#include "check_assertion.h"
29+
30+
// The function is specified as
31+
// gps_time<common_type_t<Duration, seconds>>{t.time_since_epoch()} + 378691210s
32+
// When t == t.max() there will be a signed integral overflow (other values too).
33+
int main(int, char**) {
34+
using namespace std::literals::chrono_literals;
35+
constexpr std::chrono::seconds offset{315964809};
36+
37+
(void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::nanoseconds>::max() - offset);
38+
TEST_LIBCPP_ASSERT_FAILURE(
39+
std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::nanoseconds>::max() - offset + 1ns),
40+
"the UTC to GPS conversion would overflow");
41+
42+
(void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::microseconds>::max() - offset);
43+
TEST_LIBCPP_ASSERT_FAILURE(
44+
std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::microseconds>::max() - offset + 1us),
45+
"the UTC to GPS conversion would overflow");
46+
47+
(void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::milliseconds>::max() - offset);
48+
TEST_LIBCPP_ASSERT_FAILURE(
49+
std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::milliseconds>::max() - offset + 1ms),
50+
"the UTC to GPS conversion would overflow");
51+
52+
(void)std::chrono::gps_clock::from_utc(std::chrono::utc_seconds::max() - offset);
53+
TEST_LIBCPP_ASSERT_FAILURE(std::chrono::gps_clock::from_utc(std::chrono::utc_seconds::max() - offset + 1s),
54+
"the UTC to GPS conversion would overflow");
55+
56+
// The conversion uses `common_type_t<Duration, seconds>` so types "larger"
57+
// than seconds are converted to seconds. Types "larger" than seconds are
58+
// stored in "smaller" intergral and the overflow can never occur.
59+
60+
// Validate the types can never overflow on all current (and future) supported platforms.
61+
static_assert(std::chrono::utc_time<std::chrono::days>::max() <= std::chrono::utc_seconds::max() - offset);
62+
static_assert(std::chrono::utc_time<std::chrono::weeks>::max() <= std::chrono::utc_seconds::max() - offset);
63+
static_assert(std::chrono::utc_time<std::chrono::months>::max() <= std::chrono::utc_seconds::max() - offset);
64+
static_assert(std::chrono::utc_time<std::chrono::years>::max() <= std::chrono::utc_seconds::max() - offset);
65+
66+
// Validate the run-time conversion works.
67+
(void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::days>::max());
68+
(void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::weeks>::max());
69+
(void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::months>::max());
70+
(void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::years>::max());
71+
72+
return 0;
73+
}

0 commit comments

Comments
 (0)