[libc++][chrono] Adds tzdb_list implementation.

This is the first step to implement time zone support in libc++. This
adds the complete tzdb_list class and a minimal tzdb class. The tzdb
class only contains the version, which is used by reload_tzdb.

Next to these classes it contains documentation and build system support
needed for time zone support. The code depends on the IANA Time Zone
Database, which should be available on the platform used or provided by
the libc++ vendors.

The code is labeled as experimental since there will be ABI breaks
during development; the tzdb class needs to have the standard headers.

Implements parts of:
- P0355 Extending <chrono> to Calendars and Time Zones

Addresses:
- LWG3319 Properly reference specification of IANA time zone database

Reviewed By: #libc, ldionne

Differential Revision: https://reviews.llvm.org/D154282
This commit is contained in:
Mark de Wever 2022-09-23 18:33:20 +02:00
parent 460840c09d
commit f78f93bc9f
50 changed files with 1354 additions and 9 deletions

View File

@ -87,6 +87,24 @@ option(LIBCXX_ENABLE_WIDE_CHARACTERS
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 <chrono> 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_LOCALIZATION _LIBCPP_HAS_NO_LOCALIZATION)
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)

View File

@ -0,0 +1 @@
set(LIBCXX_ENABLE_TIME_ZONE_DATABASE OFF CACHE BOOL "")

View File

@ -255,6 +255,15 @@ libc++ specific options
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 <chrono> will be disabled.
.. option:: LIBCXX_INSTALL_LIBRARY_DIR:PATH
**Default**: ``lib${LIBCXX_LIBDIR_SUFFIX}``

View File

@ -0,0 +1,122 @@
=================
Time Zone Support
=================
Introduction
============
Starting with C++20 the ``<chrono>`` library has support for time zones.
These are available in the
`IANA Time Zone Database <https://data.iana.org/time-zones/tz-link.html>`_.
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 <https://www.rfc-editor.org/rfc/rfc8536.html>`_. 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 <https://data.iana.org/time-zones/tz-how-to.html>`_
and in the `man page <https://man7.org/linux/man-pages/man8/zic.8.html>`_ 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 <https://data.iana.org/time-zones/releases/>`_.
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 <http://eel.is/c++draft/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.

View File

@ -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 <http://eel.is/c++draft/impldefindex>`_.

View File

