Skip to content

[libc++][chrono] implements GPS clock. #125921

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
Feb 21, 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
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes/21.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Implemented Papers
------------------

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

Improvements and New Features
-----------------------------
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx20Issues.csv
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@
"`LWG3355 <https://wg21.link/LWG3355>`__","The memory algorithms should support move-only input iterators introduced by P1207","2020-02 (Prague)","|Complete|","15",""
"`LWG3356 <https://wg21.link/LWG3356>`__","``__cpp_lib_nothrow_convertible``\ should be ``__cpp_lib_is_nothrow_convertible``\ ","2020-02 (Prague)","|Complete|","12",""
"`LWG3358 <https://wg21.link/LWG3358>`__","|sect|\ [span.cons] is mistaken that ``to_address``\ can throw","2020-02 (Prague)","|Complete|","17",""
"`LWG3359 <https://wg21.link/LWG3359>`__","``<chrono>``\ leap second support should allow for negative leap seconds","2020-02 (Prague)","|In Progress|","",""
"`LWG3359 <https://wg21.link/LWG3359>`__","``<chrono>``\ leap second support should allow for negative leap seconds","2020-02 (Prague)","|Complete|","21",""
"`LWG3360 <https://wg21.link/LWG3360>`__","``three_way_comparable_with``\ is inconsistent with similar concepts","2020-02 (Prague)","|Nothing To Do|","",""
"`LWG3362 <https://wg21.link/LWG3362>`__","Strike ``stop_source``\ 's ``operator!=``\ ","2020-02 (Prague)","|Complete|","17",""
"`LWG3363 <https://wg21.link/LWG3363>`__","``drop_while_view``\ should opt-out of ``sized_range``\ ","2020-02 (Prague)","|Nothing To Do|","",""
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx20Papers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
"`P1207R4 <https://wg21.link/P1207R4>`__","Movability of Single-pass Iterators","2019-07 (Cologne)","|Complete|","15",""
"`P1208R6 <https://wg21.link/P1208R6>`__","Adopt source_location for C++20","2019-07 (Cologne)","|Complete|","16",""
"`P1355R2 <https://wg21.link/P1355R2>`__","Exposing a narrow contract for ceil2","2019-07 (Cologne)","|Complete|","9",""
"`P1361R2 <https://wg21.link/P1361R2>`__","Integration of chrono with text formatting","2019-07 (Cologne)","|Partial|","",""
"`P1361R2 <https://wg21.link/P1361R2>`__","Integration of chrono with text formatting","2019-07 (Cologne)","|Complete|","21",""
"`P1423R3 <https://wg21.link/P1423R3>`__","char8_t backward compatibility remediation","2019-07 (Cologne)","|Complete|","15",""
"`P1424R1 <https://wg21.link/P1424R1>`__","'constexpr' feature macro concerns","2019-07 (Cologne)","|Nothing To Do|","","Superseded by `P1902 <https://wg21.link/P1902>`__"
"`P1466R3 <https://wg21.link/P1466R3>`__","Miscellaneous minor fixes for chrono","2019-07 (Cologne)","|Partial|","",""
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 @@ -4,7 +4,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
`[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>``",,Mark de Wever,|Complete|,20
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::tai_time<Duration>``",,Mark de Wever,|Complete|,21
`[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::gps_time<Duration>``",,Mark de Wever,|Complete|,21
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::file_time<Duration>``",,Mark de Wever,|Complete|,17
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_time<Duration>``",,Mark de Wever,|Complete|,17
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local-time-format-t<Duration>``",,,|Nothing To Do|,
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ set(files
__chrono/exception.h
__chrono/file_clock.h
__chrono/formatter.h
__chrono/gps_clock.h
__chrono/hh_mm_ss.h
__chrono/high_resolution_clock.h
__chrono/leap_second.h
Expand Down
6 changes: 6 additions & 0 deletions libcxx/include/__chrono/convert_to_tm.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <__chrono/day.h>
#include <__chrono/duration.h>
#include <__chrono/file_clock.h>
#include <__chrono/gps_clock.h>
#include <__chrono/hh_mm_ss.h>
#include <__chrono/local_info.h>
#include <__chrono/month.h>
Expand Down Expand Up @@ -124,6 +125,11 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(chrono::tai_time<_Duration> __tp) {
return std::__convert_to_tm<_Tm>(chrono::sys_time<_Rp>{__tp.time_since_epoch() - __offset});
}

template <class _Tm, class _Duration>
_LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(chrono::gps_time<_Duration> __tp) {
return std::__convert_to_tm<_Tm>(chrono::utc_clock::to_sys(chrono::gps_clock::to_utc(__tp)));
}

# endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION

Expand Down
14 changes: 14 additions & 0 deletions libcxx/include/__chrono/formatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
# include <__chrono/day.h>
# include <__chrono/duration.h>
# include <__chrono/file_clock.h>
# include <__chrono/gps_clock.h>
# include <__chrono/hh_mm_ss.h>
# include <__chrono/local_info.h>
# include <__chrono/month.h>
Expand Down Expand Up @@ -235,6 +236,8 @@ _LIBCPP_HIDE_FROM_ABI __time_zone __convert_to_time_zone([[maybe_unused]] const
# if _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
else if constexpr (__is_time_point<_Tp> && requires { requires same_as<typename _Tp::clock, chrono::tai_clock>; })
return {"TAI", chrono::seconds{0}};
else if constexpr (__is_time_point<_Tp> && requires { requires same_as<typename _Tp::clock, chrono::gps_clock>; })
return {"GPS", chrono::seconds{0}};
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
return __formatter::__convert_to_time_zone(__value.get_info());
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM
Expand Down Expand Up @@ -748,6 +751,17 @@ struct _LIBCPP_TEMPLATE_VIS formatter<chrono::tai_time<_Duration>, _CharT> : pub
}
};

template <class _Duration, __fmt_char_type _CharT>
struct _LIBCPP_TEMPLATE_VIS formatter<chrono::gps_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 // _LIBCPP_HAS_EXPERIMENTAL_TZDB
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM

Expand Down
90 changes: 90 additions & 0 deletions libcxx/include/__chrono/gps_clock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// -*- 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_GPS_CLOCK_H
#define _LIBCPP___CHRONO_GPS_CLOCK_H

#include <version>
// Enable the contents of the header only when libc++ was built with experimental features enabled.
#if _LIBCPP_HAS_EXPERIMENTAL_TZDB

# include <__assert>
# include <__chrono/duration.h>
# include <__chrono/time_point.h>
# include <__chrono/utc_clock.h>
# include <__config>
# include <__type_traits/common_type.h>

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

_LIBCPP_PUSH_MACROS
# include <__undef_macros>

_LIBCPP_BEGIN_NAMESPACE_STD

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

namespace chrono {

class gps_clock;

template <class _Duration>
using gps_time = time_point<gps_clock, _Duration>;
using gps_seconds = gps_time<seconds>;

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

// The static difference between UTC and GPS time as specified in the Standard.
static constexpr chrono::seconds __offset{315964809};

[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static time_point now() { return from_utc(utc_clock::now()); }

template <class _Duration>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static utc_time<common_type_t<_Duration, seconds>>
to_utc(const gps_time<_Duration>& __time) noexcept {
using _Rp = common_type_t<_Duration, seconds>;
_Duration __time_since_epoch = __time.time_since_epoch();
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__time_since_epoch >= utc_time<_Rp>::min().time_since_epoch() + __offset,
"the GPS to UTC conversion would underflow");

return utc_time<_Rp>{__time_since_epoch + __offset};
}

template <class _Duration>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static gps_time<common_type_t<_Duration, seconds>>
from_utc(const utc_time<_Duration>& __time) noexcept {
using _Rp = common_type_t<_Duration, seconds>;
_Duration __time_since_epoch = __time.time_since_epoch();
_LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__time_since_epoch <= utc_time<_Rp>::max().time_since_epoch() - __offset,
"the UTC to GPS conversion would overflow");

return gps_time<_Rp>{__time_since_epoch - __offset};
}
};

} // namespace chrono

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

