Skip to content

Commit 45205ed

Browse files
committed
[libc++][chrono] implements GPS clock.
Completes: - LWG3359 <chrono> leap second support should allow for negative leap seconds Implements parts of: - P0355 Extending <chrono> to Calendars and Time Zones - P1361 Integration of chrono with text formatting NOTE The original version of this patch was written before finishing the tzdb formatters so need to review the status of the "parts of papers" are they still part of are complete after this patch?
1 parent e83ad81 commit 45205ed

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)