diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt --- a/libcxx/CMakeLists.txt +++ b/libcxx/CMakeLists.txt @@ -87,6 +87,24 @@ support the C functionality for wide characters. When wide characters are not supported, several parts of the library will be disabled, notably the wide character specializations of std::basic_string." ON) + +# To use time zone support in libc++ the platform needs to have the IANA +# database installed. Libc++ will fail to build if this is enabled on a +# platform that does not provide the IANA database. The default is set to the +# current implementation state on the different platforms. +# +# TODO TZDB make the default always ON when most platforms ship with the IANA +# database. +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + set(ENABLE_TIME_ZONE_DATABASE_DEFAULT ON) +else() + set(ENABLE_TIME_ZONE_DATABASE_DEFAULT OFF) +endif() +option(LIBCXX_ENABLE_TIME_ZONE_DATABASE + "Whether to include support for time zones in the library. Disabling + time zone support can be useful when porting to platforms that don't + ship the IANA time zone database. When time zones are not supported, + time zone support in will be disabled." ${ENABLE_TIME_ZONE_DATABASE_DEFAULT}) option(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS "Whether to turn on vendor availability annotations on declarations that depend on definitions in a shared library. By default, we assume that we're not building @@ -756,6 +774,7 @@ config_define_if_not(LIBCXX_ENABLE_UNICODE _LIBCPP_HAS_NO_UNICODE) config_define_if_not(LIBCXX_ENABLE_WIDE_CHARACTERS _LIBCPP_HAS_NO_WIDE_CHARACTERS) config_define_if_not(LIBCXX_ENABLE_STD_MODULES _LIBCPP_HAS_NO_STD_MODULES) +config_define_if_not(LIBCXX_ENABLE_TIME_ZONE_DATABASE _LIBCPP_HAS_NO_TIME_ZONE_DATABASE) config_define_if_not(LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS _LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS) if (LIBCXX_HARDENING_MODE STREQUAL "hardened") config_define(1 _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT) diff --git a/libcxx/cmake/caches/Generic-no-tzdb.cmake b/libcxx/cmake/caches/Generic-no-tzdb.cmake new file mode 100644 --- /dev/null +++ b/libcxx/cmake/caches/Generic-no-tzdb.cmake @@ -0,0 +1 @@ +set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF CACHE BOOL "") diff --git a/libcxx/docs/BuildingLibcxx.rst b/libcxx/docs/BuildingLibcxx.rst --- a/libcxx/docs/BuildingLibcxx.rst +++ b/libcxx/docs/BuildingLibcxx.rst @@ -255,6 +255,15 @@ support for ``wchar_t``. This is especially useful in embedded settings where C Standard Libraries don't always provide all the usual bells and whistles. +.. option:: LIBCXX_ENABLE_TIME_ZONE_DATABASE:BOOL + + **Default**: ``ON`` + + Whether to include support for time zones in the library. Disabling + time zone support can be useful when porting to platforms that don't + ship the IANA time zone database. When time zones are not supported, + time zone support in will be disabled. + .. option:: LIBCXX_INSTALL_LIBRARY_DIR:PATH **Default**: ``lib${LIBCXX_LIBDIR_SUFFIX}`` diff --git a/libcxx/docs/DesignDocs/TimeZone.rst b/libcxx/docs/DesignDocs/TimeZone.rst new file mode 100644 --- /dev/null +++ b/libcxx/docs/DesignDocs/TimeZone.rst @@ -0,0 +1,122 @@ +================= +Time Zone Support +================= + +Introduction +============ + +Starting with C++20 the ```` library has support for time zones. +These are available in the +`IANA Time Zone Database `_. +This page describes the design decisions and trade-offs made to implement this +feature. This page contains several links with more information regarding the +contents of the IANA database, this page assumes the reader is familiar with +this information. + +Which version of the Time Zone Database to use +============================================== + +The data of the database is available on several platforms in different forms: + +- Typically Unix systems ship the database as + `TZif files `_. This format has + 3 versions and the ``time_zone_link`` information is not always available. + If available, they are symlinks in the file system. + These files don't provide the database version information. This information + is needed for the functions ``std::chrono:: remote_version()`` and + ``std::chrono::reload_tzdb()``. + +- On several Unix systems the time zone source files are available. These files + are stored in several regions, mainly the continents. This file contains a + large amount of comment with historical information regarding time zones. + The format is documented in the + `IANA documentation `_ + and in the `man page `_ of zic. + The disadvantage of this version is that at least Linux versions don't have + the database version information. This information is needed for the functions + ``std::chrono:: remote_version()`` and ``std::chrono::reload_tzdb()``. + +- On Linux systems ``tzdata.zi`` is available. This contains the same + information as the source files but in one file without the comments. This + file uses the same format as the sources, but shortens the names. For example + ``Rule`` is abbreviated to ``R``. This file contains the database version + information. + +The disadvantage of the ``TZif`` format (which is a binary format) is that it's +not possible to get the proper ``time_zone_link`` information on all platforms. +The time zone database version number is also missing from ``TZif`` files. +Since the time zone database is supposed to contain both these informations, +``TZif`` files can't be used to create a conforming implementation. + +Since it's easier to parse one file than a set of files we decided +to use the ``tzdata.zi``. The other benefit is that the ``tzdata.zi`` file +contains the database version information needed for a conforming +implementation. + +The ``tzdata.zi`` file is not available on all platforms as of August 2023, so +some vendors will need to make changes to their platform. Most vendors already +ship the database, so they only need to adjust the packaging of their time zone +package to include the files we require. One notable exception is Windows, +where no IANA time zone database is provided at all. However it's possible for +Windows packagers to add these files to their libc++ packages. The IANA +databases can be +`downloaded `_. + +An alternative would be to ship the database with libc++, either as a file or +compiled in the dylib. The text file is about 112 KB. For now libc++ will not +ship this file. If it's hard to get vendors to ship these files we can +reconsider based on that information. + +Leap seconds +------------ + +For the leap seconds libc++ will use the source file ``leap-seconds.list``. +This file is easier to parse than the ``leapseconds`` file. Both files are +present on Linux, but not always on other platforms. Since these platforms need +to change their packaging for ``tzdata.zi``, adding two instead of one files +seems a small change. + + +Updating the Time Zone Database +=============================== + +Per `[time.zone.db.remote]/1 `_ + +.. code-block:: text + + The local time zone database is that supplied by the implementation when the + program first accesses the database, for example via current_zone(). While the + program is running, the implementation may choose to update the time zone + database. This update shall not impact the program in any way unless the + program calls the functions in this subclause. This potentially updated time + zone database is referred to as the remote time zone database. + +There is an update mechanism in libc++, however this is not done automatically. +Invoking the function ``std::chrono::remote_version()`` will parse the version +information of the ``tzdata.zi`` file and return that information. Similarly, +``std::chrono::reload_tzdb()`` will parse the ``tzdata.zi`` and +``leap-seconds.list`` again. This makes it possible to update the database if +needed by the application and gives the user full power over the update policy. + +This approach has several advantages: + +- It is simple to implement. +- The library does not need to start a periodic background process to poll + changes to the filesystem. When using a background process, it may become + active when the application is busy with its core task, taking away resources + from that task. +- If there is no threading available this polling + becomes more involved. For example, query the file every *x* calls to + ``std::chrono::get_tzdb()``. This mean calls to ``std::chrono::get_tzdb()`` + would have different performance characteristics. + +The small drawback is: + +- On platforms with threading enabled updating the database may take longer. + On these platforms the remote database could have been loaded in a background + process. + +Another issue with the automatic update is that it may not be considered +Standard compliant, since the Standard uses the wording "This update shall not +impact the program in any way". Using resources could be considered as +impacting the program. diff --git a/libcxx/docs/ImplementationDefinedBehavior.rst b/libcxx/docs/ImplementationDefinedBehavior.rst new file mode 100644 --- /dev/null +++ b/libcxx/docs/ImplementationDefinedBehavior.rst @@ -0,0 +1,35 @@ +.. _implementation-defined-behavior: + +=============================== +Implementation-defined behavior +=============================== + +Contains the implementation details of the implementation-defined behavior in +libc++. Implementation-defined is mandated to be documented by the Standard. + +.. note: + This page is far from complete. + + +Implementation-defined behavior +=============================== + +Updating the Time Zone Database +------------------------------- + +The Standard allows implementations to automatically update the +*remote time zone database*. Libc++ opts not to do that. Instead calling + + - ``std::chrono::remote_version()`` will update the version information of the + *remote time zone database*, + - ``std::chrono::reload_tzdb()``, if needed, will update the entire + *remote time zone database*. + +This offers a way for users to update the *remote time zone database* and +give them full control over the process. + +Listed in the index of implementation-defined behavior +====================================================== + +The order of the entries matches the entries in the +`draft of the Standard `_. diff --git a/libcxx/docs/Status/Cxx20.rst b/libcxx/docs/Status/Cxx20.rst --- a/libcxx/docs/Status/Cxx20.rst +++ b/libcxx/docs/Status/Cxx20.rst @@ -49,6 +49,15 @@ .. [#note-P0883.2] P0883: ``ATOMIC_FLAG_INIT`` was marked deprecated in version 14.0, but was undeprecated with the implementation of LWG3659 in version 15.0. .. [#note-P2231] P2231: Optional is complete. The changes to variant haven't been implemented yet. .. [#note-P0660] P0660: Section 32.3 Stop Tokens is complete. ``jthread`` hasn't been implemented yet. + .. [#note-P0355] P0355: The implementation status is: + + * ``Calendars`` mostly done in Clang 7 + * ``Input parsers`` not done + * ``Stream output`` Obsolete due to `P1361R2 `_ "Integration of chrono with text formatting" + * ``Time zone and leap seconds`` In Progress + * ``TAI clock`` not done + * ``GPS clock`` not done + * ``UTC clock`` not done .. _issues-status-cxx20: diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv --- a/libcxx/docs/Status/Cxx20Issues.csv +++ b/libcxx/docs/Status/Cxx20Issues.csv @@ -241,7 +241,7 @@ "`3316 `__","Correctly define epoch for ``utc_clock``\ / ``utc_timepoint``\ ","Prague","","","|chrono|" "`3317 `__","Incorrect ``operator<<``\ for floating-point durations","Prague","","","|chrono|" "`3318 `__","Clarify whether clocks can represent time before their epoch","Prague","","","|chrono|" -"`3319 `__","Properly reference specification of IANA time zone database","Prague","","","|chrono|" +"`3319 `__","Properly reference specification of IANA time zone database","Prague","|Nothing To Do|","","|chrono|" "`3320 `__","``span::cbegin/cend``\ methods produce different results than ``std::[ranges::]cbegin/cend``\ ","Prague","|Complete|","" "`3321 `__","``uninitialized_construct_using_allocator``\ should use ``construct_at``\ ","Prague","|Complete|","16.0" "`3323 `__","``*has-tuple-element*``\ helper concept needs ``convertible_to``\ ","Prague","|Complete|","16.0","|ranges|" diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -17,7 +17,7 @@ "`P0768R1 `__","CWG","Library Support for the Spaceship (Comparison) Operator","Albuquerque","|Complete|","" "`P0777R1 `__","LWG","Treating Unnecessary ``decay``\ ","Albuquerque","|Complete|","7.0" "`P0122R7 `__","LWG","","Jacksonville","|Complete|","7.0" -"`P0355R7 `__","LWG","Extending chrono to Calendars and Time Zones","Jacksonville","|In Progress|","" +"`P0355R7 `__","LWG","Extending chrono to Calendars and Time Zones","Jacksonville","|Partial| [#note-P0355]_","" "`P0551R3 `__","LWG","Thou Shalt Not Specialize ``std``\ Function Templates!","Jacksonville","|Complete|","11.0" "`P0753R2 `__","LWG","Manipulators for C++ Synchronized Buffered Ostream","Jacksonville","","" "`P0754R2 `__","LWG","","Jacksonville","|Complete|","7.0" diff --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst --- a/libcxx/docs/UsingLibcxx.rst +++ b/libcxx/docs/UsingLibcxx.rst @@ -369,6 +369,13 @@ * ``byteswap`` * ``cbrt`` * ``ceil`` +* ``chrono::tzdb_list::begin`` +* ``chrono::tzdb_list::cbegin`` +* ``chrono::tzdb_list::cend`` +* ``chrono::tzdb_list::end`` +* ``chrono::get_tzdb_list`` +* ``chrono::get_tzdb`` +* ``chrono::remote_version`` * ``clamp`` * ``copysign`` * ``count_if`` diff --git a/libcxx/docs/index.rst b/libcxx/docs/index.rst --- a/libcxx/docs/index.rst +++ b/libcxx/docs/index.rst @@ -40,6 +40,7 @@ BuildingLibcxx TestingLibcxx Contributing + ImplementationDefinedBehavior Modules Hardening ReleaseProcedure @@ -193,6 +194,7 @@ DesignDocs/UniquePtrTrivialAbi DesignDocs/UnspecifiedBehaviorRandomization DesignDocs/VisibilityMacros + DesignDocs/TimeZone Build Bots and Test Coverage diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -283,6 +283,8 @@ __chrono/steady_clock.h __chrono/system_clock.h __chrono/time_point.h + __chrono/tzdb.h + __chrono/tzdb_list.h __chrono/weekday.h __chrono/year.h __chrono/year_month.h diff --git a/libcxx/include/__availability b/libcxx/include/__availability --- a/libcxx/include/__availability +++ b/libcxx/include/__availability @@ -174,6 +174,11 @@ // # define _LIBCPP_AVAILABILITY_HAS_NO_PMR # define _LIBCPP_AVAILABILITY_PMR + // This controls the availability of the C++20 time zone database. + // The parser code is built in the library. +// # define _LIBCPP_AVAILABILITY_HAS_NO_TZDB +# define _LIBCPP_AVAILABILITY_TZDB + #elif defined(__APPLE__) // shared_mutex and shared_timed_mutex @@ -348,6 +353,9 @@ # define _LIBCPP_AVAILABILITY_PMR # endif +# define _LIBCPP_AVAILABILITY_HAS_NO_TZDB +# define _LIBCPP_AVAILABILITY_TZDB __attribute__((unavailable)) + #else // ...New vendors can add availability markup here... diff --git a/libcxx/include/__chrono/tzdb.h b/libcxx/include/__chrono/tzdb.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__chrono/tzdb.h @@ -0,0 +1,45 @@ +// -*- 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_TZDB_H +#define _LIBCPP___CHRONO_TZDB_H + +#include +// Enable the contents of the header only when libc++ was built with experimental features enabled. +#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB) + +# include + +# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +# endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +# if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \ + !defined(_LIBCPP_HAS_NO_LOCALIZATION) + +namespace chrono { + +struct _LIBCPP_AVAILABILITY_TZDB tzdb { + string version; +}; + +} // namespace chrono + +# endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) + // && !defined(_LIBCPP_HAS_NO_LOCALIZATION) + +_LIBCPP_END_NAMESPACE_STD + +#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB) + +#endif // _LIBCPP___CHRONO_TZDB_H diff --git a/libcxx/include/__chrono/tzdb_list.h b/libcxx/include/__chrono/tzdb_list.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__chrono/tzdb_list.h @@ -0,0 +1,81 @@ +// -*- 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_TZDB_LIST_H +#define _LIBCPP___CHRONO_TZDB_LIST_H + +#include +// Enable the contents of the header only when libc++ was built with experimental features enabled. +#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB) + +# include <__availability> +# include <__chrono/tzdb.h> +# include +# include + +# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +# endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +# if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \ + !defined(_LIBCPP_HAS_NO_LOCALIZATION) + +namespace chrono { + +class _LIBCPP_AVAILABILITY_TZDB tzdb_list { +public: + _LIBCPP_EXPORTED_FROM_ABI explicit tzdb_list(tzdb&& __tzdb); + _LIBCPP_EXPORTED_FROM_ABI ~tzdb_list(); + + tzdb_list(const tzdb_list&) = delete; + tzdb_list& operator=(const tzdb_list&) = delete; + + using const_iterator = forward_list::const_iterator; + + _LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const tzdb& front() const noexcept; + + _LIBCPP_EXPORTED_FROM_ABI const_iterator erase_after(const_iterator __p); + + _LIBCPP_EXPORTED_FROM_ABI tzdb& __emplace_front(tzdb&& __tzdb); + + _LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const_iterator begin() const noexcept; + _LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const_iterator end() const noexcept; + + _LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const_iterator cbegin() const noexcept; + _LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const_iterator cend() const noexcept; + +private: + class __impl; + __impl* __impl_; +}; + +_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI tzdb_list& get_tzdb_list(); + +_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline const tzdb& get_tzdb() { + return get_tzdb_list().front(); +} + +_LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI const tzdb& reload_tzdb(); + +_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI string remote_version(); + +} // namespace chrono + +# endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) + // && !defined(_LIBCPP_HAS_NO_LOCALIZATION) + +_LIBCPP_END_NAMESPACE_STD + +#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB) + +#endif // _LIBCPP___CHRONO_TZDB_LIST_H diff --git a/libcxx/include/__config b/libcxx/include/__config --- a/libcxx/include/__config +++ b/libcxx/include/__config @@ -397,10 +397,8 @@ // easier to grep for target specific flags once the feature is complete. # if !defined(_LIBCPP_ENABLE_EXPERIMENTAL) && !defined(_LIBCPP_BUILDING_LIBRARY) # define _LIBCPP_HAS_NO_INCOMPLETE_PSTL -# endif - -# if !defined(_LIBCPP_ENABLE_EXPERIMENTAL) && !defined(_LIBCPP_BUILDING_LIBRARY) # define _LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN +# define _LIBCPP_HAS_NO_INCOMPLETE_TZDB # endif // Need to detect which libc we're using if we're on Linux. diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in --- a/libcxx/include/__config_site.in +++ b/libcxx/include/__config_site.in @@ -28,6 +28,7 @@ #cmakedefine _LIBCPP_HAS_NO_LOCALIZATION #cmakedefine _LIBCPP_HAS_NO_WIDE_CHARACTERS #cmakedefine _LIBCPP_HAS_NO_STD_MODULES +#cmakedefine _LIBCPP_HAS_NO_TIME_ZONE_DATABASE // PSTL backends #cmakedefine _LIBCPP_PSTL_CPU_BACKEND_SERIAL diff --git a/libcxx/include/chrono b/libcxx/include/chrono --- a/libcxx/include/chrono +++ b/libcxx/include/chrono @@ -677,6 +677,39 @@ constexpr hours make12(const hours& h) noexcept; constexpr hours make24(const hours& h, bool is_pm) noexcept; +// [time.zone.db], time zone database +struct tzdb { // C++20 + string version; +}; + +class tzdb_list { // C++20 +public: + tzdb_list(const tzdb_list&) = delete; + tzdb_list& operator=(const tzdb_list&) = delete; + + // unspecified additional constructors + + class const_iterator; + + const tzdb& front() const noexcept; + + const_iterator erase_after(const_iterator p); + + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; +}; + +// [time.zone.db.access], time zone database access +const tzdb& get_tzdb(); // C++20 +tzdb_list& get_tzdb_list(); // C++20 + +// [time.zone.db.remote], remote time zone database support +const tzdb& reload_tzdb(); // C++20 +string remote_version(); // C++20 + // 25.10.5, class time_zone // C++20 enum class choose {earliest, latest}; class time_zone; @@ -799,6 +832,11 @@ # include <__chrono/statically_widen.h> #endif +#if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \ + !defined(_LIBCPP_HAS_NO_LOCALIZATION) +# include <__chrono/tzdb.h> +# include <__chrono/tzdb_list.h> +#endif #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in --- a/libcxx/include/module.modulemap.in +++ b/libcxx/include/module.modulemap.in @@ -1191,6 +1191,14 @@ header "__chrono/system_clock.h" export std_private_chrono_time_point } +module std_private_chrono_tzdb [system] { + header "__chrono/tzdb.h" + export * +} +module std_private_chrono_tzdb_list [system] { + header "__chrono/tzdb_list.h" + export * +} module std_private_chrono_time_point [system] { header "__chrono/time_point.h" } module std_private_chrono_weekday [system] { header "__chrono/weekday.h" } module std_private_chrono_year [system] { header "__chrono/year.h" } diff --git a/libcxx/modules/std/chrono.inc b/libcxx/modules/std/chrono.inc --- a/libcxx/modules/std/chrono.inc +++ b/libcxx/modules/std/chrono.inc @@ -190,21 +190,27 @@ using std::chrono::make12; using std::chrono::make24; -#if 0 +#if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \ + !defined(_LIBCPP_HAS_NO_LOCALIZATION) + // [time.zone.db], time zone database using std::chrono::tzdb; using std::chrono::tzdb_list; // [time.zone.db.access], time zone database access - using std::chrono::current_zone; + // using std::chrono::current_zone; using std::chrono::get_tzdb; using std::chrono::get_tzdb_list; - using std::chrono::locate_zone; + // using std::chrono::locate_zone; // [time.zone.db.remote], remote time zone database support 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 // [time.zone.exception], exception classes using std::chrono::ambiguous_local_time; using std::chrono::nonexistent_local_time; diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt --- a/libcxx/src/CMakeLists.txt +++ b/libcxx/src/CMakeLists.txt @@ -326,6 +326,13 @@ ) endif() +if (LIBCXX_ENABLE_LOCALIZATION AND LIBCXX_ENABLE_FILESYSTEM AND LIBCXX_ENABLE_TIME_ZONE_DATABASE) + list(APPEND LIBCXX_EXPERIMENTAL_SOURCES + tz.cpp + tzdb_list.cpp + ) +endif() + add_library(cxx_experimental STATIC ${LIBCXX_EXPERIMENTAL_SOURCES}) target_link_libraries(cxx_experimental PUBLIC cxx-headers) if (LIBCXX_ENABLE_SHARED) diff --git a/libcxx/src/tz.cpp b/libcxx/src/tz.cpp new file mode 100644 --- /dev/null +++ b/libcxx/src/tz.cpp @@ -0,0 +1,146 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +#include +#include +#include +#include +#include + +// Contains a parser for the IANA time zone data files. +// +// These files can be found at https://data.iana.org/time-zones/ and are in the +// public domain. Information regarding the input can be found at +// https://data.iana.org/time-zones/tz-how-to.html and +// https://man7.org/linux/man-pages/man8/zic.8.html. +// +// As indicated at https://howardhinnant.github.io/date/tz.html#Installation +// For Windows another file seems to be required +// https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml +// This file seems to contain the mapping of Windows time zone name to IANA +// time zone names. +// +// However this article mentions another way to do the mapping on Windows +// https://devblogs.microsoft.com/oldnewthing/20210527-00/?p=105255 +// This requires Windows 10 Version 1903, which was released in May of 2019 +// and considered end of life in December 2020 +// https://learn.microsoft.com/en-us/lifecycle/announcements/windows-10-1903-end-of-servicing +// +// TODO TZDB Implement the Windows mapping in tzdb::current_zone + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace chrono { + +// This function is weak so it can be overriden in the tests. The +// declaration is in the test header test/support/test_tzdb.h +_LIBCPP_WEAK string_view __libcpp_tzdb_directory() { +#if defined(__linux__) + return "/usr/share/zoneinfo/"; +#else +# error "unknown path to the IANA Time Zone Database" +#endif +} + +[[nodiscard]] static bool __is_whitespace(int __c) { return __c == ' ' || __c == '\t'; } + +static void __skip_optional_whitespace(istream& __input) { + while (chrono::__is_whitespace(__input.peek())) + __input.get(); +} + +static void __skip_mandatory_whitespace(istream& __input) { + if (!chrono::__is_whitespace(__input.get())) + std::__throw_runtime_error("corrupt tzdb: expected whitespace"); + + chrono::__skip_optional_whitespace(__input); +} + +static void __matches(istream& __input, char __expected) { + if (std::tolower(__input.get()) != __expected) + std::__throw_runtime_error((string("corrupt tzdb: expected character '") + __expected + '\'').c_str()); +} + +static void __matches(istream& __input, string_view __expected) { + for (auto __c : __expected) + if (std::tolower(__input.get()) != __c) + std::__throw_runtime_error((string("corrupt tzdb: expected string '") + string(__expected) + '\'').c_str()); +} + +[[nodiscard]] static string __parse_string(istream& __input) { + string __result; + while (true) { + int __c = __input.get(); + switch (__c) { + case ' ': + case '\t': + case '\n': + __input.unget(); + [[fallthrough]]; + case istream::traits_type::eof(): + if (__result.empty()) + std::__throw_runtime_error("corrupt tzdb: expected a string"); + + return __result; + + default: + __result.push_back(__c); + } + } +} + +static string __parse_version(istream& __input) { + // The first line in tzdata.zi contains + // # version YYYYw + // The parser expects this pattern + // #\s*version\s*\(.*) + // This part is not documented. + chrono::__matches(__input, '#'); + chrono::__skip_optional_whitespace(__input); + chrono::__matches(__input, "version"); + chrono::__skip_mandatory_whitespace(__input); + return chrono::__parse_string(__input); +} + +static tzdb __make_tzdb() { + tzdb __result; + + filesystem::path __root = chrono::__libcpp_tzdb_directory(); + ifstream __tzdata{__root / "tzdata.zi"}; + + __result.version = chrono::__parse_version(__tzdata); + return __result; +} + +//===----------------------------------------------------------------------===// +// Public API +//===----------------------------------------------------------------------===// + +_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI tzdb_list& get_tzdb_list() { + static tzdb_list __result{chrono::__make_tzdb()}; + return __result; +} + +_LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI const tzdb& reload_tzdb() { + if (chrono::remote_version() == chrono::get_tzdb().version) + return chrono::get_tzdb(); + + return chrono::get_tzdb_list().__emplace_front(chrono::__make_tzdb()); +} + +_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI string remote_version() { + filesystem::path __root = chrono::__libcpp_tzdb_directory(); + ifstream __tzdata{__root / "tzdata.zi"}; + return chrono::__parse_version(__tzdata); +} + +} // namespace chrono + +_LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/src/tzdb_list.cpp b/libcxx/src/tzdb_list.cpp new file mode 100644 --- /dev/null +++ b/libcxx/src/tzdb_list.cpp @@ -0,0 +1,113 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +#include + +#include <__mutex/unique_lock.h> +#include + +// When threads are not available the locking is not required. +#ifndef _LIBCPP_HAS_NO_THREADS +# include +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace chrono { + +//===----------------------------------------------------------------------===// +// Private API +//===----------------------------------------------------------------------===// + +class tzdb_list::__impl { +public: + explicit __impl(tzdb&& __tzdb) { __tzdb_.push_front(std::move(__tzdb)); } + + using const_iterator = tzdb_list::const_iterator; + + const tzdb& front() const noexcept { +#ifndef _LIBCPP_HAS_NO_THREADS + shared_lock __lock{__mutex_}; +#endif + return __tzdb_.front(); + } + + const_iterator erase_after(const_iterator __p) { +#ifndef _LIBCPP_HAS_NO_THREADS + unique_lock __lock{__mutex_}; +#endif + return __tzdb_.erase_after(__p); + } + + tzdb& __emplace_front(tzdb&& __tzdb) { +#ifndef _LIBCPP_HAS_NO_THREADS + unique_lock __lock{__mutex_}; +#endif + return __tzdb_.emplace_front(std::move(__tzdb)); + } + + const_iterator begin() const noexcept { +#ifndef _LIBCPP_HAS_NO_THREADS + shared_lock __lock{__mutex_}; +#endif + return __tzdb_.begin(); + } + const_iterator end() const noexcept { + // forward_list::end does not access the list, so no need to take a lock. + return __tzdb_.end(); + } + + const_iterator cbegin() const noexcept { return begin(); } + const_iterator cend() const noexcept { return end(); } + +private: +#ifndef _LIBCPP_HAS_NO_THREADS + mutable shared_mutex __mutex_; +#endif + forward_list __tzdb_; +}; + +//===----------------------------------------------------------------------===// +// Public API +//===----------------------------------------------------------------------===// + +_LIBCPP_EXPORTED_FROM_ABI tzdb_list::tzdb_list(tzdb&& __tzdb) : __impl_{new __impl(std::move(__tzdb))} {} + +_LIBCPP_EXPORTED_FROM_ABI tzdb_list::~tzdb_list() { delete __impl_; } + +_LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const tzdb& tzdb_list::front() const noexcept { + return __impl_->front(); +} + +_LIBCPP_EXPORTED_FROM_ABI tzdb_list::const_iterator tzdb_list::erase_after(const_iterator __p) { + return __impl_->erase_after(__p); +} + +_LIBCPP_EXPORTED_FROM_ABI tzdb& tzdb_list::__emplace_front(tzdb&& __tzdb) { + return __impl_->__emplace_front(std::move(__tzdb)); +} + +_LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI tzdb_list::const_iterator tzdb_list::begin() const noexcept { + return __impl_->begin(); +} +_LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI tzdb_list::const_iterator tzdb_list::end() const noexcept { + return __impl_->end(); +} + +_LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI tzdb_list::const_iterator tzdb_list::cbegin() const noexcept { + return __impl_->cbegin(); +} +_LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI tzdb_list::const_iterator tzdb_list::cend() const noexcept { + return __impl_->cend(); +} + +} // namespace chrono + +_LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.compile.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Check that format functions aren't marked [[nodiscard]] when +// _LIBCPP_DISBALE_NODISCARD_EXT is defined + +// 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 + +// + +// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_NODISCARD_EXT + +#include + +#include "test_macros.h" + +void test() { + std::chrono::tzdb_list& list = std::chrono::get_tzdb_list(); + list.front(); + list.begin(); + list.end(); + list.cbegin(); + list.cend(); + + std::chrono::get_tzdb_list(); + std::chrono::get_tzdb(); + std::chrono::remote_version(); +} diff --git a/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/chrono.nodiscard_extensions.verify.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Check that format functions are marked [[nodiscard]] as a conforming extension + +// 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 + +// + +#include + +#include "test_macros.h" + +void test() { + std::chrono::tzdb_list& list = std::chrono::get_tzdb_list(); + list.front(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + list.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + list.end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + list.cbegin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + list.cend(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + + namespace crno = std::chrono; + crno::get_tzdb_list(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + crno::get_tzdb(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} + crno::remote_version(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} +} diff --git a/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp b/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp --- a/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp +++ b/libcxx/test/libcxx/experimental/fexperimental-library.compile.pass.cpp @@ -30,3 +30,7 @@ #ifdef _LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN # error "-fexperimental-library should enable the stop_token" #endif + +#ifdef _LIBCPP_HAS_NO_INCOMPLETE_TZDB +# error "-fexperimental-library should enable the chrono TZDB" +#endif diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.db/time.zone.db.list/erase_after.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.db/time.zone.db.list/erase_after.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/time/time.zone/time.zone.db/time.zone.db.list/erase_after.pass.cpp @@ -0,0 +1,71 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// +// +// class tzdb_list; +// +// const_iterator erase_after(const_iterator p); + +#include +#include +#include +#include + +#include "filesystem_test_helper.h" +#include "test_macros.h" +#include "test_tzdb.h" + +scoped_test_env env; +[[maybe_unused]] const std::filesystem::path dir = env.create_dir("zoneinfo"); +const std::filesystem::path data = env.create_file("zoneinfo/tzdata.zi"); + +std::string_view std::chrono::__libcpp_tzdb_directory() { + static std::string result = dir.string(); + return result; +} + +static void write(std::string_view input) { std::ofstream{data}.write(input.data(), input.size()); } + +int main(int, const char**) { + write("# version 1"); + std::chrono::tzdb_list& list = std::chrono::get_tzdb_list(); // [1] + + write("# version 2"); + std::chrono::reload_tzdb(); // [2, 1] + + assert(std::distance(list.begin(), list.end()) == 2); + assert(list.front().version == "2"); + + list.erase_after(list.begin()); // [2] + assert(std::distance(list.begin(), list.end()) == 1); + assert(list.front().version == "2"); + + write("# version 3"); + std::chrono::reload_tzdb(); // [3, 2] + assert(std::distance(list.begin(), list.end()) == 2); + + write("# version 4"); + std::chrono::reload_tzdb(); // [4, 3, 2] + assert(std::distance(list.begin(), list.end()) == 3); + assert(list.front().version == "4"); + + std::chrono::tzdb_list::const_iterator it = ++list.begin(); + assert(it->version == "3"); + + list.erase_after(it); // [4, 3] + assert(std::distance(list.begin(), list.end()) == 2); + assert(list.front().version == "4"); + assert(it->version == "3"); +} diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.db/time.zone.db.remote/reload_tzdb.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.db/time.zone.db.remote/reload_tzdb.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/time/time.zone/time.zone.db/time.zone.db.remote/reload_tzdb.pass.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// const tzdb& reload_tzdb(); + +#include +#include +#include +#include + +#include "filesystem_test_helper.h" +#include "test_macros.h" +#include "test_tzdb.h" + +scoped_test_env env; +[[maybe_unused]] const std::filesystem::path dir = env.create_dir("zoneinfo"); +const std::filesystem::path data = env.create_file("zoneinfo/tzdata.zi"); + +std::string_view std::chrono::__libcpp_tzdb_directory() { + static std::string result = dir.string(); + return result; +} + +static void write(std::string_view input) { std::ofstream{data}.write(input.data(), input.size()); } + +int main(int, const char**) { + write("# version old_version"); + const std::chrono::tzdb_list& list = std::chrono::get_tzdb_list(); + std::string version = "new_version"; + + assert(list.front().version == "old_version"); + assert(std::distance(list.begin(), list.end()) == 1); + assert(std::distance(list.cbegin(), list.cend()) == 1); + + write("# version new_version"); + assert(std::chrono::remote_version() == version); + + std::chrono::reload_tzdb(); + + assert(std::distance(list.begin(), list.end()) == 2); + assert(std::distance(list.cbegin(), list.cend()) == 2); + assert(list.front().version == version); + + return 0; +} diff --git a/libcxx/test/libcxx/time/time.zone/time.zone.db/version.pass.cpp b/libcxx/test/libcxx/time/time.zone/time.zone.db/version.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/time/time.zone/time.zone.db/version.pass.cpp @@ -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 +// +//===----------------------------------------------------------------------===// + +// 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 + +// + +// Tests the IANA database version parsing. +// This is not part of the public tzdb interface. + +#include +#include +#include +#include + +#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 data = env.create_file("zoneinfo/tzdata.zi"); + +std::string_view std::chrono::__libcpp_tzdb_directory() { + static std::string result = dir.string(); + return result; +} + +static void test(std::string_view input, std::string_view expected) { + std::ofstream{data}.write(input.data(), input.size()); + std::string version = std::chrono::remote_version(); + + TEST_REQUIRE( + version == expected, + TEST_WRITE_CONCATENATED( + "\nInput ", input, "\nExpected version ", expected, "\nActual version ", version, '\n')); +} + +static void test_exception(std::string_view input, [[maybe_unused]] std::string_view what) { + std::ofstream{data}.write(input.data(), input.size()); + + 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::remote_version()); +} + +int main(int, const char**) { + test_exception("", "corrupt tzdb: expected character '#'"); + test_exception("#version", "corrupt tzdb: expected whitespace"); + test("#version \t ABCD", "ABCD"); + test("#Version \t ABCD", "ABCD"); + test("#vErsion \t ABCD", "ABCD"); + test("#verSion \t ABCD", "ABCD"); + test("#VERSION \t ABCD", "ABCD"); + test("# \t version \t 2023a", "2023a"); + + return 0; +} diff --git a/libcxx/test/libcxx/transitive_includes.gen.py b/libcxx/test/libcxx/transitive_includes.gen.py --- a/libcxx/test/libcxx/transitive_includes.gen.py +++ b/libcxx/test/libcxx/transitive_includes.gen.py @@ -64,7 +64,7 @@ {lit_header_restrictions.get(header, '')} // TODO: Fix this test to make it work with localization or wide characters disabled -// UNSUPPORTED{BLOCKLIT}: no-localization, no-wide-characters +// UNSUPPORTED{BLOCKLIT}: no-localization, no-wide-characters, no-threads, no-filesystem, libcpp-has-no-incomplete-tzdb, no-tzdb // When built with modules, this test doesn't work because --trace-includes doesn't // report the stack of includes correctly. diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv --- a/libcxx/test/libcxx/transitive_includes/cxx03.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv @@ -119,9 +119,11 @@ chrono cstdint chrono cstring chrono ctime +chrono forward_list chrono limits chrono ratio chrono stdexcept +chrono string chrono string_view chrono tuple chrono type_traits diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv --- a/libcxx/test/libcxx/transitive_includes/cxx11.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv @@ -119,9 +119,11 @@ chrono cstdint chrono cstring chrono ctime +chrono forward_list chrono limits chrono ratio chrono stdexcept +chrono string chrono string_view chrono tuple chrono type_traits diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv --- a/libcxx/test/libcxx/transitive_includes/cxx14.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv @@ -119,9 +119,11 @@ chrono cstdint chrono cstring chrono ctime +chrono forward_list chrono limits chrono ratio chrono stdexcept +chrono string chrono string_view chrono tuple chrono type_traits diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv --- a/libcxx/test/libcxx/transitive_includes/cxx17.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv @@ -119,9 +119,11 @@ chrono cstdint chrono cstring chrono ctime +chrono forward_list chrono limits chrono ratio chrono stdexcept +chrono string chrono string_view chrono tuple chrono type_traits diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv --- a/libcxx/test/libcxx/transitive_includes/cxx20.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv @@ -121,6 +121,7 @@ chrono cstdint chrono cstring chrono ctime +chrono forward_list chrono limits chrono locale chrono optional diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv --- a/libcxx/test/libcxx/transitive_includes/cxx23.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv @@ -73,6 +73,7 @@ chrono cstddef chrono cstdint chrono ctime +chrono forward_list chrono initializer_list chrono limits chrono locale diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv --- a/libcxx/test/libcxx/transitive_includes/cxx26.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv @@ -73,6 +73,7 @@ chrono cstddef chrono cstdint chrono ctime +chrono forward_list chrono initializer_list chrono limits chrono locale 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 new file mode 100644 --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb.pass.cpp @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// const tzdb& get_tzdb(); + +#include + +#include + +#include "test_macros.h" + +int main(int, const char**) { + const std::chrono::tzdb& db = std::chrono::get_tzdb(); + + assert(!db.version.empty()); + + return 0; +} diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb_list.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb_list.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.access/get_tzdb_list.pass.cpp @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// const tzdb& get_tzdb_list(); + +#include + +#include +#include + +#include "test_macros.h" + +int main(int, const char**) { + const std::chrono::tzdb_list& list = std::chrono::get_tzdb_list(); + + assert(!list.front().version.empty()); + assert(std::distance(list.begin(), list.end()) == 1); + assert(std::distance(list.cbegin(), list.cend()) == 1); + + return 0; +} diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/erase_after.compile.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/erase_after.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/erase_after.compile.pass.cpp @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// +// +// class tzdb_list; +// +// const_iterator erase_after(const_iterator p); +// +// [time.zone.db.list]/5 +// Preconditions: The iterator following p is dereferenceable. +// +// Since there is no Standard way to create a second entry it's not +// possible to fullfill this precondition. This is tested in a libc++ +// specific test. + +#include +#include + +std::chrono::tzdb_list& list = std::chrono::get_tzdb_list(); +static_assert(std::same_as); diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/front.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/front.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/front.pass.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// +// +// class tzdb_list; +// +// const tzdb& front() const noexcept; + +#include + +int main(int, char**) { + const std::chrono::tzdb_list& list = std::chrono::get_tzdb_list(); + [[maybe_unused]] const std::chrono::tzdb& _ = list.front(); + static_assert(noexcept(list.front())); + + return 0; +} diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/iterators.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/iterators.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/iterators.pass.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// +// +// class tzdb_list; +// +// const_iterator begin() const noexcept; +// const_iterator end() const noexcept; +// +// const_iterator cbegin() const noexcept; +// const_iterator cend() const noexcept; + +#include +#include +#include + +int main(int, char**) { + const std::chrono::tzdb_list& list = std::chrono::get_tzdb_list(); + using it = std::chrono::tzdb_list::const_iterator; + + static_assert(noexcept(list.begin())); + static_assert(noexcept(list.end())); + static_assert(noexcept(list.cbegin())); + static_assert(noexcept(list.cend())); + + std::same_as auto begin = list.begin(); + std::same_as auto end = list.end(); + assert(std::distance(begin, end) == 1); + + std::same_as auto cbegin = list.cbegin(); + assert(begin == cbegin); + + std::same_as auto cend = list.cend(); + assert(end == cend); + + return 0; +} diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/types.compile.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.list/types.compile.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// class tzdb_list { +// public: +// tzdb_list(const tzdb_list&) = delete; +// tzdb_list& operator=(const tzdb_list&) = delete; +// +// ... +// +// }; +// +// [time.zone.db.list]/1 +// The tzdb_list database is a singleton; the unique object of type +// tzdb_list can be accessed via the get_tzdb_list() function. +//// +// This means the class may not have a default constructor. + +#include +#include + +static_assert(!std::copyable); +static_assert(!std::movable); +static_assert(!std::default_initializable); diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.remote/reload_tzdb.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.remote/reload_tzdb.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.remote/reload_tzdb.pass.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// Note there is no Standard way to change the remote database used. +// That is tested in +// test/libcxx/time/time.zone/time.zone.db/time.zone.db.remote/reload_tzdb.pass.cpp + +// const tzdb& reload_tzdb(); + +#include +#include +#include + +#include "test_macros.h" + +int main(int, const char**) { + const std::chrono::tzdb_list& list = std::chrono::get_tzdb_list(); + std::string version = list.front().version; + assert(!version.empty()); + + assert(std::distance(list.begin(), list.end()) == 1); + assert(std::distance(list.cbegin(), list.cend()) == 1); + assert(std::chrono::remote_version() == version); + + std::chrono::reload_tzdb(); + + assert(std::distance(list.begin(), list.end()) == 1); + assert(std::distance(list.cbegin(), list.cend()) == 1); + assert(std::chrono::remote_version() == version); + + return 0; +} diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.remote/remote_version.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.remote/remote_version.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.remote/remote_version.pass.cpp @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// const string remote_version(); + +#include + +#include + +#include "test_macros.h" + +int main(int, const char**) { + std::string version = std::chrono::remote_version(); + assert(!version.empty()); + + assert(version == std::chrono::get_tzdb().version); + assert(version == std::chrono::get_tzdb_list().front().version); + + return 0; +} diff --git a/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.tzdb/tzdb.members.pass.cpp b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.tzdb/tzdb.members.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/time/time.zone/time.zone.db/time.zone.db.tzdb/tzdb.members.pass.cpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// struct tzdb { +// string version; +// vector zones; +// vector links; +// vector leap_seconds; +// +// ... +// }; + +#include +#include +#include +#include + +#include "assert_macros.h" + +int main(int, const char**) { + std::chrono::tzdb tzdb; + + static_assert(std::same_as); + tzdb.version = "version"; + assert(tzdb.version == "version"); + + // TODO TZDB add the other data members + + return 0; +} diff --git a/libcxx/test/support/test_tzdb.h b/libcxx/test/support/test_tzdb.h new file mode 100644 --- /dev/null +++ b/libcxx/test/support/test_tzdb.h @@ -0,0 +1,31 @@ +// -*- 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_TZDB_H +#define SUPPORT_TEST_TZDB_H + +#include + +#if defined(_LIBCPP_VERSION) + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace chrono { + +// This function is marked as "overridable" in libc++ only for the test +// suite. Therefore the declaration is not in . +_LIBCPP_AVAILABILITY_TZDB _LIBCPP_OVERRIDABLE_FUNC_VIS string_view __libcpp_tzdb_directory(); + +} // namespace chrono + +_LIBCPP_END_NAMESPACE_STD + +#endif + +#endif // SUPPORT_TEST_TZDB_H diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml --- a/libcxx/utils/ci/buildkite-pipeline.yml +++ b/libcxx/utils/ci/buildkite-pipeline.yml @@ -681,6 +681,24 @@ limit: 2 timeout_in_minutes: 120 + - label: "No time zone database" + command: "libcxx/utils/ci/run-buildbot generic-no-tzdb" + artifact_paths: + - "**/test-results.xml" + - "**/*.abilist" + env: + CC: "clang-${LLVM_HEAD_VERSION}" + CXX: "clang++-${LLVM_HEAD_VERSION}" + ENABLE_CLANG_TIDY: "On" + agents: + queue: "libcxx-builders" + os: "linux" + retry: + automatic: + - exit_status: -1 # Agent was lost + limit: 2 + timeout_in_minutes: 120 + - label: "No experimental features" command: "libcxx/utils/ci/run-buildbot generic-no-experimental" artifact_paths: diff --git a/libcxx/utils/ci/run-buildbot b/libcxx/utils/ci/run-buildbot --- a/libcxx/utils/ci/run-buildbot +++ b/libcxx/utils/ci/run-buildbot @@ -459,6 +459,11 @@ generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-no-wide-characters.cmake" check-runtimes ;; +generic-no-tzdb) + clean + generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-no-tzdb.cmake" + check-runtimes +;; generic-no-experimental) clean generate-cmake -C "${MONOREPO_ROOT}/libcxx/cmake/caches/Generic-no-experimental.cmake" diff --git a/libcxx/utils/libcxx/test/features.py b/libcxx/utils/libcxx/test/features.py --- a/libcxx/utils/libcxx/test/features.py +++ b/libcxx/utils/libcxx/test/features.py @@ -302,6 +302,7 @@ "_LIBCPP_HAS_NO_RANDOM_DEVICE": "no-random-device", "_LIBCPP_HAS_NO_LOCALIZATION": "no-localization", "_LIBCPP_HAS_NO_WIDE_CHARACTERS": "no-wide-characters", + "_LIBCPP_HAS_NO_TIME_ZONE_DATABASE": "no-tzdb", "_LIBCPP_HAS_NO_UNICODE": "libcpp-has-no-unicode", "_LIBCPP_HAS_NO_STD_MODULES": "libcpp-has-no-std-modules", "_LIBCPP_PSTL_CPU_BACKEND_LIBDISPATCH": "libcpp-pstl-cpu-backend-libdispatch", @@ -556,6 +557,15 @@ cfg.available_features, ), ), + # Tests that require time zone database support in the built library + Feature( + name="availability-tzdb-missing", + when=lambda cfg: BooleanExpression.evaluate( + # TODO(ldionne) Please provide the correct value. + "(stdlib=apple-libc++ && target={{.+}}-apple-macosx{{(10.9|10.10|10.11|10.12|10.13|10.14|10.15|11.0|12.0|13.0)(.0)?}})", + cfg.available_features, + ), + ), # Tests that require 64-bit architecture Feature( name="32-bit-pointer", diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py --- a/libcxx/utils/libcxx/test/params.py +++ b/libcxx/utils/libcxx/test/params.py @@ -278,6 +278,7 @@ else [ AddFeature("libcpp-has-no-incomplete-pstl"), AddFeature("libcpp-has-no-experimental-stop_token"), + AddFeature("libcpp-has-no-incomplete-tzdb"), ], ), Parameter(