_LIBCPP_END_NAMESPACE_STD

_LIBCPP_POP_MACROS

#endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB

#endif // _LIBCPP___CHRONO_GPS_CLOCK_H
7 changes: 7 additions & 0 deletions libcxx/include/__chrono/ostream.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
# include <__chrono/day.h>
# include <__chrono/duration.h>
# include <__chrono/file_clock.h>
# include <__chrono/gps_clock.h>
# include <__chrono/hh_mm_ss.h>
# include <__chrono/local_info.h>
# include <__chrono/month.h>
Expand Down Expand Up @@ -78,6 +79,12 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const tai_time<_Duration>& __tp
return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%F %T}"), __tp);
}

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

# endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
# endif // _LIBCPP_HAS_TIME_ZONE_DATABASE && _LIBCPP_HAS_FILESYSTEM

Expand Down
29 changes: 29 additions & 0 deletions libcxx/include/chrono
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,33 @@ template<class charT, class traits, class Duration> // C++20
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& os, const tai_time<Duration>& t);

// [time.clock.gps], class gps_clock
class gps_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<gps_clock>;
static constexpr bool is_steady = unspecified;

static time_point now();

template<class Duration>
static utc_time<common_type_t<Duration, seconds>>
to_utc(const gps_time<Duration>& t);
template<class Duration>
static gps_time<common_type_t<Duration, seconds>>
from_utc(const utc_time<Duration>& t);
};

