[libcxx-commits] [libcxx] [libc++] fix `gps_time` formatting and related tests (PR #181560)

Matt Stephanson via libcxx-commits libcxx-commits at lists.llvm.org
Sun Feb 15 10:28:30 PST 2026


https://github.com/MattStephanson created https://github.com/llvm/llvm-project/pull/181560

 - The Standard wording, [[time.format]/13](https://eel.is/c++draft/time.format#13), is similar to TAI formatting in that it's equivalent to formatting a `sys_time` with a fixed offset. Leap seconds should not be considered.

 - Tests need to be adjusted by adding the number of leap seconds between the GPS epoch and the tested date, which is 15s for 2010 and 18s for 2019.

 - The TAI and GPS tests using `meow_time<cr::duration<long, std::ratio<1, 10>>>` should use `long long` because the offset will overflow a 32-bit signed integer.

>From ec3010b7c11ec6d0e594acc492b69d16c2bda12a Mon Sep 17 00:00:00 2001
From: Matt Stephanson <stephanson.matt at gmail.com>
Date: Sun, 15 Feb 2026 09:48:33 -0800
Subject: [PATCH] [libc++] fix `gps_time` formatting and related tests

 - The Standard wording, [time.format]/13, is similar to TAI formatting in that it's
equivalent to formatting a `sys_time` with a fixed offset. Leap seconds should not be
considered.

 - Tests need to be adjusted by adding the number of leap seconds between the GPS
epoch and the tested date, which is 15s for 2010 and 18s for 2019.

 - The TAI and GPS tests using `meow_time<cr::duration<long, std::ratio<1, 10>>>`
should use `long long` because the offset will overflow a 32-bit signed integer.
---
 libcxx/include/__chrono/convert_to_tm.h       |  5 +-
 .../time.clock.gps/gps_time.ostream.pass.cpp  | 54 +++++++++----------
 .../time.clock.tai/tai_time.ostream.pass.cpp  | 12 ++---
 3 files changed, 37 insertions(+), 34 deletions(-)

diff --git a/libcxx/include/__chrono/convert_to_tm.h b/libcxx/include/__chrono/convert_to_tm.h
index 817e6747a789f..16e1415238c2b 100644
--- a/libcxx/include/__chrono/convert_to_tm.h
+++ b/libcxx/include/__chrono/convert_to_tm.h
@@ -127,7 +127,10 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(chrono::tai_time<_Duration> __tp) {
 
 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)));
+  using _Rp = common_type_t<_Duration, chrono::seconds>;
+  // The time between the GPS epoch (1980-01-06) and UNIX epoch (1970-01-01).
+  constexpr chrono::seconds __offset{3657 * 24 * 60 * 60};
+  return std::__convert_to_tm<_Tm>(chrono::sys_time<_Rp>{__tp.time_since_epoch() + __offset});
 }
 
 #    endif // _LIBCPP_HAS_EXPERIMENTAL_TZDB
