[libcxx-commits] [libcxx] 093ddac - [libc++][chrono] Fixes leap seconds. (#90070)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jul 4 11:45:19 PDT 2024


Author: Mark de Wever
Date: 2024-07-04T20:45:15+02:00
New Revision: 093ddac68c6855b5c2d786a99475b793e35e2342

URL: https://github.com/llvm/llvm-project/commit/093ddac68c6855b5c2d786a99475b793e35e2342
DIFF: https://github.com/llvm/llvm-project/commit/093ddac68c6855b5c2d786a99475b793e35e2342.diff

LOG: [libc++][chrono] Fixes leap seconds. (#90070)

While implementing the UTC clock it turns out that the implementation of
the leap seconds was not correct, it should store the individual value,
not the sum.

It also looks like LWG3359 has not been fully implemented.

Implements parts of:
- LWG3359 <chrono> leap second support should allow for negative leap
seconds

Added: 
    

Modified: 
    libcxx/docs/Status/Cxx20Issues.csv
    libcxx/src/tzdb.cpp
    libcxx/test/libcxx/time/time.zone/time.zone.db/leap_seconds.pass.cpp
    libcxx/test/std/time/time.zone/time.zone.db/leap_seconds.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index e748ff6ad749b7..f617ffd13315d3 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -269,7 +269,7 @@
 "`3355 <https://wg21.link/LWG3355>`__","The memory algorithms should support move-only input iterators introduced by P1207","Prague","|Complete|","15.0","|ranges|"
 "`3356 <https://wg21.link/LWG3356>`__","``__cpp_lib_nothrow_convertible``\  should be ``__cpp_lib_is_nothrow_convertible``\ ","Prague","|Complete|","12.0"
 "`3358 <https://wg21.link/LWG3358>`__","|sect|\ [span.cons] is mistaken that ``to_address``\  can throw","Prague","|Complete|","17.0"
-"`3359 <https://wg21.link/LWG3359>`__","``<chrono>``\  leap second support should allow for negative leap seconds","Prague","|Complete|","19.0","|chrono|"
+"`3359 <https://wg21.link/LWG3359>`__","``<chrono>``\  leap second support should allow for negative leap seconds","Prague","|In Progress|","","|chrono|"
 "`3360 <https://wg21.link/LWG3360>`__","``three_way_comparable_with``\  is inconsistent with similar concepts","Prague","|Nothing To Do|","","|spaceship|"
 "`3362 <https://wg21.link/LWG3362>`__","Strike ``stop_source``\ 's ``operator!=``\ ","Prague","",""
 "`3363 <https://wg21.link/LWG3363>`__","``drop_while_view``\  should opt-out of ``sized_range``\ ","Prague","|Nothing To Do|","","|ranges|"

diff  --git a/libcxx/src/tzdb.cpp b/libcxx/src/tzdb.cpp
index 5951e59248e94f..2d07796c6951f6 100644
--- a/libcxx/src/tzdb.cpp
+++ b/libcxx/src/tzdb.cpp
@@ -626,29 +626,49 @@ static void __parse_leap_seconds(vector<leap_second>& __leap_seconds, istream&&
   // seconds since 1 January 1970.
   constexpr auto __offset = sys_days{1970y / January / 1} - sys_days{1900y / January / 1};
 
-  while (true) {
-    switch (__input.peek()) {
-    case istream::traits_type::eof():
-      return;
-
-    case ' ':
-    case '\t':
-    case '\n':
-      __input.get();
-      continue;
+  struct __entry {
+    sys_seconds __timestamp;
+    seconds __value;
+  };
+  vector<__entry> __entries;
+  [&] {
+    while (true) {
+      switch (__input.peek()) {
+      case istream::traits_type::eof():
+        return;
+
+      case ' ':
+      case '\t':
+      case '\n':
+        __input.get();
+        continue;
+
+      case '#':
+        chrono::__skip_line(__input);
+        continue;
+      }
 
-    case '#':
+      sys_seconds __date = sys_seconds{seconds{chrono::__parse_integral(__input, false)}} - __offset;
+      chrono::__skip_mandatory_whitespace(__input);
+      seconds __value{chrono::__parse_integral(__input, false)};
       chrono::__skip_line(__input);
-      continue;
-    }
 
-    sys_seconds __date = sys_seconds{seconds{chrono::__parse_integral(__input, false)}} - __offset;
-    chrono::__skip_mandatory_whitespace(__input);
-    seconds __value{chrono::__parse_integral(__input, false)};
-    chrono::__skip_line(__input);
-
-    __leap_seconds.emplace_back(std::__private_constructor_tag{}, __date, __value);
-  }
+      __entries.emplace_back(__date, __value);
+    }
+  }();
+  // The Standard requires the leap seconds to be sorted. The file
+  // leap-seconds.list usually provides them in sorted order, but that is not
+  // guaranteed so we ensure it here.
+  ranges::sort(__entries, {}, &__entry::__timestamp);
+
+  // The database should contain the number of seconds inserted by a leap
+  // second (1 or -1). So the 
diff erence between the two elements is stored.
+  // std::ranges::views::adjacent has not been implemented yet.
+  (void)ranges::adjacent_find(__entries, [&](const __entry& __first, const __entry& __second) {
+    __leap_seconds.emplace_back(
+        std::__private_constructor_tag{}, __second.__timestamp, __second.__value - __first.__value);
+    return false;
+  });
 }
 
 void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) {
@@ -667,10 +687,6 @@ void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) {
   // The latter is much easier to parse, it seems Howard shares that
   // opinion.
   chrono::__parse_leap_seconds(__tzdb.leap_seconds, ifstream{__root / "leap-seconds.list"});
-  // The Standard requires the leap seconds to be sorted. The file
-  // leap-seconds.list usually provides them in sorted order, but that is not
-  // guaranteed so we ensure it here.
-  std::ranges::sort(__tzdb.leap_seconds);
 }
 
 #ifdef _WIN32

diff  --git a/libcxx/test/libcxx/time/time.zone/time.zone.db/leap_seconds.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.db/leap_seconds.pass.cpp
index 25a0f00003da2f..d7ae21926b4b26 100644
--- a/libcxx/test/libcxx/time/time.zone/time.zone.db/leap_seconds.pass.cpp
+++ b/libcxx/test/libcxx/time/time.zone/time.zone.db/leap_seconds.pass.cpp
@@ -83,32 +83,39 @@ static void test_leap_seconds() {
 2303683200  12  # 1 Jan 1973
 2287785600  11  # 1 Jul 1972
 2272060800  10  # 1 Jan 1972
-86400        1  # 2 Jan 1900 Dummy entry to test before 1970
+86400        9  # 2 Jan 1900 Dummy entry to test before 1970
+1            8  # 2 Jan 1900 Dummy entry to test before 1970
+
+# Fictional negative leap second
+2303769600  11  # 2 Jan 1973
 
 # largest accepted value by the parser
-5764607523034234879 2
+5764607523034234879 12
 )");
 
-  assert(result.leap_seconds.size() == 5);
+  assert(result.leap_seconds.size() == 6);
 
   assert(result.leap_seconds[0].date() == sys_seconds{sys_days{1900y / January / 2}});
   assert(result.leap_seconds[0].value() == 1s);
 
   assert(result.leap_seconds[1].date() == sys_seconds{sys_days{1972y / January / 1}});
-  assert(result.leap_seconds[1].value() == 10s);
+  assert(result.leap_seconds[1].value() == 1s);
 
   assert(result.leap_seconds[2].date() == sys_seconds{sys_days{1972y / July / 1}});
-  assert(result.leap_seconds[2].value() == 11s);
+  assert(result.leap_seconds[2].value() == 1s);
 
   assert(result.leap_seconds[3].date() == sys_seconds{sys_days{1973y / January / 1}});
-  assert(result.leap_seconds[3].value() == 12s);
+  assert(result.leap_seconds[3].value() == 1s);
+
+  assert(result.leap_seconds[4].date() == sys_seconds{sys_days{1973y / January / 2}});
+  assert(result.leap_seconds[4].value() == -1s);
 
-  assert(result.leap_seconds[4].date() ==
+  assert(result.leap_seconds[5].date() ==
          sys_seconds{5764607523034234879s
                      // The database uses 1900-01-01 as epoch.
                      - std::chrono::duration_cast<std::chrono::seconds>(
                            sys_days{1970y / January / 1} - sys_days{1900y / January / 1})});
-  assert(result.leap_seconds[4].value() == 2s);
+  assert(result.leap_seconds[5].value() == 1s);
 }
 
 int main(int, const char**) {

diff  --git a/libcxx/test/std/time/time.zone/time.zone.db/leap_seconds.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/leap_seconds.pass.cpp
index f873ad3167819e..39023c31aecbbf 100644
--- a/libcxx/test/std/time/time.zone/time.zone.db/leap_seconds.pass.cpp
+++ b/libcxx/test/std/time/time.zone/time.zone.db/leap_seconds.pass.cpp
@@ -33,34 +33,33 @@ using namespace std::literals::chrono_literals;
 // At the moment of writing that list is the actual list in the IANA database.
 // If in the future more leap seconds can be added.
 static const std::array leap_seconds = {
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1972y / std::chrono::January / 1}}, 10s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1972y / std::chrono::July / 1}}, 11s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1973y / std::chrono::January / 1}}, 12s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1974y / std::chrono::January / 1}}, 13s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1975y / std::chrono::January / 1}}, 14s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1976y / std::chrono::January / 1}}, 15s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1977y / std::chrono::January / 1}}, 16s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1978y / std::chrono::January / 1}}, 17s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1979y / std::chrono::January / 1}}, 18s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1980y / std::chrono::January / 1}}, 19s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1981y / std::chrono::July / 1}}, 20s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1982y / std::chrono::July / 1}}, 21s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1983y / std::chrono::July / 1}}, 22s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1985y / std::chrono::July / 1}}, 23s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1988y / std::chrono::January / 1}}, 24s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1990y / std::chrono::January / 1}}, 25s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1991y / std::chrono::January / 1}}, 26s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1992y / std::chrono::July / 1}}, 27s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1993y / std::chrono::July / 1}}, 28s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1994y / std::chrono::July / 1}}, 29s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1996y / std::chrono::January / 1}}, 30s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1997y / std::chrono::July / 1}}, 31s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1999y / std::chrono::January / 1}}, 32s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2006y / std::chrono::January / 1}}, 33s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2009y / std::chrono::January / 1}}, 34s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2012y / std::chrono::July / 1}}, 35s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2015y / std::chrono::July / 1}}, 36s),
-    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2017y / std::chrono::January / 1}}, 37s)};
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1972y / std::chrono::July / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1973y / std::chrono::January / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1974y / std::chrono::January / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1975y / std::chrono::January / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1976y / std::chrono::January / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1977y / std::chrono::January / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1978y / std::chrono::January / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1979y / std::chrono::January / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1980y / std::chrono::January / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1981y / std::chrono::July / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1982y / std::chrono::July / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1983y / std::chrono::July / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1985y / std::chrono::July / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1988y / std::chrono::January / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1990y / std::chrono::January / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1991y / std::chrono::January / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1992y / std::chrono::July / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1993y / std::chrono::July / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1994y / std::chrono::July / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1996y / std::chrono::January / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1997y / std::chrono::July / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{1999y / std::chrono::January / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2006y / std::chrono::January / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2009y / std::chrono::January / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2012y / std::chrono::July / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2015y / std::chrono::July / 1}}, 1s),
+    std::make_pair(std::chrono::sys_seconds{std::chrono::sys_days{2017y / std::chrono::January / 1}}, 1s)};
 
 int main(int, const char**) {
   const std::chrono::tzdb& tzdb = std::chrono::get_tzdb();


        


More information about the libcxx-commits mailing list