template<class Duration>
using gps_time = time_point<gps_clock, Duration>; // C++20
using gps_seconds = gps_time<seconds>; // C++20

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

class file_clock // C++20
{
public:
Expand Down Expand Up @@ -930,6 +957,8 @@ namespace std {
struct formatter<chrono::utc_time<Duration>, charT>; // C++20
template<class Duration, class charT>
struct formatter<chrono::tai_time<Duration>, charT>; // C++20
template<class Duration, class charT>
struct formatter<chrono::gps_time<Duration>, charT>; // C++20
template<class Duration, class charT>
struct formatter<chrono::file_time<Duration>, charT>; // C++20
template<class Duration, class charT>
Expand Down
4 changes: 4 additions & 0 deletions libcxx/include/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,10 @@ module std [system] {
module exception { header "__chrono/exception.h" }
module file_clock { header "__chrono/file_clock.h" }
module formatter { header "__chrono/formatter.h" }
module gps_clock {
header "__chrono/gps_clock.h"
export std.chrono.time_point
}
module hh_mm_ss { header "__chrono/hh_mm_ss.h" }
module high_resolution_clock {
header "__chrono/high_resolution_clock.h"
Expand Down
2 changes: 0 additions & 2 deletions libcxx/modules/std/chrono.inc
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,11 @@ export namespace std {
using std::chrono::tai_seconds;
using std::chrono::tai_time;

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

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

Expand Down
11 changes: 11 additions & 0 deletions libcxx/test/libcxx/diagnostics/chrono.nodiscard.verify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,15 @@ void test(std::chrono::time_zone tz, std::chrono::time_zone_link link, std::chro
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::chrono::tai_clock::from_utc(std::chrono::utc_seconds{});
}

{ // [time.clock.gps]
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::chrono::gps_clock::now();

// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::chrono::gps_clock::to_utc(std::chrono::gps_seconds{});

// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::chrono::gps_clock::from_utc(std::chrono::utc_seconds{});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

// REQUIRES: std-at-least-c++20
// UNSUPPORTED: no-filesystem, no-localization, no-tzdb

// XFAIL: libcpp-has-no-experimental-tzdb
// XFAIL: availability-tzdb-missing

// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
// REQUIRES: has-unix-headers
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing

// <chrono>
//
// class gps_clock;

// static gps_time<common_type_t<_Duration, seconds>>
// from_utc(const utc_time<_Duration>& t) noexcept;

#include <chrono>

#include "check_assertion.h"

// The function is specified as
// gps_time<common_type_t<Duration, seconds>>{t.time_since_epoch()} + 378691210s
// When t == t.max() there will be a signed integral overflow (other values too).
int main(int, char**) {
using namespace std::literals::chrono_literals;
constexpr std::chrono::seconds offset{315964809};

(void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::nanoseconds>::max() - offset);
TEST_LIBCPP_ASSERT_FAILURE(
std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::nanoseconds>::max() - offset + 1ns),
"the UTC to GPS conversion would overflow");

(void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::microseconds>::max() - offset);
TEST_LIBCPP_ASSERT_FAILURE(
std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::microseconds>::max() - offset + 1us),
"the UTC to GPS conversion would overflow");

(void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::milliseconds>::max() - offset);
TEST_LIBCPP_ASSERT_FAILURE(
std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::milliseconds>::max() - offset + 1ms),
"the UTC to GPS conversion would overflow");

(void)std::chrono::gps_clock::from_utc(std::chrono::utc_seconds::max() - offset);
TEST_LIBCPP_ASSERT_FAILURE(std::chrono::gps_clock::from_utc(std::chrono::utc_seconds::max() - offset + 1s),
"the UTC to GPS conversion would overflow");

// The conversion uses `common_type_t<Duration, seconds>` so types "larger"
// than seconds are converted to seconds. Types "larger" than seconds are
// stored in "smaller" intergral and the overflow can never occur.

// Validate the types can never overflow on all current (and future) supported platforms.
static_assert(std::chrono::utc_time<std::chrono::days>::max() <= std::chrono::utc_seconds::max() - offset);
static_assert(std::chrono::utc_time<std::chrono::weeks>::max() <= std::chrono::utc_seconds::max() - offset);
static_assert(std::chrono::utc_time<std::chrono::months>::max() <= std::chrono::utc_seconds::max() - offset);
static_assert(std::chrono::utc_time<std::chrono::years>::max() <= std::chrono::utc_seconds::max() - offset);

// Validate the run-time conversion works.
(void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::days>::max());
(void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::weeks>::max());
(void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::months>::max());
(void)std::chrono::gps_clock::from_utc(std::chrono::utc_time<std::chrono::years>::max());

return 0;
}
Loading
Loading