@ -49,6 +49,15 @@ Paper Status
.. [#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 <https://wg21.link/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:

View File

@ -241,7 +241,7 @@
"`3316 <https://wg21.link/LWG3316>`__","Correctly define epoch for ``utc_clock``\ / ``utc_timepoint``\ ","Prague","","","|chrono|"
"`3317 <https://wg21.link/LWG3317>`__","Incorrect ``operator<<``\ for floating-point durations","Prague","","","|chrono|"
"`3318 <https://wg21.link/LWG3318>`__","Clarify whether clocks can represent time before their epoch","Prague","","","|chrono|"
"`3319 <https://wg21.link/LWG3319>`__","Properly reference specification of IANA time zone database","Prague","","","|chrono|"
"`3319 <https://wg21.link/LWG3319>`__","Properly reference specification of IANA time zone database","Prague","|Nothing To Do|","","|chrono|"
"`3320 <https://wg21.link/LWG3320>`__","``span::cbegin/cend``\ methods produce different results than ``std::[ranges::]cbegin/cend``\ ","Prague","|Complete|",""
"`3321 <https://wg21.link/LWG3321>`__","``uninitialized_construct_using_allocator``\ should use ``construct_at``\ ","Prague","|Complete|","16.0"
"`3323 <https://wg21.link/LWG3323>`__","``*has-tuple-element*``\ helper concept needs ``convertible_to``\ ","Prague","|Complete|","16.0","|ranges|"

Can't render this file because it has a wrong number of fields in line 2.

View File

@ -17,7 +17,7 @@
"`P0768R1 <https://wg21.link/P0768R1>`__","CWG","Library Support for the Spaceship (Comparison) Operator","Albuquerque","|Complete|",""
"`P0777R1 <https://wg21.link/P0777R1>`__","LWG","Treating Unnecessary ``decay``\ ","Albuquerque","|Complete|","7.0"
"`P0122R7 <https://wg21.link/P0122R7>`__","LWG","<span>","Jacksonville","|Complete|","7.0"
"`P0355R7 <https://wg21.link/P0355R7>`__","LWG","Extending chrono to Calendars and Time Zones","Jacksonville","|In Progress|",""
"`P0355R7 <https://wg21.link/P0355R7>`__","LWG","Extending chrono to Calendars and Time Zones","Jacksonville","|Partial| [#note-P0355]_",""
"`P0551R3 <https://wg21.link/P0551R3>`__","LWG","Thou Shalt Not Specialize ``std``\ Function Templates!","Jacksonville","|Complete|","11.0"
"`P0753R2 <https://wg21.link/P0753R2>`__","LWG","Manipulators for C++ Synchronized Buffered Ostream","Jacksonville","",""
"`P0754R2 <https://wg21.link/P0754R2>`__","LWG","<version>","Jacksonville","|Complete|","7.0"

Can't render this file because it has a wrong number of fields in line 2.

View File

@ -369,6 +369,13 @@ which no dialect declares as such (See the second form described above).
* ``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``

View File

@ -40,6 +40,7 @@ Getting Started with libc++
BuildingLibcxx
TestingLibcxx
Contributing
ImplementationDefinedBehavior
Modules
Hardening
ReleaseProcedure
@ -193,6 +194,7 @@ Design Documents
DesignDocs/UniquePtrTrivialAbi
DesignDocs/UnspecifiedBehaviorRandomization
DesignDocs/VisibilityMacros
DesignDocs/TimeZone
Build Bots and Test Coverage

View File

@ -283,6 +283,8 @@ set(files
__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

View File

@ -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...

View File

@ -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 <version>
// Enable the contents of the header only when libc++ was built with experimental features enabled.
#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
# include <string>
# 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

View File

@ -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 <version>
// 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 <forward_list>
# include <string_view>
# 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<tzdb>::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

View File

@ -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.

View File

@ -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

View File

@ -677,6 +677,39 @@ constexpr bool is_pm(hours const& h) noexcept;
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 @@ constexpr chrono::year operator ""y(unsigned lo
# 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

View File

@ -1191,6 +1191,14 @@ module std_private_chrono_system_clock [system] {
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" }

View File

@ -190,21 +190,27 @@ export namespace std {
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;

View File

@ -326,6 +326,13 @@ if (LIBCXX_PSTL_CPU_BACKEND STREQUAL "libdispatch")
)
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)

146
libcxx/src/tz.cpp Normal file
View File

@ -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 <chrono>
#include <filesystem>
#include <fstream>
#include <stdexcept>
#include <string>
// 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

113
libcxx/src/tzdb_list.cpp Normal file
View File

@ -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 <chrono>
#include <__mutex/unique_lock.h>
#include <forward_list>
// When threads are not available the locking is not required.
#ifndef _LIBCPP_HAS_NO_THREADS
# include <shared_mutex>
#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<T>::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> __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

View File

@ -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
// <chrono>
// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_NODISCARD_EXT
#include <chrono>
#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();
}

View File

@ -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
// <chrono>
#include <chrono>
#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}}
}

View File

@ -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

View File

@ -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
// <chrono>
//
// class tzdb_list;
//
// const_iterator erase_after(const_iterator p);
#include <cassert>
#include <chrono>
#include <fstream>
#include <iterator>
#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");
}

View File

@ -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
// <chrono>
// const tzdb& reload_tzdb();
#include <cassert>
#include <chrono>
#include <fstream>
#include <iterator>
#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;
}

View File

@ -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
// <chrono>
// Tests the IANA database version parsing.
// This is not part of the public tzdb interface.
#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 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;
}

View File

@ -64,7 +64,7 @@ else:
{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.

View File

@ -119,9 +119,11 @@ chrono cstddef
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

1 algorithm atomic
119 chrono cstdint
120 chrono cstring
121 chrono ctime
122 chrono forward_list
123 chrono limits
124 chrono ratio
125 chrono stdexcept
126 chrono string
127 chrono string_view
128 chrono tuple
129 chrono type_traits

View File

@ -119,9 +119,11 @@ chrono cstddef
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

1 algorithm atomic
119 chrono cstdint
120 chrono cstring
121 chrono ctime
122 chrono forward_list
123 chrono limits
124 chrono ratio
125 chrono stdexcept
126 chrono string
127 chrono string_view
128 chrono tuple
129 chrono type_traits

View File

@ -119,9 +119,11 @@ chrono cstddef
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

1 algorithm atomic
119 chrono cstdint
120 chrono cstring
121 chrono ctime
122 chrono forward_list
123 chrono limits
124 chrono ratio
125 chrono stdexcept
126 chrono string
127 chrono string_view
128 chrono tuple
129 chrono type_traits

View File

@ -119,9 +119,11 @@ chrono cstddef
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

1 algorithm atomic
119 chrono cstdint
120 chrono cstring
121 chrono ctime
122 chrono forward_list
123 chrono limits
124 chrono ratio
125 chrono stdexcept
126 chrono string
127 chrono string_view
128 chrono tuple
129 chrono type_traits

View File

@ -121,6 +121,7 @@ chrono cstddef
chrono cstdint
chrono cstring
chrono ctime
chrono forward_list
chrono limits
chrono locale
chrono optional

1 algorithm atomic
121 chrono cstdint
122 chrono cstring
123 chrono ctime
124 chrono forward_list
125 chrono limits
126 chrono locale
127 chrono optional

View File

@ -73,6 +73,7 @@ chrono compare
chrono cstddef
chrono cstdint
chrono ctime
chrono forward_list
chrono initializer_list
chrono limits
chrono locale

1 algorithm climits
73 chrono cstddef
74 chrono cstdint
75 chrono ctime
76 chrono forward_list
77 chrono initializer_list
78 chrono limits
79 chrono locale

View File

@ -73,6 +73,7 @@ chrono compare
chrono cstddef
chrono cstdint
chrono ctime
chrono forward_list
chrono initializer_list
chrono limits
chrono locale

1 algorithm climits
73 chrono cstddef
74 chrono cstdint
75 chrono ctime
76 chrono forward_list
77 chrono initializer_list
78 chrono limits
79 chrono locale

View File

@ -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
// <chrono>
// const tzdb& get_tzdb();
#include <chrono>
#include <cassert>
#include "test_macros.h"
int main(int, const char**) {
const std::chrono::tzdb& db = std::chrono::get_tzdb();
assert(!db.version.empty());
return 0;
}

View File

@ -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
// <chrono>
// const tzdb& get_tzdb_list();
#include <chrono>
#include <iterator>
#include <cassert>
#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;
}

View File

@ -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
// <chrono>
//
// 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 <chrono>
#include <concepts>
std::chrono::tzdb_list& list = std::chrono::get_tzdb_list();
static_assert(std::same_as<decltype(list.erase_after(std::chrono::tzdb_list::const_iterator{})),
std::chrono::tzdb_list::const_iterator>);

View File

@ -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
// <chrono>
//
// class tzdb_list;
//
// const tzdb& front() const noexcept;
#include <chrono>
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;
}

View File

@ -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
// <chrono>
//
// class tzdb_list;
//
// const_iterator begin() const noexcept;
// const_iterator end() const noexcept;
//
// const_iterator cbegin() const noexcept;
// const_iterator cend() const noexcept;
#include <chrono>
#include <iterator>
#include <cassert>
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<it> auto begin = list.begin();
std::same_as<it> auto end = list.end();
assert(std::distance(begin, end) == 1);
std::same_as<it> auto cbegin = list.cbegin();
assert(begin == cbegin);
std::same_as<it> auto cend = list.cend();
assert(end == cend);
return 0;
}

View File

@ -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
// <chrono>
// 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 <chrono>
#include <concepts>
static_assert(!std::copyable<std::chrono::tzdb_list>);
static_assert(!std::movable<std::chrono::tzdb_list>);
static_assert(!std::default_initializable<std::chrono::tzdb_list>);

View File

@ -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
// <chrono>
// 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 <cassert>
#include <chrono>
#include <iterator>
#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;
}

View File

@ -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
// <chrono>
// const string remote_version();
#include <chrono>
#include <cassert>
#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;
}

View File

@ -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
// <chrono>
// struct tzdb {
// string version;
// vector<time_zone> zones;
// vector<time_zone_link> links;
// vector<leap_second> leap_seconds;
//
// ...
// };
#include <chrono>
#include <cassert>
#include <concepts>
#include <string>
#include "assert_macros.h"
int main(int, const char**) {
std::chrono::tzdb tzdb;
static_assert(std::same_as<decltype(tzdb.version), std::string>);
tzdb.version = "version";
assert(tzdb.version == "version");
// TODO TZDB add the other data members
return 0;
}

View File

@ -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 <string_view>
#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 <chrono>.
_LIBCPP_AVAILABILITY_TZDB _LIBCPP_OVERRIDABLE_FUNC_VIS string_view __libcpp_tzdb_directory();
} // namespace chrono
_LIBCPP_END_NAMESPACE_STD
#endif
#endif // SUPPORT_TEST_TZDB_H

View File

@ -681,6 +681,24 @@ steps:
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:

View File

@ -459,6 +459,11 @@ generic-no-wide-characters)
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"

View File

@ -302,6 +302,7 @@ macros = {
"_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 @@ DEFAULT_FEATURES += [
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",

View File

@ -278,6 +278,7 @@ DEFAULT_PARAMETERS = [
else [
AddFeature("libcpp-has-no-incomplete-pstl"),
AddFeature("libcpp-has-no-experimental-stop_token"),
AddFeature("libcpp-has-no-incomplete-tzdb"),
],
),
Parameter(