diff --git a/libcxx/test/std/time/time.clock/time.clock.gps/gps_time.ostream.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.gps/gps_time.ostream.pass.cpp
index 3f942bc63ce5a..25201a4dc1129 100644
--- a/libcxx/test/std/time/time.clock/time.clock.gps/gps_time.ostream.pass.cpp
+++ b/libcxx/test/std/time/time.clock/time.clock.gps/gps_time.ostream.pass.cpp
@@ -69,14 +69,14 @@ static void test_c() {
   namespace cr = std::chrono;
 
   assert(stream_c_locale<CharT>(cr::gps_time<cr::nanoseconds>{946'688'523'123'456'789ns}) ==
-         SV("2010-01-05 01:01:48.123456789"));
+         SV("2010-01-05 01:02:03.123456789"));
   assert(stream_c_locale<CharT>(cr::gps_time<cr::microseconds>{946'688'523'123'456us}) ==
-         SV("2010-01-05 01:01:48.123456"));
+         SV("2010-01-05 01:02:03.123456"));
 
-  assert(stream_c_locale<CharT>(cr::gps_time<cr::milliseconds>{946'684'822'123ms}) == SV("2010-01-05 00:00:07.123"));
-  assert(stream_c_locale<CharT>(cr::gps_seconds{1'234'567'890s}) == SV("2019-02-18 23:31:12"));
-  assert(stream_c_locale<CharT>(cr::gps_time<cr::minutes>{20'576'131min}) == SV("2019-02-18 23:30:42"));
-  assert(stream_c_locale<CharT>(cr::gps_time<cr::hours>{342'935h}) == SV("2019-02-18 22:59:42"));
+  assert(stream_c_locale<CharT>(cr::gps_time<cr::milliseconds>{946'684'822'123ms}) == SV("2010-01-05 00:00:22.123"));
+  assert(stream_c_locale<CharT>(cr::gps_seconds{1'234'567'890s}) == SV("2019-02-18 23:31:30"));
+  assert(stream_c_locale<CharT>(cr::gps_time<cr::minutes>{20'576'131min}) == SV("2019-02-18 23:31:00"));
+  assert(stream_c_locale<CharT>(cr::gps_time<cr::hours>{342'935h}) == SV("2019-02-18 23:00:00"));
 
   assert(stream_c_locale<CharT>(cr::gps_time<cr::duration<signed char, std::ratio<2, 1>>>{
              cr::duration<signed char, std::ratio<2, 1>>{60}}) == SV("1980-01-06 00:02:00"));
@@ -84,10 +84,10 @@ static void test_c() {
              cr::duration<short, std::ratio<1, 2>>{3600}}) == SV("1980-01-06 00:30:00.0"));
   assert(stream_c_locale<CharT>(cr::gps_time<cr::duration<int, std::ratio<1, 4>>>{
              cr::duration<int, std::ratio<1, 4>>{3600}}) == SV("1980-01-06 00:15:00.00"));
-  assert(stream_c_locale<CharT>(cr::gps_time<cr::duration<long, std::ratio<1, 10>>>{
-             cr::duration<long, std::ratio<1, 10>>{36611}}) == SV("1980-01-06 01:01:01.1"));
+  assert(stream_c_locale<CharT>(cr::gps_time<cr::duration<long long, std::ratio<1, 10>>>{
+             cr::duration<long long, std::ratio<1, 10>>{36611}}) == SV("1980-01-06 01:01:01.1"));
   assert(stream_c_locale<CharT>(cr::gps_time<cr::duration<long long, std::ratio<1, 100>>>{
-             cr::duration<long long, std::ratio<1, 100>>{123'456'789'010}}) == SV("2019-02-18 23:31:12.10"));
+             cr::duration<long long, std::ratio<1, 100>>{123'456'789'010}}) == SV("2019-02-18 23:31:30.10"));
 }
 
 template <class CharT>
@@ -96,15 +96,15 @@ static void test_fr_FR() {
   namespace cr = std::chrono;
 
   assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::nanoseconds>{946'688'523'123'456'789ns}) ==
-         SV("2010-01-05 01:01:48,123456789"));
+         SV("2010-01-05 01:02:03,123456789"));
   assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::microseconds>{946'688'523'123'456us}) ==
-         SV("2010-01-05 01:01:48,123456"));
+         SV("2010-01-05 01:02:03,123456"));
 
   assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::milliseconds>{946'684'822'123ms}) ==
-         SV("2010-01-05 00:00:07,123"));
-  assert(stream_fr_FR_locale<CharT>(cr::gps_seconds{1'234'567'890s}) == SV("2019-02-18 23:31:12"));
-  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::minutes>{20'576'131min}) == SV("2019-02-18 23:30:42"));
-  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::hours>{342'935h}) == SV("2019-02-18 22:59:42"));
+         SV("2010-01-05 00:00:22,123"));
+  assert(stream_fr_FR_locale<CharT>(cr::gps_seconds{1'234'567'890s}) == SV("2019-02-18 23:31:30"));
+  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::minutes>{20'576'131min}) == SV("2019-02-18 23:31:00"));
+  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::hours>{342'935h}) == SV("2019-02-18 23:00:00"));
 
   assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::duration<signed char, std::ratio<2, 1>>>{
              cr::duration<signed char, std::ratio<2, 1>>{60}}) == SV("1980-01-06 00:02:00"));
@@ -112,10 +112,10 @@ static void test_fr_FR() {
              cr::duration<short, std::ratio<1, 2>>{3600}}) == SV("1980-01-06 00:30:00,0"));
   assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::duration<int, std::ratio<1, 4>>>{
              cr::duration<int, std::ratio<1, 4>>{3600}}) == SV("1980-01-06 00:15:00,00"));
-  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::duration<long, std::ratio<1, 10>>>{
-             cr::duration<long, std::ratio<1, 10>>{36611}}) == SV("1980-01-06 01:01:01,1"));
+  assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::duration<long long, std::ratio<1, 10>>>{
+             cr::duration<long long, std::ratio<1, 10>>{36611}}) == SV("1980-01-06 01:01:01,1"));
   assert(stream_fr_FR_locale<CharT>(cr::gps_time<cr::duration<long long, std::ratio<1, 100>>>{
-             cr::duration<long long, std::ratio<1, 100>>{123'456'789'010}}) == SV("2019-02-18 23:31:12,10"));
+             cr::duration<long long, std::ratio<1, 100>>{123'456'789'010}}) == SV("2019-02-18 23:31:30,10"));
 }
 
 template <class CharT>
@@ -124,15 +124,15 @@ static void test_ja_JP() {
   namespace cr = std::chrono;
 
   assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::nanoseconds>{946'688'523'123'456'789ns}) ==
-         SV("2010-01-05 01:01:48.123456789"));
+         SV("2010-01-05 01:02:03.123456789"));
   assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::microseconds>{946'688'523'123'456us}) ==
-         SV("2010-01-05 01:01:48.123456"));
+         SV("2010-01-05 01:02:03.123456"));
 
   assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::milliseconds>{946'684'822'123ms}) ==
-         SV("2010-01-05 00:00:07.123"));
-  assert(stream_ja_JP_locale<CharT>(cr::gps_seconds{1'234'567'890s}) == SV("2019-02-18 23:31:12"));
-  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::minutes>{20'576'131min}) == SV("2019-02-18 23:30:42"));
-  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::hours>{342'935h}) == SV("2019-02-18 22:59:42"));
+         SV("2010-01-05 00:00:22.123"));
+  assert(stream_ja_JP_locale<CharT>(cr::gps_seconds{1'234'567'890s}) == SV("2019-02-18 23:31:30"));
+  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::minutes>{20'576'131min}) == SV("2019-02-18 23:31:00"));
+  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::hours>{342'935h}) == SV("2019-02-18 23:00:00"));
 
   assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::duration<signed char, std::ratio<2, 1>>>{
              cr::duration<signed char, std::ratio<2, 1>>{60}}) == SV("1980-01-06 00:02:00"));
@@ -140,10 +140,10 @@ static void test_ja_JP() {
              cr::duration<short, std::ratio<1, 2>>{3600}}) == SV("1980-01-06 00:30:00.0"));
   assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::duration<int, std::ratio<1, 4>>>{
              cr::duration<int, std::ratio<1, 4>>{3600}}) == SV("1980-01-06 00:15:00.00"));
-  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::duration<long, std::ratio<1, 10>>>{
-             cr::duration<long, std::ratio<1, 10>>{36611}}) == SV("1980-01-06 01:01:01.1"));
+  assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::duration<long long, std::ratio<1, 10>>>{
+             cr::duration<long long, std::ratio<1, 10>>{36611}}) == SV("1980-01-06 01:01:01.1"));
   assert(stream_ja_JP_locale<CharT>(cr::gps_time<cr::duration<long long, std::ratio<1, 100>>>{
-             cr::duration<long long, std::ratio<1, 100>>{123'456'789'010}}) == SV("2019-02-18 23:31:12.10"));
+             cr::duration<long long, std::ratio<1, 100>>{123'456'789'010}}) == SV("2019-02-18 23:31:30.10"));
 }
 
 template <class CharT>
diff --git a/libcxx/test/std/time/time.clock/time.clock.tai/tai_time.ostream.pass.cpp b/libcxx/test/std/time/time.clock/time.clock.tai/tai_time.ostream.pass.cpp
index e4f953118fd43..1ca9f18e99646 100644
--- a/libcxx/test/std/time/time.clock/time.clock.tai/tai_time.ostream.pass.cpp
+++ b/libcxx/test/std/time/time.clock/time.clock.tai/tai_time.ostream.pass.cpp
@@ -84,8 +84,8 @@ static void test_c() {
              cr::duration<short, std::ratio<1, 2>>{3600}}) == SV("1958-01-01 00:30:00.0"));
   assert(stream_c_locale<CharT>(cr::tai_time<cr::duration<int, std::ratio<1, 4>>>{
              cr::duration<int, std::ratio<1, 4>>{3600}}) == SV("1958-01-01 00:15:00.00"));
-  assert(stream_c_locale<CharT>(cr::tai_time<cr::duration<long, std::ratio<1, 10>>>{
-             cr::duration<long, std::ratio<1, 10>>{36611}}) == SV("1958-01-01 01:01:01.1"));
+  assert(stream_c_locale<CharT>(cr::tai_time<cr::duration<long long, std::ratio<1, 10>>>{
+             cr::duration<long long, std::ratio<1, 10>>{36611}}) == SV("1958-01-01 01:01:01.1"));
   assert(stream_c_locale<CharT>(cr::tai_time<cr::duration<long long, std::ratio<1, 100>>>{
              cr::duration<long long, std::ratio<1, 100>>{12'345'678'9010}}) == SV("1997-02-13 23:31:30.10"));
 }
@@ -112,8 +112,8 @@ static void test_fr_FR() {
              cr::duration<short, std::ratio<1, 2>>{3600}}) == SV("1958-01-01 00:30:00,0"));
   assert(stream_fr_FR_locale<CharT>(cr::tai_time<cr::duration<int, std::ratio<1, 4>>>{
              cr::duration<int, std::ratio<1, 4>>{3600}}) == SV("1958-01-01 00:15:00,00"));
-  assert(stream_fr_FR_locale<CharT>(cr::tai_time<cr::duration<long, std::ratio<1, 10>>>{
-             cr::duration<long, std::ratio<1, 10>>{36611}}) == SV("1958-01-01 01:01:01,1"));
+  assert(stream_fr_FR_locale<CharT>(cr::tai_time<cr::duration<long long, std::ratio<1, 10>>>{
+             cr::duration<long long, std::ratio<1, 10>>{36611}}) == SV("1958-01-01 01:01:01,1"));
   assert(stream_fr_FR_locale<CharT>(cr::tai_time<cr::duration<long long, std::ratio<1, 100>>>{
              cr::duration<long long, std::ratio<1, 100>>{12'345'678'9010}}) == SV("1997-02-13 23:31:30,10"));
 }
@@ -140,8 +140,8 @@ static void test_ja_JP() {
              cr::duration<short, std::ratio<1, 2>>{3600}}) == SV("1958-01-01 00:30:00.0"));
   assert(stream_ja_JP_locale<CharT>(cr::tai_time<cr::duration<int, std::ratio<1, 4>>>{
              cr::duration<int, std::ratio<1, 4>>{3600}}) == SV("1958-01-01 00:15:00.00"));
-  assert(stream_ja_JP_locale<CharT>(cr::tai_time<cr::duration<long, std::ratio<1, 10>>>{
-             cr::duration<long, std::ratio<1, 10>>{36611}}) == SV("1958-01-01 01:01:01.1"));
+  assert(stream_ja_JP_locale<CharT>(cr::tai_time<cr::duration<long long, std::ratio<1, 10>>>{
+             cr::duration<long long, std::ratio<1, 10>>{36611}}) == SV("1958-01-01 01:01:01.1"));
   assert(stream_ja_JP_locale<CharT>(cr::tai_time<cr::duration<long long, std::ratio<1, 100>>>{
              cr::duration<long long, std::ratio<1, 100>>{12'345'678'9010}}) == SV("1997-02-13 23:31:30.10"));
 }



More information about the libcxx-commits mailing list