https://github.com/mordante updated https://github.com/llvm/llvm-project/pull/82113
>From 3710cf0f9e8fa6065b25123e9b2158079e10805c Mon Sep 17 00:00:00 2001 From: Mark de Wever <ko...@xs4all.nl> Date: Fri, 23 Sep 2022 18:33:20 +0200 Subject: [PATCH] [libc++][chrono] Loads leap-seconds.list in tzdb. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implements the loading of the leap-seconds.list file and store its contents in the tzdb struct. This adds the required `leap_seconds` member. The class leap_seconds is fully implemented including its non-member functions. Implements parts of: - P0355 Extending <chrono> to Calendars and Time Zones - P1614 The Mothership has Landed Implements: - P1981 Rename leap to leap_second - LWG3359 <chrono> leap second support should allow for negative leap seconds - LWG3383 ยง[time.zone.leap.nonmembers] sys_seconds should be replaced with seconds --- libcxx/docs/Status/Cxx20Issues.csv | 4 +- libcxx/docs/Status/Cxx20Papers.csv | 2 +- libcxx/docs/Status/SpaceshipProjects.csv | 2 +- libcxx/include/CMakeLists.txt | 1 + libcxx/include/__chrono/leap_second.h | 112 ++++++++++++++++++ libcxx/include/__chrono/tzdb.h | 3 + libcxx/include/chrono | 39 ++++++ libcxx/include/libcxx.imp | 1 + libcxx/include/module.modulemap.in | 1 + libcxx/modules/std/chrono.inc | 28 ++--- libcxx/src/CMakeLists.txt | 1 + libcxx/src/include/tzdb/leap_second_private.h | 27 +++++ libcxx/src/tzdb.cpp | 41 +++++++ ...rono.nodiscard_extensions.compile.pass.cpp | 6 + .../chrono.nodiscard_extensions.verify.cpp | 6 + .../time.zone.db/leap_seconds.pass.cpp | 102 ++++++++++++++++ .../time.zone.db.access/get_tzdb.pass.cpp | 3 + .../time.zone.leap/assign.copy.pass.cpp | 56 +++++++++ .../time.zone.leap/cons.copy.pass.cpp | 51 ++++++++ .../time.zone.leap/members/date.pass.cpp | 48 ++++++++ .../time.zone.leap/members/value.pass.cpp | 48 ++++++++ .../nonmembers/comparison.pass.cpp | 77 ++++++++++++ libcxx/test/support/test_chrono_leap_second.h | 53 +++++++++ 23 files changed, 693 insertions(+), 19 deletions(-) create mode 100644 libcxx/include/__chrono/leap_second.h create mode 100644 libcxx/src/include/tzdb/leap_second_private.h create mode 100644 libcxx/test/libcxx/time/time.zone/time.zone.db/leap_seconds.pass.cpp create mode 100644 libcxx/test/std/time/time.zone/time.zone.leap/assign.copy.pass.cpp create mode 100644 libcxx/test/std/time/time.zone/time.zone.leap/cons.copy.pass.cpp create mode 100644 libcxx/test/std/time/time.zone/time.zone.leap/members/date.pass.cpp create mode 100644 libcxx/test/std/time/time.zone/time.zone.leap/members/value.pass.cpp create mode 100644 libcxx/test/std/time/time.zone/time.zone.leap/nonmembers/comparison.pass.cpp create mode 100644 libcxx/test/support/test_chrono_leap_second.h diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv index f0e9c4090f9cf6..db57b15256a62f 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","","","|chrono|" +"`3359 <https://wg21.link/LWG3359>`__","``<chrono>``\ leap second support should allow for negative leap seconds","Prague","|Complete|","19.0","|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|" @@ -286,7 +286,7 @@ "`3380 <https://wg21.link/LWG3380>`__","``common_type``\ and comparison categories","Prague","|Complete|","15.0","|spaceship|" "`3381 <https://wg21.link/LWG3381>`__","``begin``\ and ``data``\ must agree for ``contiguous_range``\ ","Prague","|Nothing To Do|","","|ranges|" "`3382 <https://wg21.link/LWG3382>`__","NTTP for ``pair``\ and ``array``\ ","Prague","","" -"`3383 <https://wg21.link/LWG3383>`__","|sect|\ [time.zone.leap.nonmembers] ``sys_seconds``\ should be replaced with ``seconds``\ ","Prague","","","|chrono|" +"`3383 <https://wg21.link/LWG3383>`__","|sect|\ [time.zone.leap.nonmembers] ``sys_seconds``\ should be replaced with ``seconds``\ ","Prague","|Complete|","19.0","|chrono|" "`3384 <https://wg21.link/LWG3384>`__","``transform_view::*sentinel*``\ has an incorrect ``operator-``\ ","Prague","|Complete|","15.0","|ranges|" "`3385 <https://wg21.link/LWG3385>`__","``common_iterator``\ is not sufficiently constrained for non-copyable iterators","Prague","|Complete|","15.0","|ranges|" "`3387 <https://wg21.link/LWG3387>`__","|sect|\ [range.reverse.view] ``reverse_view<V>``\ unintentionally requires ``range<const V>``\ ","Prague","|Complete|","15.0","|ranges|" diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv index db6491419a5cc3..77078b11a72780 100644 --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -179,7 +179,7 @@ "`P1970R2 <https://wg21.link/P1970R2>`__","LWG","Consistency for size() functions: Add ranges::ssize","Prague","|Complete|","15.0","|ranges|" "`P1973R1 <https://wg21.link/P1973R1>`__","LWG","Rename ""_default_init"" Functions, Rev1","Prague","|Complete|","16.0" "`P1976R2 <https://wg21.link/P1976R2>`__","LWG","Fixed-size span construction from dynamic range","Prague","|Complete|","11.0","|ranges|" -"`P1981R0 <https://wg21.link/P1981R0>`__","LWG","Rename leap to leap_second","Prague","* *","" +"`P1981R0 <https://wg21.link/P1981R0>`__","LWG","Rename leap to leap_second","Prague","|Complete|","19.0","|chrono|" "`P1982R0 <https://wg21.link/P1982R0>`__","LWG","Rename link to time_zone_link","Prague","|Complete|","19.0","|chrono|" "`P1983R0 <https://wg21.link/P1983R0>`__","LWG","Wording for GB301, US296, US292, US291, and US283","Prague","|Complete|","15.0","|ranges|" "`P1994R1 <https://wg21.link/P1994R1>`__","LWG","elements_view needs its own sentinel","Prague","|Complete|","16.0","|ranges|" diff --git a/libcxx/docs/Status/SpaceshipProjects.csv b/libcxx/docs/Status/SpaceshipProjects.csv index c8221078e9a8df..3d14f487d9a910 100644 --- a/libcxx/docs/Status/SpaceshipProjects.csv +++ b/libcxx/docs/Status/SpaceshipProjects.csv @@ -173,7 +173,7 @@ Section,Description,Dependencies,Assignee,Complete | `year_month_weekday_last <https://reviews.llvm.org/D152699>`_",None,Hristo Hristov,|Complete| `[time.zone.nonmembers] <https://wg21.link/time.zone.nonmembers>`_,"`chrono::time_zone`",A ``<chrono>`` implementation,Mark de Wever,|Complete| `[time.zone.zonedtime.nonmembers] <https://wg21.link/time.zone.zonedtime.nonmembers>`_,"`chrono::zoned_time`",A ``<chrono>`` implementation,Mark de Wever,|In Progress| -`[time.zone.leap.nonmembers] <https://wg21.link/time.zone.leap.nonmembers>`_,"`chrono::time_leap_seconds`",A ``<chrono>`` implementation,Mark de Wever,|In Progress| +`[time.zone.leap.nonmembers] <https://wg21.link/time.zone.leap.nonmembers>`_,"`chrono::time_leap_seconds`",A ``<chrono>`` implementation,Mark de Wever,|Complete| `[time.zone.link.nonmembers] <https://wg21.link/time.zone.link.nonmembers>`_,"`chrono::time_zone_link`",A ``<chrono>`` implementation,Mark de Wever,|Complete| - `5.13 Clause 28: Localization library <https://wg21.link/p1614r2#clause-28-localization-library>`_,,,, "| `[locale] <https://wg21.link/locale>`_ diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index fa3b464e56c4d0..e550e6bfcdefd3 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -281,6 +281,7 @@ set(files __chrono/formatter.h __chrono/hh_mm_ss.h __chrono/high_resolution_clock.h + __chrono/leap_second.h __chrono/literals.h __chrono/month.h __chrono/month_weekday.h diff --git a/libcxx/include/__chrono/leap_second.h b/libcxx/include/__chrono/leap_second.h new file mode 100644 index 00000000000000..7c1f5ff72498e4 --- /dev/null +++ b/libcxx/include/__chrono/leap_second.h @@ -0,0 +1,112 @@ +// -*- 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 +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#ifndef _LIBCPP___CHRONO_LEAP_SECOND_H +#define _LIBCPP___CHRONO_LEAP_SECOND_H + +#include <version> +// Enable the contents of the header only when libc++ was built with experimental features enabled. +#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB) + +# include <__chrono/duration.h> +# include <__chrono/system_clock.h> +# include <__chrono/time_point.h> +# include <__compare/ordering.h> +# include <__compare/three_way_comparable.h> +# include <__config> + +# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +# endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +# if _LIBCPP_STD_VER >= 20 + +namespace chrono { + +class leap_second { +public: + struct __constructor_tag; + [[nodiscard]] + _LIBCPP_HIDE_FROM_ABI explicit constexpr leap_second(__constructor_tag&&, sys_seconds __date, seconds __value) + : __date_(__date), __value_(__value) {} + + _LIBCPP_HIDE_FROM_ABI leap_second(const leap_second&) = default; + _LIBCPP_HIDE_FROM_ABI leap_second& operator=(const leap_second&) = default; + + _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr sys_seconds date() const noexcept { return __date_; } + + _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr seconds value() const noexcept { return __value_; } + +private: + sys_seconds __date_; + seconds __value_; +}; + +_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const leap_second& __x, const leap_second& __y) { + return __x.date() == __y.date(); +} +_LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const leap_second& __x, const leap_second& __y) { + return __x.date() <=> __y.date(); +} + +template <class _Duration> +_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const leap_second& __x, const sys_time<_Duration>& __y) { + return __x.date() == __y; +} +template <class _Duration> +_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const leap_second& __x, const sys_time<_Duration>& __y) { + return __x.date() < __y; +} +template <class _Duration> +_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const sys_time<_Duration>& __x, const leap_second& __y) { + return __x < __y.date(); +} +template <class _Duration> +_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const leap_second& __x, const sys_time<_Duration>& __y) { + return __y < __x; +} +template <class _Duration> +_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const sys_time<_Duration>& __x, const leap_second& __y) { + return __y < __x; +} +template <class _Duration> +_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const leap_second& __x, const sys_time<_Duration>& __y) { + return !(__y < __x); +} +template <class _Duration> +_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const sys_time<_Duration>& __x, const leap_second& __y) { + return !(__y < __x); +} +template <class _Duration> +_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const leap_second& __x, const sys_time<_Duration>& __y) { + return !(__x < __y); +} +template <class _Duration> +_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const sys_time<_Duration>& __x, const leap_second& __y) { + return !(__x < __y); +} +template <class _Duration> + requires three_way_comparable_with<sys_seconds, sys_time<_Duration>> +_LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(const leap_second& __x, const sys_time<_Duration>& __y) { + return __x.date() <=> __y; +} + +} // namespace chrono + +# endif //_LIBCPP_STD_VER >= 20 + +_LIBCPP_END_NAMESPACE_STD + +#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB) + +#endif // _LIBCPP___CHRONO_LEAP_SECOND_H diff --git a/libcxx/include/__chrono/tzdb.h b/libcxx/include/__chrono/tzdb.h index 582172e5df9ddb..45c20f279f9c96 100644 --- a/libcxx/include/__chrono/tzdb.h +++ b/libcxx/include/__chrono/tzdb.h @@ -16,6 +16,7 @@ // Enable the contents of the header only when libc++ was built with experimental features enabled. #if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB) +# include <__chrono/leap_second.h> # include <__chrono/time_zone.h> # include <__chrono/time_zone_link.h> # include <__config> @@ -40,6 +41,8 @@ struct tzdb { string version; vector<time_zone> zones; vector<time_zone_link> links; + + vector<leap_second> leap_seconds; }; } // namespace chrono diff --git a/libcxx/include/chrono b/libcxx/include/chrono index fe73f7c772b996..01ba15d97f3d20 100644 --- a/libcxx/include/chrono +++ b/libcxx/include/chrono @@ -688,6 +688,7 @@ struct tzdb { string version; vector<time_zone> zones; vector<time_zone_link> links; + vector<leap_second> leap_seconds; }; class tzdb_list { // C++20 @@ -731,6 +732,43 @@ class time_zone { bool operator==(const time_zone& x, const time_zone& y) noexcept; // C++20 strong_ordering operator<=>(const time_zone& x, const time_zone& y) noexcept; // C++20 +// [time.zone.leap], leap second support +class leap_second { // C++20 +public: + leap_second(const leap_second&) = default; + leap_second& operator=(const leap_second&) = default; + + // unspecified additional constructors + + constexpr sys_seconds date() const noexcept; + constexpr seconds value() const noexcept; +}; + +constexpr bool operator==(const leap_second& x, const leap_second& y); // C++20 +constexpr strong_ordering operator<=>(const leap_second& x, const leap_second& y); + +template<class Duration> // C++20 + constexpr bool operator==(const leap_second& x, const sys_time<Duration>& y); +template<class Duration> // C++20 + constexpr bool operator< (const leap_second& x, const sys_time<Duration>& y); +template<class Duration> // C++20 + constexpr bool operator< (const sys_time<Duration>& x, const leap_second& y); +template<class Duration> // C++20 + constexpr bool operator> (const leap_second& x, const sys_time<Duration>& y); +template<class Duration> // C++20 + constexpr bool operator> (const sys_time<Duration>& x, const leap_second& y); +template<class Duration> // C++20 + constexpr bool operator<=(const leap_second& x, const sys_time<Duration>& y); +template<class Duration> // C++20 + constexpr bool operator<=(const sys_time<Duration>& x, const leap_second& y); +template<class Duration> // C++20 + constexpr bool operator>=(const leap_second& x, const sys_time<Duration>& y); +template<class Duration> // C++20 + constexpr bool operator>=(const sys_time<Duration>& x, const leap_second& y); +template<class Duration> // C++20 + requires three_way_comparable_with<sys_seconds, sys_time<Duration>> + constexpr auto operator<=>(const leap_second& x, const sys_time<Duration>& y); + // [time.zone.link], class time_zone_link class time_zone_link { // C++20 public: @@ -863,6 +901,7 @@ constexpr chrono::year operator ""y(unsigned lo #if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \ !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <__chrono/leap_second.h> # include <__chrono/time_zone.h> # include <__chrono/time_zone_link.h> # include <__chrono/tzdb.h> diff --git a/libcxx/include/libcxx.imp b/libcxx/include/libcxx.imp index fbe09fa5e54ab1..aeeb970433fc4a 100644 --- a/libcxx/include/libcxx.imp +++ b/libcxx/include/libcxx.imp @@ -278,6 +278,7 @@ { include: [ "<__chrono/formatter.h>", "private", "<chrono>", "public" ] }, { include: [ "<__chrono/hh_mm_ss.h>", "private", "<chrono>", "public" ] }, { include: [ "<__chrono/high_resolution_clock.h>", "private", "<chrono>", "public" ] }, + { include: [ "<__chrono/leap_second.h>", "private", "<chrono>", "public" ] }, { include: [ "<__chrono/literals.h>", "private", "<chrono>", "public" ] }, { include: [ "<__chrono/month.h>", "private", "<chrono>", "public" ] }, { include: [ "<__chrono/month_weekday.h>", "private", "<chrono>", "public" ] }, diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in index e72136a58c0b1b..4f106d0a6c7e4a 100644 --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1141,6 +1141,7 @@ module std_private_chrono_high_resolution_clock [system] { export std_private_chrono_steady_clock export std_private_chrono_system_clock } +module std_private_chrono_leap_second [system] { header "__chrono/leap_second.h" } module std_private_chrono_literals [system] { header "__chrono/literals.h" } module std_private_chrono_month [system] { header "__chrono/month.h" } module std_private_chrono_month_weekday [system] { header "__chrono/month_weekday.h" } diff --git a/libcxx/modules/std/chrono.inc b/libcxx/modules/std/chrono.inc index 109023a3abc25f..2c0bd3f98a67d7 100644 --- a/libcxx/modules/std/chrono.inc +++ b/libcxx/modules/std/chrono.inc @@ -208,10 +208,7 @@ export namespace std { using std::chrono::reload_tzdb; using std::chrono::remote_version; -# endif // !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && - // !defined(_LIBCPP_HAS_NO_LOCALIZATION) - -# if 0 +# if 0 // [time.zone.exception], exception classes using std::chrono::ambiguous_local_time; using std::chrono::nonexistent_local_time; @@ -221,11 +218,11 @@ export namespace std { // [time.zone.timezone], class time_zone using std::chrono::choose; -# endif -# ifdef _LIBCPP_ENABLE_EXPERIMENTAL +# endif // if 0 + using std::chrono::time_zone; -# endif -# if 0 + +# if 0 // [time.zone.zonedtraits], class template zoned_traits using std::chrono::zoned_traits; @@ -234,22 +231,23 @@ export namespace std { using std::chrono::zoned_time; using std::chrono::zoned_seconds; +# endif // if 0 // [time.zone.leap], leap second support using std::chrono::leap_second; -# endif -# ifdef _LIBCPP_ENABLE_EXPERIMENTAL // [time.zone.link], class time_zone_link using std::chrono::time_zone_link; -# endif -# if 0 +# if 0 // [time.format], formatting using std::chrono::local_time_format; -# endif -#endif // _LIBCPP_ENABLE_EXPERIMENTAL - } // namespace chrono +# endif +# endif // _LIBCPP_ENABLE_EXPERIMENTAL +#endif // !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && + // !defined(_LIBCPP_HAS_NO_LOCALIZATION) + + } // namespace chrono #ifndef _LIBCPP_HAS_NO_LOCALIZATION using std::formatter; diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt index cc6954a7bac37e..428770cba7138f 100644 --- a/libcxx/src/CMakeLists.txt +++ b/libcxx/src/CMakeLists.txt @@ -336,6 +336,7 @@ endif() if (LIBCXX_ENABLE_LOCALIZATION AND LIBCXX_ENABLE_FILESYSTEM AND LIBCXX_ENABLE_TIME_ZONE_DATABASE) list(APPEND LIBCXX_EXPERIMENTAL_SOURCES + include/tzdb/leap_second_private.h include/tzdb/time_zone_link_private.h include/tzdb/time_zone_private.h include/tzdb/types_private.h diff --git a/libcxx/src/include/tzdb/leap_second_private.h b/libcxx/src/include/tzdb/leap_second_private.h new file mode 100644 index 00000000000000..7a811ab1975942 --- /dev/null +++ b/libcxx/src/include/tzdb/leap_second_private.h @@ -0,0 +1,27 @@ +// -*- 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 +// +//===----------------------------------------------------------------------===// + +// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html + +#ifndef _LIBCPP_SRC_INCLUDE_TZDB_LEAP_SECOND_PRIVATE_H +#define _LIBCPP_SRC_INCLUDE_TZDB_LEAP_SECOND_PRIVATE_H + +#include <chrono> + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace chrono { + +struct leap_second::__constructor_tag {}; + +} // namespace chrono + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_SRC_INCLUDE_TZDB_LEAP_SECOND_PRIVATE_H diff --git a/libcxx/src/tzdb.cpp b/libcxx/src/tzdb.cpp index 2bb801e48694ee..f1b72d57d7d5c7 100644 --- a/libcxx/src/tzdb.cpp +++ b/libcxx/src/tzdb.cpp @@ -15,6 +15,7 @@ #include <stdexcept> #include <string> +#include "include/tzdb/leap_second_private.h" #include "include/tzdb/time_zone_link_private.h" #include "include/tzdb/time_zone_private.h" #include "include/tzdb/types_private.h" @@ -603,6 +604,36 @@ static void __parse_tzdata(tzdb& __db, __tz::__rules_storage_type& __rules, istr } } +static void __parse_leap_seconds(vector<leap_second>& __leap_seconds, istream&& __input) { + // The file stores dates since 1 January 1900, 00:00:00, we want + // 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; + + case '#': + 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(leap_second::__constructor_tag{}, __date, __value); + } +} + void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) { filesystem::path __root = chrono::__libcpp_tzdb_directory(); ifstream __tzdata{__root / "tzdata.zi"}; @@ -612,6 +643,16 @@ void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) { std::ranges::sort(__tzdb.zones); std::ranges::sort(__tzdb.links); std::ranges::sort(__rules, {}, [](const auto& p) { return p.first; }); + + // There are two files with the leap second information + // - leapseconds as specified by zic + // - leap-seconds.list the source data + // 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"}); + // Note the input is sorted, but that does not seem to be are + // requirement, it is a requirement in the Standard. + std::ranges::sort(__tzdb.leap_seconds); } //===----------------------------------------------------------------------===// diff --git a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp index 8019824046c47f..c868832ea74ada 100644 --- a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp +++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp @@ -26,6 +26,7 @@ // These types have "private" constructors. extern std::chrono::time_zone tz; extern std::chrono::time_zone_link link; +extern std::chrono::leap_second leap; void test() { std::chrono::tzdb_list& list = std::chrono::get_tzdb_list(); @@ -51,4 +52,9 @@ void test() { operator==(link, link); operator<=>(link, link); } + + { + leap.date(); + leap.value(); + } } diff --git a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp index e9b27559497b7e..4d26b46a89c91b 100644 --- a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp +++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp @@ -23,6 +23,7 @@ // These types have "private" constructors. extern std::chrono::time_zone tz; extern std::chrono::time_zone_link link; +extern std::chrono::leap_second leap; void test() { std::chrono::tzdb_list& list = std::chrono::get_tzdb_list(); @@ -51,4 +52,9 @@ void test() { // expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}} operator<=>(link, link); } + + { + leap.date(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + leap.value(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + } } 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 new file mode 100644 index 00000000000000..d7303890132824 --- /dev/null +++ b/libcxx/test/libcxx/time/time.zone/time.zone.db/leap_seconds.pass.cpp @@ -0,0 +1,102 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-filesystem, no-localization, no-tzdb + +// XFAIL: libcpp-has-no-incomplete-tzdb +// XFAIL: availability-tzdb-missing + +// <chrono> + +// Tests the IANA database leap seconds parsing and operations. +// This is not part of the public tzdb interface. + +#include <cassert> +#include <chrono> +#include <fstream> +#include <string> +#include <string_view> + +#include "assert_macros.h" +#include "concat_macros.h" +#include "filesystem_test_helper.h" +#include "test_tzdb.h" + +scoped_test_env env; +[[maybe_unused]] const std::filesystem::path dir = env.create_dir("zoneinfo"); +const std::filesystem::path tzdata = env.create_file("zoneinfo/tzdata.zi"); +const std::filesystem::path leap_seconds = env.create_file("zoneinfo/leap-seconds.list"); + +std::string_view std::chrono::__libcpp_tzdb_directory() { + static std::string result = dir.string(); + return result; +} + +void write(std::string_view input) { + static int version = 0; + + std::ofstream f{tzdata}; + f << "# version " << version++ << '\n'; + std::ofstream{leap_seconds}.write(input.data(), input.size()); +} + +static const std::chrono::tzdb& parse(std::string_view input) { + write(input); + return std::chrono::reload_tzdb(); +} + +static void test_exception(std::string_view input, [[maybe_unused]] std::string_view what) { + write(input); + + TEST_VALIDATE_EXCEPTION( + std::runtime_error, + [&]([[maybe_unused]] const std::runtime_error& e) { + TEST_LIBCPP_REQUIRE( + e.what() == what, + TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n')); + }, + TEST_IGNORE_NODISCARD std::chrono::reload_tzdb()); +} + +static void test_invalid() { + test_exception("0", "corrupt tzdb: expected a non-zero digit"); + + test_exception("1", "corrupt tzdb: expected whitespace"); + + test_exception("1 ", "corrupt tzdb: expected a non-zero digit"); +} + +static void test_leap_seconds() { + using namespace std::chrono; + + const tzdb& result = parse( + R"( +2272060800 10 # 1 Jan 1972 +2287785600 11 # 1 Jul 1972 +2303683200 12 # 1 Jan 1973 +)"); + + assert(result.leap_seconds.size() == 3); + + assert(result.leap_seconds[0].date() == sys_seconds{sys_days{1972y / January / 1}}); + assert(result.leap_seconds[0].value() == 10s); + + assert(result.leap_seconds[1].date() == sys_seconds{sys_days{1972y / July / 1}}); + assert(result.leap_seconds[1].value() == 11s); + + assert(result.leap_seconds[2].date() == sys_seconds{sys_days{1973y / January / 1}}); + assert(result.leap_seconds[2].value() == 12s); +} + +int main(int, const char**) { + test_invalid(); + test_leap_seconds(); + + return 0; +} diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb.pass.cpp index b6204c615d965e..470a722d0b69c6 100644 --- a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb.pass.cpp +++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb.pass.cpp @@ -35,5 +35,8 @@ int main(int, const char**) { assert(std::ranges::is_sorted(db.links)); assert(std::ranges::adjacent_find(db.links) == db.links.end()); // is unique? + assert(!db.leap_seconds.empty()); + assert(std::ranges::is_sorted(db.leap_seconds)); + return 0; } diff --git a/libcxx/test/std/time/time.zone/time.zone.leap/assign.copy.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.leap/assign.copy.pass.cpp new file mode 100644 index 00000000000000..de7e9235ff02e5 --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.leap/assign.copy.pass.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-filesystem, no-localization, no-tzdb + +// XFAIL: libcpp-has-no-incomplete-tzdb +// XFAIL: availability-tzdb-missing + +// <chrono> + +// class leap_second +// { +// leap_second& operator=(const leap_second&) = default; +// +// ... +// }; + +#include <chrono> +#include <type_traits> +#include <cassert> + +// Add the include path required by test_chrono_leap_second.h when using libc++. +// ADDITIONAL_COMPILE_FLAGS(stdlib=libc++): -I %S/../../../../../src/include +#include "test_chrono_leap_second.h" + +constexpr bool test() { + std::chrono::leap_second a = + test_leap_second_create(std::chrono::sys_seconds{std::chrono::seconds{0}}, std::chrono::seconds{1}); + std::chrono::leap_second b = + test_leap_second_create(std::chrono::sys_seconds{std::chrono::seconds{10}}, std::chrono::seconds{15}); + + // operator== only compares the date member. + assert(a.date() != b.date()); + assert(a.value() != b.value()); + + b = a; + assert(a.date() == b.date()); + assert(a.value() == b.value()); + + return true; +} + +int main(int, const char**) { + static_assert(std::is_copy_assignable_v<std::chrono::leap_second>); + + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/time/time.zone/time.zone.leap/cons.copy.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.leap/cons.copy.pass.cpp new file mode 100644 index 00000000000000..1eab5d613734a9 --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.leap/cons.copy.pass.cpp @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-filesystem, no-localization, no-tzdb + +// XFAIL: libcpp-has-no-incomplete-tzdb +// XFAIL: availability-tzdb-missing + +// <chrono> + +// class leap_second +// { +// leap_second(const leap_second&) = default; +// +// ... +// }; + +#include <chrono> +#include <concepts> +#include <cassert> + +// Add the include path required by test_chrono_leap_second.h when using libc++. +// ADDITIONAL_COMPILE_FLAGS(stdlib=libc++): -I %S/../../../../../src/include +#include "test_chrono_leap_second.h" + +constexpr bool test() { + std::chrono::leap_second a = + test_leap_second_create(std::chrono::sys_seconds{std::chrono::seconds{0}}, std::chrono::seconds{1}); + std::chrono::leap_second b(a); + + // operator== only compares the date member. + assert(a.date() == b.date()); + assert(a.value() == b.value()); + + return true; +} + +int main(int, const char**) { + static_assert(std::copy_constructible<std::chrono::leap_second>); + + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/time/time.zone/time.zone.leap/members/date.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.leap/members/date.pass.cpp new file mode 100644 index 00000000000000..e119dbc9a67ba4 --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.leap/members/date.pass.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-filesystem, no-localization, no-tzdb + +// XFAIL: libcpp-has-no-incomplete-tzdb +// XFAIL: availability-tzdb-missing + +// <chrono> + +// class leap_second; + +// constexpr sys_seconds date() const noexcept; + +#include <cassert> +#include <chrono> + +#include "test_macros.h" + +// Add the include path required by test_chrono_leap_second.h when using libc++. +// ADDITIONAL_COMPILE_FLAGS(stdlib=libc++): -I %S/../../../../../../src/include +#include "test_chrono_leap_second.h" + +constexpr bool test(const std::chrono::leap_second leap_second, std::chrono::sys_seconds expected) { + std::same_as<std::chrono::sys_seconds> auto date = leap_second.date(); + assert(date == expected); + static_assert(noexcept(leap_second.date())); + + return true; +} + +int main(int, const char**) { + const std::chrono::tzdb& tzdb = std::chrono::get_tzdb(); + assert(!tzdb.leap_seconds.empty()); + test(tzdb.leap_seconds[0], tzdb.leap_seconds[0].date()); + + static_assert( + test(test_leap_second_create(std::chrono::sys_seconds{std::chrono::seconds{0}}, std::chrono::seconds{1}), + std::chrono::sys_seconds{std::chrono::seconds{0}})); + + return 0; +} diff --git a/libcxx/test/std/time/time.zone/time.zone.leap/members/value.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.leap/members/value.pass.cpp new file mode 100644 index 00000000000000..27f78eedb9cc10 --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.leap/members/value.pass.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-filesystem, no-localization, no-tzdb + +// XFAIL: libcpp-has-no-incomplete-tzdb +// XFAIL: availability-tzdb-missing + +// <chrono> + +// class leap_second; + +// constexpr seconds value() const noexcept; + +#include <cassert> +#include <chrono> + +#include "test_macros.h" + +// Add the include path required by test_chrono_leap_second.h when using libc++. +// ADDITIONAL_COMPILE_FLAGS(stdlib=libc++): -I %S/../../../../../../src/include +#include "test_chrono_leap_second.h" + +constexpr bool test(const std::chrono::leap_second leap_second, std::chrono::seconds expected) { + std::same_as<std::chrono::seconds> auto value = leap_second.value(); + assert(value == expected); + static_assert(noexcept(leap_second.value())); + + return true; +} + +int main(int, const char**) { + const std::chrono::tzdb& tzdb = std::chrono::get_tzdb(); + assert(!tzdb.leap_seconds.empty()); + test(tzdb.leap_seconds[0], tzdb.leap_seconds[0].value()); + + static_assert( + test(test_leap_second_create(std::chrono::sys_seconds{std::chrono::seconds{0}}, std::chrono::seconds{1}), + std::chrono::seconds{1})); + + return 0; +} diff --git a/libcxx/test/std/time/time.zone/time.zone.leap/nonmembers/comparison.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.leap/nonmembers/comparison.pass.cpp new file mode 100644 index 00000000000000..42dbbf139ddd3f --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.leap/nonmembers/comparison.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: no-filesystem, no-localization, no-tzdb + +// XFAIL: libcpp-has-no-incomplete-tzdb +// XFAIL: availability-tzdb-missing + +// <chrono> + +// class leap_second; + +//constexpr bool operator==(const leap_second& x, const leap_second& y); // C++20 +//constexpr strong_ordering operator<=>(const leap_second& x, const leap_second& y); +// +//template<class Duration> +// constexpr bool operator==(const leap_second& x, const sys_time<Duration>& y); +//template<class Duration> +// constexpr bool operator< (const leap_second& x, const sys_time<Duration>& y); +//template<class Duration> +// constexpr bool operator< (const sys_time<Duration>& x, const leap_second& y); +//template<class Duration> +// constexpr bool operator> (const leap_second& x, const sys_time<Duration>& y); +//template<class Duration> +// constexpr bool operator> (const sys_time<Duration>& x, const leap_second& y); +//template<class Duration> +// constexpr bool operator<=(const leap_second& x, const sys_time<Duration>& y); +//template<class Duration> +// constexpr bool operator<=(const sys_time<Duration>& x, const leap_second& y); +//template<class Duration> +// constexpr bool operator>=(const leap_second& x, const sys_time<Duration>& y); +//template<class Duration> +// constexpr bool operator>=(const sys_time<Duration>& x, const leap_second& y); +//template<class Duration> +// requires three_way_comparable_with<sys_seconds, sys_time<Duration>> +// constexpr auto operator<=>(const leap_second& x, const sys_time<Duration>& y); + +#include <cassert> +#include <chrono> + +#include "test_macros.h" +#include "test_comparisons.h" + +// Add the include path required by test_chrono_leap_second.h when using libc++. +// ADDITIONAL_COMPILE_FLAGS(stdlib=libc++): -I %S/../../../../../../src/include +#include "test_chrono_leap_second.h" + +constexpr bool test(const std::chrono::leap_second lhs, const std::chrono::leap_second rhs) { + AssertOrderReturn<std::strong_ordering, std::chrono::leap_second>(); + assert(testOrder(lhs, rhs, std::strong_ordering::less)); + + AssertOrderReturn<std::strong_ordering, std::chrono::leap_second, std::chrono::sys_seconds>(); + assert(testOrder(lhs, rhs.date(), std::strong_ordering::less)); + + AssertOrderReturn<std::strong_ordering, std::chrono::sys_seconds, std::chrono::leap_second>(); + assert(testOrder(lhs.date(), rhs, std::strong_ordering::less)); + + return true; +} + +int main(int, const char**) { + const std::chrono::tzdb& tzdb = std::chrono::get_tzdb(); + assert(tzdb.leap_seconds.size() > 2); + test(tzdb.leap_seconds[0], tzdb.leap_seconds[1]); + + static_assert( + test(test_leap_second_create(std::chrono::sys_seconds{std::chrono::seconds{0}}, std::chrono::seconds{1}), + test_leap_second_create(std::chrono::sys_seconds{std::chrono::seconds{1}}, std::chrono::seconds{2}))); + + return 0; +} diff --git a/libcxx/test/support/test_chrono_leap_second.h b/libcxx/test/support/test_chrono_leap_second.h new file mode 100644 index 00000000000000..253eac316c4246 --- /dev/null +++ b/libcxx/test/support/test_chrono_leap_second.h @@ -0,0 +1,53 @@ +// -*- 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 SUPPORT_TEST_CHRONO_LEAP_SECOND_HPP +#define SUPPORT_TEST_CHRONO_LEAP_SECOND_HPP + +/** + * @file Helper functions to create a @ref std::chrono::leap_second. + * + * Since the standard doesn't specify how a @ref std::chrono::leap_second is + * constructed this is implementation defined. To make the public API tests of + * the class generic this header defines helper functions to create the + * required object. + * + * @note This requires every standard library implementation to write their own + * helper function. Vendors are encouraged to create a pull request at + * https://github.com/llvm/llvm-project so their specific implementation can be + * part of this file. + */ + +#include "test_macros.h" + +#if TEST_STD_VER < 20 +# error "The format header requires at least C++20" +#endif + +#include <chrono> + +#ifdef _LIBCPP_VERSION + +// In order to find this include the calling test needs to provide this path in +// the search path. Typically this looks like: +// REMOVE_THIS_PREFIX__ADDITIONAL_COMPILE_FLAGS(stdlib=libc++): -I %S/../../../../../../src/include +// where the number of `../` sequences depends on the subdirectory level of the +// test. +# include "tzdb/leap_second_private.h" // Header in the dylib + +inline constexpr std::chrono::leap_second +test_leap_second_create(const std::chrono::sys_seconds& date, const std::chrono::seconds& value) { + return std::chrono::leap_second{std::chrono::leap_second::__constructor_tag{}, date, value}; +} + +#else // _LIBCPP_VERSION +# error "Please create a vendor specific version of the test functions and file a review at https://reviews.llvm.org/" +#endif // _LIBCPP_VERSION + +#endif // SUPPORT_TEST_CHRONO_LEAP_SECOND_HPP _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits