[libc++][chrono] Loads tzdata.zi in tzdb. (#74928)

This implements the loading of the tzdata.zi file and store its contents
in the tzdb struct.

This adds all required members except:
- the leap seconds,
- the locate_zone, and
- current_zone.

The class time_zone is incomplete and only contains the parts needed for
storing the parsed data.

The class time_zone_link is fully implemented including its non-member
functions.

Implements parts of:
- P0355 Extending <chrono> to Calendars and Time Zones
- P1614 The Mothership has Landed

Implements:
- P1982 Rename link to time_zone_link
This commit is contained in:
Mark de Wever 2024-02-17 14:28:01 +01:00 committed by GitHub
parent ded3ca224f
commit d332d88b91
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 2619 additions and 242 deletions

View File

@ -180,7 +180,7 @@
"`P1973R1 <https://wg21.link/P1973R1>`__","LWG","Rename ""_default_init"" Functions, Rev1","Prague","|Complete|","16.0" "`P1973R1 <https://wg21.link/P1973R1>`__","LWG","Rename ""_default_init"" Functions, Rev1","Prague","|Complete|","16.0"
"`P1976R2 <https://wg21.link/P1976R2>`__","LWG","Fixed-size span construction from dynamic range","Prague","|Complete|","11.0","|ranges|" "`P1976R2 <https://wg21.link/P1976R2>`__","LWG","Fixed-size span construction from dynamic range","Prague","|Complete|","11.0","|ranges|"
"`P1981R0 <https://wg21.link/P1981R0>`__","LWG","Rename leap to leap_second","Prague","* *","" "`P1981R0 <https://wg21.link/P1981R0>`__","LWG","Rename leap to leap_second","Prague","* *",""
"`P1982R0 <https://wg21.link/P1982R0>`__","LWG","Rename link to time_zone_link","Prague","* *","" "`P1982R0 <https://wg21.link/P1982R0>`__","LWG","Rename link to time_zone_link","Prague","|Complete|","19.0","|chrono|"
"`P1983R0 <https://wg21.link/P1983R0>`__","LWG","Wording for GB301, US296, US292, US291, and US283","Prague","|Complete|","15.0","|ranges|" "`P1983R0 <https://wg21.link/P1983R0>`__","LWG","Wording for GB301, US296, US292, US291, and US283","Prague","|Complete|","15.0","|ranges|"
"`P1994R1 <https://wg21.link/P1994R1>`__","LWG","elements_view needs its own sentinel","Prague","|Complete|","16.0","|ranges|" "`P1994R1 <https://wg21.link/P1994R1>`__","LWG","elements_view needs its own sentinel","Prague","|Complete|","16.0","|ranges|"
"`P2002R1 <https://wg21.link/P2002R1>`__","CWG","Defaulted comparison specification cleanups","Prague","* *","" "`P2002R1 <https://wg21.link/P2002R1>`__","CWG","Defaulted comparison specification cleanups","Prague","* *",""

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

View File

@ -171,10 +171,10 @@ Section,Description,Dependencies,Assignee,Complete
| `month_weekday_last <https://reviews.llvm.org/D152699>`_ | `month_weekday_last <https://reviews.llvm.org/D152699>`_
| `year_month_weekday <https://reviews.llvm.org/D152699>`_ | `year_month_weekday <https://reviews.llvm.org/D152699>`_
| `year_month_weekday_last <https://reviews.llvm.org/D152699>`_",None,Hristo Hristov,|Complete| | `year_month_weekday_last <https://reviews.llvm.org/D152699>`_",None,Hristo Hristov,|Complete|
`[time.zone.nonmembers] <https://wg21.link/time.zone.nonmembers>`_,"`chrono::time_zone`",A ``<chrono>`` implementation,Mark de Wever,|In Progress| `[time.zone.nonmembers] <https://wg21.link/time.zone.nonmembers>`_,"`chrono::time_zone`",A ``<chrono>`` implementation,Mark de Wever,|Complete|
`[time.zone.zonedtime.nonmembers] <https://wg21.link/time.zone.zonedtime.nonmembers>`_,"`chrono::zoned_time`",A ``<chrono>`` implementation,Mark de Wever,|In Progress| `[time.zone.zonedtime.nonmembers] <https://wg21.link/time.zone.zonedtime.nonmembers>`_,"`chrono::zoned_time`",A ``<chrono>`` implementation,Mark de Wever,|In Progress|
`[time.zone.leap.nonmembers] <https://wg21.link/time.zone.leap.nonmembers>`_,"`chrono::time_leap_seconds`",A ``<chrono>`` implementation,Mark de Wever,|In Progress| `[time.zone.leap.nonmembers] <https://wg21.link/time.zone.leap.nonmembers>`_,"`chrono::time_leap_seconds`",A ``<chrono>`` implementation,Mark de Wever,|In Progress|
`[time.zone.link.nonmembers] <https://wg21.link/time.zone.link.nonmembers>`_,"`chrono::time_zone_link`",A ``<chrono>`` implementation,Mark de Wever,|In Progress| `[time.zone.link.nonmembers] <https://wg21.link/time.zone.link.nonmembers>`_,"`chrono::time_zone_link`",A ``<chrono>`` implementation,Mark de Wever,|Complete|
- `5.13 Clause 28: Localization library <https://wg21.link/p1614r2#clause-28-localization-library>`_,,,, - `5.13 Clause 28: Localization library <https://wg21.link/p1614r2#clause-28-localization-library>`_,,,,
"| `[locale] <https://wg21.link/locale>`_ "| `[locale] <https://wg21.link/locale>`_
| `[locale.operators] <https://wg21.link/locale.operators>`_",| remove ops `locale <https://reviews.llvm.org/D152654>`_,None,Hristo Hristov,|Complete| | `[locale.operators] <https://wg21.link/locale.operators>`_",| remove ops `locale <https://reviews.llvm.org/D152654>`_,None,Hristo Hristov,|Complete|

1 Section Description Dependencies Assignee Complete
171
172
173
174
175
176
177
178
179
180

View File

@ -291,6 +291,8 @@ set(files
__chrono/steady_clock.h __chrono/steady_clock.h
__chrono/system_clock.h __chrono/system_clock.h
__chrono/time_point.h __chrono/time_point.h
__chrono/time_zone.h
__chrono/time_zone_link.h
__chrono/tzdb.h __chrono/tzdb.h
__chrono/tzdb_list.h __chrono/tzdb_list.h
__chrono/weekday.h __chrono/weekday.h

View File

@ -0,0 +1,86 @@
// -*- 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_TIME_ZONE_H
#define _LIBCPP___CHRONO_TIME_ZONE_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 <__compare/strong_order.h>
# include <__config>
# include <__memory/unique_ptr.h>
# include <string_view>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
# endif
_LIBCPP_PUSH_MACROS
# include <__undef_macros>
_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 time_zone {
_LIBCPP_HIDE_FROM_ABI time_zone() = default;
public:
class __impl; // public so it can be used by make_unique.
// The "constructor".
//
// The default constructor is private to avoid the constructor from being
// part of the ABI. Instead use an __ugly_named function as an ABI interface,
// since that gives us the ability to change it in the future.
[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI static time_zone __create(unique_ptr<__impl>&& __p);
_LIBCPP_EXPORTED_FROM_ABI ~time_zone();
_LIBCPP_HIDE_FROM_ABI time_zone(time_zone&&) = default;
_LIBCPP_HIDE_FROM_ABI time_zone& operator=(time_zone&&) = default;
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI string_view name() const noexcept { return __name(); }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI const __impl& __implementation() const noexcept { return *__impl_; }
private:
[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string_view __name() const noexcept;
unique_ptr<__impl> __impl_;
};
_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline bool
operator==(const time_zone& __x, const time_zone& __y) noexcept {
return __x.name() == __y.name();
}
_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline strong_ordering
operator<=>(const time_zone& __x, const time_zone& __y) noexcept {
return __x.name() <=> __y.name();
}
} // 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
_LIBCPP_POP_MACROS
#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
#endif // _LIBCPP___CHRONO_TIME_ZONE_H

View File

@ -0,0 +1,79 @@
// -*- 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_TIME_ZONE_LINK_H
#define _LIBCPP___CHRONO_TIME_ZONE_LINK_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 <__compare/strong_order.h>
# include <__config>
# include <string>
# include <string_view>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
# endif
_LIBCPP_PUSH_MACROS
# include <__undef_macros>
_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 time_zone_link {
public:
struct __constructor_tag;
_LIBCPP_NODISCARD_EXT
_LIBCPP_HIDE_FROM_ABI explicit time_zone_link(__constructor_tag&&, string_view __name, string_view __target)
: __name_{__name}, __target_{__target} {}
_LIBCPP_HIDE_FROM_ABI time_zone_link(time_zone_link&&) = default;
_LIBCPP_HIDE_FROM_ABI time_zone_link& operator=(time_zone_link&&) = default;
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI string_view name() const noexcept { return __name_; }
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI string_view target() const noexcept { return __target_; }
private:
string __name_;
// TODO TZDB instead of the name we can store the pointer to a zone. These
// pointers are immutable. This makes it possible to directly return a
// pointer in the time_zone in the 'locate_zone' function.
string __target_;
};
_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline bool
operator==(const time_zone_link& __x, const time_zone_link& __y) noexcept {
return __x.name() == __y.name();
}
_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_HIDE_FROM_ABI inline strong_ordering
operator<=>(const time_zone_link& __x, const time_zone_link& __y) noexcept {
return __x.name() <=> __y.name();
}
} // namespace chrono
# endif //_LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
#endif // _LIBCPP___CHRONO_TIME_ZONE_LINK_H

View File

@ -16,12 +16,19 @@
// Enable the contents of the header only when libc++ was built with experimental features enabled. // Enable the contents of the header only when libc++ was built with experimental features enabled.
#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB) #if !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
# include <__chrono/time_zone.h>
# include <__chrono/time_zone_link.h>
# include <__config>
# include <string> # include <string>
# include <vector>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header # pragma GCC system_header
# endif # endif
_LIBCPP_PUSH_MACROS
# include <__undef_macros>
_LIBCPP_BEGIN_NAMESPACE_STD _LIBCPP_BEGIN_NAMESPACE_STD
# if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \ # if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
@ -29,8 +36,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace chrono { namespace chrono {
struct _LIBCPP_AVAILABILITY_TZDB tzdb { struct tzdb {
string version; string version;
vector<time_zone> zones;
vector<time_zone_link> links;
}; };
} // namespace chrono } // namespace chrono
@ -40,6 +49,8 @@ struct _LIBCPP_AVAILABILITY_TZDB tzdb {
_LIBCPP_END_NAMESPACE_STD _LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB) #endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_TZDB)
#endif // _LIBCPP___CHRONO_TZDB_H #endif // _LIBCPP___CHRONO_TZDB_H

View File

@ -18,8 +18,9 @@
# include <__availability> # include <__availability>
# include <__chrono/tzdb.h> # include <__chrono/tzdb.h>
# include <__config>
# include <__fwd/string.h>
# include <forward_list> # include <forward_list>
# include <string_view>
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header # pragma GCC system_header
@ -32,9 +33,18 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace chrono { namespace chrono {
// TODO TZDB
// Libc++ recently switched to only export __ugly_names from the dylib.
// Since the library is still experimental the functions in this header
// should be adapted to this new style. The other tzdb headers should be
// evaluated too.
class _LIBCPP_AVAILABILITY_TZDB tzdb_list { class _LIBCPP_AVAILABILITY_TZDB tzdb_list {
public: public:
_LIBCPP_EXPORTED_FROM_ABI explicit tzdb_list(tzdb&& __tzdb); class __impl; // public to allow construction in dylib
_LIBCPP_HIDE_FROM_ABI explicit tzdb_list(__impl* __p) : __impl_(__p) {
_LIBCPP_ASSERT_NON_NULL(__impl_ != nullptr, "initialized time_zone without a valid pimpl object");
}
_LIBCPP_EXPORTED_FROM_ABI ~tzdb_list(); _LIBCPP_EXPORTED_FROM_ABI ~tzdb_list();
tzdb_list(const tzdb_list&) = delete; tzdb_list(const tzdb_list&) = delete;
@ -46,16 +56,15 @@ public:
_LIBCPP_EXPORTED_FROM_ABI const_iterator erase_after(const_iterator __p); _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 begin() const noexcept;
_LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const_iterator end() 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 cbegin() const noexcept;
_LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const_iterator cend() const noexcept; _LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const_iterator cend() const noexcept;
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI __impl& __implementation() { return *__impl_; }
private: private:
class __impl;
__impl* __impl_; __impl* __impl_;
}; };

View File

@ -686,6 +686,8 @@ constexpr hours make24(const hours& h, bool is_pm) noexcept;
// [time.zone.db], time zone database // [time.zone.db], time zone database
struct tzdb { // C++20 struct tzdb { // C++20
string version; string version;
vector<time_zone> zones;
vector<time_zone_link> links;
}; };
class tzdb_list { // C++20 class tzdb_list { // C++20
@ -716,15 +718,34 @@ tzdb_list& get_tzdb_list();
const tzdb& reload_tzdb(); // C++20 const tzdb& reload_tzdb(); // C++20
string remote_version(); // C++20 string remote_version(); // C++20
// 25.10.5, class time_zone // C++20 // 25.10.5, class time_zone // C++20
enum class choose {earliest, latest}; enum class choose {earliest, latest};
class time_zone; class time_zone {
bool operator==(const time_zone& x, const time_zone& y) noexcept; time_zone(time_zone&&) = default;
bool operator!=(const time_zone& x, const time_zone& y) noexcept; time_zone& operator=(time_zone&&) = default;
bool operator<(const time_zone& x, const time_zone& y) noexcept;
bool operator>(const time_zone& x, const time_zone& y) noexcept; // unspecified additional constructors
bool operator<=(const time_zone& x, const time_zone& y) noexcept;
bool operator>=(const time_zone& x, const time_zone& y) noexcept; string_view name() const noexcept;
};
bool operator==(const time_zone& x, const time_zone& y) noexcept; // C++20
strong_ordering operator<=>(const time_zone& x, const time_zone& y) noexcept; // C++20
// [time.zone.link], class time_zone_link
class time_zone_link { // C++20
public:
time_zone_link(time_zone_link&&) = default;
time_zone_link& operator=(time_zone_link&&) = default;
// unspecified additional constructors
string_view name() const noexcept;
string_view target() const noexcept;
};
bool operator==(const time_zone_link& x, const time_zone_link& y); // C++20
strong_ordering operator<=>(const time_zone_link& x, const time_zone_link& y); // C++20
} // chrono } // chrono
namespace std { namespace std {
@ -842,6 +863,8 @@ constexpr chrono::year operator ""y(unsigned lo
#if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \ #if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION) !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# include <__chrono/time_zone.h>
# include <__chrono/time_zone_link.h>
# include <__chrono/tzdb.h> # include <__chrono/tzdb.h>
# include <__chrono/tzdb_list.h> # include <__chrono/tzdb_list.h>
#endif #endif

View File

@ -288,6 +288,8 @@
{ include: [ "<__chrono/steady_clock.h>", "private", "<chrono>", "public" ] }, { include: [ "<__chrono/steady_clock.h>", "private", "<chrono>", "public" ] },
{ include: [ "<__chrono/system_clock.h>", "private", "<chrono>", "public" ] }, { include: [ "<__chrono/system_clock.h>", "private", "<chrono>", "public" ] },
{ include: [ "<__chrono/time_point.h>", "private", "<chrono>", "public" ] }, { include: [ "<__chrono/time_point.h>", "private", "<chrono>", "public" ] },
{ include: [ "<__chrono/time_zone.h>", "private", "<chrono>", "public" ] },
{ include: [ "<__chrono/time_zone_link.h>", "private", "<chrono>", "public" ] },
{ include: [ "<__chrono/tzdb.h>", "private", "<chrono>", "public" ] }, { include: [ "<__chrono/tzdb.h>", "private", "<chrono>", "public" ] },
{ include: [ "<__chrono/tzdb_list.h>", "private", "<chrono>", "public" ] }, { include: [ "<__chrono/tzdb_list.h>", "private", "<chrono>", "public" ] },
{ include: [ "<__chrono/weekday.h>", "private", "<chrono>", "public" ] }, { include: [ "<__chrono/weekday.h>", "private", "<chrono>", "public" ] },

View File

@ -1156,6 +1156,12 @@ module std_private_chrono_steady_clock [system] {
header "__chrono/steady_clock.h" header "__chrono/steady_clock.h"
export std_private_chrono_time_point export std_private_chrono_time_point
} }
module std_private_chrono_time_zone [system] {
header "__chrono/time_zone.h"
}
module std_private_chrono_time_zone_link [system] {
header "__chrono/time_zone_link.h"
}
module std_private_chrono_system_clock [system] { module std_private_chrono_system_clock [system] {
header "__chrono/system_clock.h" header "__chrono/system_clock.h"
export std_private_chrono_time_point export std_private_chrono_time_point

View File

@ -221,7 +221,11 @@ export namespace std {
// [time.zone.timezone], class time_zone // [time.zone.timezone], class time_zone
using std::chrono::choose; using std::chrono::choose;
# endif
# ifdef _LIBCPP_ENABLE_EXPERIMENTAL
using std::chrono::time_zone; using std::chrono::time_zone;
# endif
# if 0
// [time.zone.zonedtraits], class template zoned_traits // [time.zone.zonedtraits], class template zoned_traits
using std::chrono::zoned_traits; using std::chrono::zoned_traits;
@ -233,10 +237,14 @@ export namespace std {
// [time.zone.leap], leap second support // [time.zone.leap], leap second support
using std::chrono::leap_second; using std::chrono::leap_second;
# endif
# ifdef _LIBCPP_ENABLE_EXPERIMENTAL
// [time.zone.link], class time_zone_link // [time.zone.link], class time_zone_link
using std::chrono::time_zone_link; using std::chrono::time_zone_link;
# endif
# if 0
// [time.format], formatting // [time.format], formatting
using std::chrono::local_time_format; using std::chrono::local_time_format;
# endif # endif

View File

@ -336,7 +336,13 @@ endif()
if (LIBCXX_ENABLE_LOCALIZATION AND LIBCXX_ENABLE_FILESYSTEM AND LIBCXX_ENABLE_TIME_ZONE_DATABASE) if (LIBCXX_ENABLE_LOCALIZATION AND LIBCXX_ENABLE_FILESYSTEM AND LIBCXX_ENABLE_TIME_ZONE_DATABASE)
list(APPEND LIBCXX_EXPERIMENTAL_SOURCES list(APPEND LIBCXX_EXPERIMENTAL_SOURCES
tz.cpp include/tzdb/time_zone_link_private.h
include/tzdb/time_zone_private.h
include/tzdb/types_private.h
include/tzdb/tzdb_list_private.h
include/tzdb/tzdb_private.h
time_zone.cpp
tzdb.cpp
tzdb_list.cpp tzdb_list.cpp
) )
endif() endif()

View File

@ -0,0 +1,27 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
#ifndef _LIBCPP_SRC_INCLUDE_TZDB_TIME_ZONE_LINK_PRIVATE_H
#define _LIBCPP_SRC_INCLUDE_TZDB_TIME_ZONE_LINK_PRIVATE_H
#include <chrono>
_LIBCPP_BEGIN_NAMESPACE_STD
namespace chrono {
struct time_zone_link::__constructor_tag {};
} // namespace chrono
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_SRC_INCLUDE_TZDB_TIME_ZONE_LINK_PRIVATE_H

View File

@ -0,0 +1,48 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
#ifndef _LIBCPP_SRC_INCLUDE_TZDB_TIME_ZONE_PRIVATE_H
#define _LIBCPP_SRC_INCLUDE_TZDB_TIME_ZONE_PRIVATE_H
#include <chrono>
#include <string>
#include <vector>
#include "types_private.h"
_LIBCPP_BEGIN_NAMESPACE_STD
namespace chrono {
class time_zone::__impl {
public:
explicit _LIBCPP_HIDE_FROM_ABI __impl(string&& __name) : __name_(std::move(__name)) {}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI string_view __name() const noexcept { return __name_; }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI vector<__tz::__continuation>& __continuations() { return __continuations_; }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI const vector<__tz::__continuation>& __continuations() const {
return __continuations_;
}
private:
string __name_;
// Note the first line has a name + __continuation, the other lines
// are just __continuations. So there is always at least one item in
// the vector.
vector<__tz::__continuation> __continuations_;
};
} // namespace chrono
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_SRC_INCLUDE_TZDB_TIME_ZONE_PRIVATE_H

View File

@ -0,0 +1,106 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
#ifndef __LIBCPP_SRC_INCLUDE_TZDB_TYPES_PRIVATE_H
#define __LIBCPP_SRC_INCLUDE_TZDB_TYPES_PRIVATE_H
#include <chrono>
#include <string>
#include <utility>
#include <variant>
#include <vector>
_LIBCPP_BEGIN_NAMESPACE_STD
// TODO TZDB
// The helper classes in this header have no constructor but are loaded with
// dedicated parse functions. In the original design this header was public and
// the parsing was done in the dylib. In that design having constructors would
// expand the ABI interface. Since this header is now in the dylib that design
// should be reconsidered. (For now the design is kept as is, in case this
// header needs to be public for unforseen reasons.)
namespace chrono::__tz {
// Sun>=8 first Sunday on or after the eighth
// Sun<=25 last Sunday on or before the 25th
struct __constrained_weekday {
/* year_month_day operator()(year __year, month __month);*/ // needed but not implemented
weekday __weekday;
enum __comparison_t { __le, __ge } __comparison;
day __day;
};
// The on field has a few alternative presentations
// 5 the fifth of the month
// lastSun the last Sunday in the month
// lastMon the last Monday in the month
// Sun>=8 first Sunday on or after the eighth
// Sun<=25 last Sunday on or before the 25th
using __on = variant<day, weekday_last, __constrained_weekday>;
enum class __clock { __local, __standard, __universal };
struct __at {
seconds __time{0};
__tz::__clock __clock{__tz::__clock::__local};
};
struct __save {
seconds __time;
bool __is_dst;
};
// The names of the fields match the fields of a Rule.
struct __rule {
year __from;
year __to;
month __in;
__tz::__on __on;
__tz::__at __at;
__tz::__save __save;
string __letters;
};
using __rules_storage_type = std::vector<std::pair<string, vector<__tz::__rule>>>; // TODO TZDB use flat_map;
struct __continuation {
// Non-owning link to the RULE entries.
__tz::__rules_storage_type* __rule_database_;
seconds __stdoff;
// The RULES is either a SAVE or a NAME.
// The size_t is used as cache. After loading the rules they are
// sorted and remain stable, then an index in the vector can be
// used.
// If this field contains - then standard time always
// applies. This is indicated by the monostate.
using __rules_t = variant<monostate, __tz::__save, string, size_t>;
__rules_t __rules;
string __format;
// TODO TZDB the until field can contain more than just a year.
// Parts of the UNTIL, the optional parts are default initialized
// optional<year> __until_;
year __year = chrono::year::min();
month __in{January};
__tz::__on __on{chrono::day{1}};
__tz::__at __at{chrono::seconds{0}, __tz::__clock::__local};
};
} // namespace chrono::__tz
_LIBCPP_END_NAMESPACE_STD
#endif // __LIBCPP_SRC_INCLUDE_TZDB_TYPES_PRIVATE_H

View File

@ -0,0 +1,104 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
#ifndef _LIBCPP_SRC_INCLUDE_TZDB_TZDB_LIST_PRIVATE_H
#define _LIBCPP_SRC_INCLUDE_TZDB_TZDB_LIST_PRIVATE_H
#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
#include "types_private.h"
#include "tzdb_private.h"
_LIBCPP_BEGIN_NAMESPACE_STD
namespace chrono {
//===----------------------------------------------------------------------===//
// Private API
//===----------------------------------------------------------------------===//
// The tzdb_list stores a list of "tzdb" entries.
//
// The public tzdb database does not store the RULE entries of the IANA
// database. These entries are considered an implementation detail. Since most
// of the tzdb_list interface is exposed as "a list of tzdb entries" it's not
// possible to use a helper struct that stores a tzdb and the RULE database.
// Instead this class stores these in parallel forward lists.
//
// Since the nodes of a forward_list are stable it's possible to store pointers
// and references to these nodes.
class tzdb_list::__impl {
public:
__impl() { __load_no_lock(); }
[[nodiscard]] const tzdb& __load() {
#ifndef _LIBCPP_HAS_NO_THREADS
unique_lock __lock{__mutex_};
#endif
__load_no_lock();
return __tzdb_.front();
}
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
__rules_.erase_after(std::next(__rules_.cbegin(), std::distance(__tzdb_.cbegin(), __p)));
return __tzdb_.erase_after(__p);
}
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:
// Loads the tzdbs
// pre: The caller ensures the locking, if needed, is done.
void __load_no_lock() { chrono::__init_tzdb(__tzdb_.emplace_front(), __rules_.emplace_front()); }
#ifndef _LIBCPP_HAS_NO_THREADS
mutable shared_mutex __mutex_;
#endif
forward_list<tzdb> __tzdb_;
forward_list<__tz::__rules_storage_type> __rules_;
};
} // namespace chrono
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_SRC_INCLUDE_TZDB_TZDB_LIST_PRIVATE_H

View File

@ -0,0 +1,28 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// For information see https://libcxx.llvm.org/DesignDocs/TimeZone.html
#ifndef _LIBCPP_SRC_INCLUDE_TZDB_TZ_PRIVATE_H
#define _LIBCPP_SRC_INCLUDE_TZDB_TZ_PRIVATE_H
#include <chrono>
#include "types_private.h"
_LIBCPP_BEGIN_NAMESPACE_STD
namespace chrono {
void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules);
} // namespace chrono
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_SRC_INCLUDE_TZDB_TZ_PRIVATE_H

32
libcxx/src/time_zone.cpp Normal file
View File

@ -0,0 +1,32 @@
//===----------------------------------------------------------------------===//
//
// 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 "include/tzdb/time_zone_private.h"
_LIBCPP_BEGIN_NAMESPACE_STD
namespace chrono {
[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI time_zone time_zone::__create(unique_ptr<time_zone::__impl>&& __p) {
_LIBCPP_ASSERT_NON_NULL(__p != nullptr, "initialized time_zone without a valid pimpl object");
time_zone result;
result.__impl_ = std::move(__p);
return result;
}
_LIBCPP_EXPORTED_FROM_ABI time_zone::~time_zone() = default;
[[nodiscard]] _LIBCPP_EXPORTED_FROM_ABI string_view time_zone::__name() const noexcept { return __impl_->__name(); }
} // namespace chrono
_LIBCPP_END_NAMESPACE_STD

View File

@ -1,146 +0,0 @@
//===----------------------------------------------------------------------===//
//
// 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

641
libcxx/src/tzdb.cpp Normal file
View File

@ -0,0 +1,641 @@
//===----------------------------------------------------------------------===//
//
// 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 <algorithm>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <stdexcept>
#include <string>
#include "include/tzdb/time_zone_link_private.h"
#include "include/tzdb/time_zone_private.h"
#include "include/tzdb/types_private.h"
#include "include/tzdb/tzdb_list_private.h"
#include "include/tzdb/tzdb_private.h"
// 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
}
//===----------------------------------------------------------------------===//
// Details
//===----------------------------------------------------------------------===//
[[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);
}
[[nodiscard]] static bool __is_eol(int __c) { return __c == '\n' || __c == std::char_traits<char>::eof(); }
static void __skip_line(istream& __input) {
while (!chrono::__is_eol(__input.peek())) {
__input.get();
}
__input.get();
}
static void __skip(istream& __input, char __suffix) {
if (std::tolower(__input.peek()) == __suffix)
__input.get();
}
static void __skip(istream& __input, string_view __suffix) {
for (auto __c : __suffix)
if (std::tolower(__input.peek()) == __c)
__input.get();
}
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);
}
}
}
[[nodiscard]] static int64_t __parse_integral(istream& __input, bool __leading_zero_allowed) {
int64_t __result = __input.get();
if (__leading_zero_allowed) {
if (__result < '0' || __result > '9')
std::__throw_runtime_error("corrupt tzdb: expected a digit");
} else {
if (__result < '1' || __result > '9')
std::__throw_runtime_error("corrupt tzdb: expected a non-zero digit");
}
__result -= '0';
while (true) {
if (__input.peek() < '0' || __input.peek() > '9')
return __result;
// In order to avoid possible overflows we limit the accepted range.
// Most values parsed are expected to be very small:
// - 8784 hours in a year
// - 31 days in a month
// - year no real maximum, these values are expected to be less than
// the range of the year type.
//
// However the leapseconds use a seconds after epoch value. Using an
// int would run into an overflow in 2038. By using a 64-bit value
// the range is large enough for the bilions of years. Limiting that
// range slightly to make the code easier is not an issue.
if (__result > (std::numeric_limits<int64_t>::max() / 16))
std::__throw_runtime_error("corrupt tzdb: integral too large");
__result *= 10;
__result += __input.get() - '0';
}
}
//===----------------------------------------------------------------------===//
// Calendar
//===----------------------------------------------------------------------===//
[[nodiscard]] static day __parse_day(istream& __input) {
unsigned __result = chrono::__parse_integral(__input, false);
if (__result > 31)
std::__throw_runtime_error("corrupt tzdb day: value too large");
return day{__result};
}
[[nodiscard]] static weekday __parse_weekday(istream& __input) {
// TZDB allows the shortest unique name.
switch (std::tolower(__input.get())) {
case 'f':
chrono::__skip(__input, "riday");
return Friday;
case 'm':
chrono::__skip(__input, "onday");
return Monday;
case 's':
switch (std::tolower(__input.get())) {
case 'a':
chrono::__skip(__input, "turday");
return Saturday;
case 'u':
chrono::__skip(__input, "nday");
return Sunday;
}
break;
case 't':
switch (std::tolower(__input.get())) {
case 'h':
chrono::__skip(__input, "ursday");
return Thursday;
case 'u':
chrono::__skip(__input, "esday");
return Tuesday;
}
break;
case 'w':
chrono::__skip(__input, "ednesday");
return Wednesday;
}
std::__throw_runtime_error("corrupt tzdb weekday: invalid name");
}
[[nodiscard]] static month __parse_month(istream& __input) {
// TZDB allows the shortest unique name.
switch (std::tolower(__input.get())) {
case 'a':
switch (std::tolower(__input.get())) {
case 'p':
chrono::__skip(__input, "ril");
return April;
case 'u':
chrono::__skip(__input, "gust");
return August;
}
break;
case 'd':
chrono::__skip(__input, "ecember");
return December;
case 'f':
chrono::__skip(__input, "ebruary");
return February;
case 'j':
switch (std::tolower(__input.get())) {
case 'a':
chrono::__skip(__input, "nuary");
return January;
case 'u':
switch (std::tolower(__input.get())) {
case 'n':
chrono::__skip(__input, 'e');
return June;
case 'l':
chrono::__skip(__input, 'y');
return July;
}
}
break;
case 'm':
if (std::tolower(__input.get()) == 'a')
switch (std::tolower(__input.get())) {
case 'y':
return May;
case 'r':
chrono::__skip(__input, "ch");
return March;
}
break;
case 'n':
chrono::__skip(__input, "ovember");
return November;
case 'o':
chrono::__skip(__input, "ctober");
return October;
case 's':
chrono::__skip(__input, "eptember");
return September;
}
std::__throw_runtime_error("corrupt tzdb month: invalid name");
}
[[nodiscard]] static year __parse_year_value(istream& __input) {
bool __negative = __input.peek() == '-';
if (__negative) [[unlikely]]
__input.get();
int64_t __result = __parse_integral(__input, true);
if (__result > static_cast<int>(year::max())) {
if (__negative)
std::__throw_runtime_error("corrupt tzdb year: year is less than the minimum");
std::__throw_runtime_error("corrupt tzdb year: year is greater than the maximum");
}
return year{static_cast<int>(__negative ? -__result : __result)};
}
[[nodiscard]] static year __parse_year(istream& __input) {
if (std::tolower(__input.peek()) != 'm') [[likely]]
return chrono::__parse_year_value(__input);
__input.get();
switch (std::tolower(__input.peek())) {
case 'i':
__input.get();
chrono::__skip(__input, 'n');
[[fallthrough]];
case ' ':
// The m is minimum, even when that is ambiguous.
return year::min();
case 'a':
__input.get();
chrono::__skip(__input, 'x');
return year::max();
}
std::__throw_runtime_error("corrupt tzdb year: expected 'min' or 'max'");
}
//===----------------------------------------------------------------------===//
// TZDB fields
//===----------------------------------------------------------------------===//
[[nodiscard]] static year __parse_to(istream& __input, year __only) {
if (std::tolower(__input.peek()) != 'o')
return chrono::__parse_year(__input);
__input.get();
chrono::__skip(__input, "nly");
return __only;
}
[[nodiscard]] static __tz::__constrained_weekday::__comparison_t __parse_comparison(istream& __input) {
switch (__input.get()) {
case '>':
chrono::__matches(__input, '=');
return __tz::__constrained_weekday::__ge;
case '<':
chrono::__matches(__input, '=');
return __tz::__constrained_weekday::__le;
}
std::__throw_runtime_error("corrupt tzdb on: expected '>=' or '<='");
}
[[nodiscard]] static __tz::__on __parse_on(istream& __input) {
if (std::isdigit(__input.peek()))
return chrono::__parse_day(__input);
if (std::tolower(__input.peek()) == 'l') {
chrono::__matches(__input, "last");
return weekday_last(chrono::__parse_weekday(__input));
}
return __tz::__constrained_weekday{
chrono::__parse_weekday(__input), chrono::__parse_comparison(__input), chrono::__parse_day(__input)};
}
[[nodiscard]] static seconds __parse_duration(istream& __input) {
seconds __result{0};
int __c = __input.peek();
bool __negative = __c == '-';
if (__negative) {
__input.get();
// Negative is either a negative value or a single -.
// The latter means 0 and the parsing is complete.
if (!std::isdigit(__input.peek()))
return __result;
}
__result += hours(__parse_integral(__input, true));
if (__input.peek() != ':')
return __negative ? -__result : __result;
__input.get();
__result += minutes(__parse_integral(__input, true));
if (__input.peek() != ':')
return __negative ? -__result : __result;
__input.get();
__result += seconds(__parse_integral(__input, true));
if (__input.peek() != '.')
return __negative ? -__result : __result;
__input.get();
(void)__parse_integral(__input, true); // Truncate the digits.
return __negative ? -__result : __result;
}
[[nodiscard]] static __tz::__clock __parse_clock(istream& __input) {
switch (__input.get()) { // case sensitive
case 'w':
return __tz::__clock::__local;
case 's':
return __tz::__clock::__standard;
case 'u':
case 'g':
case 'z':
return __tz::__clock::__universal;
}
__input.unget();
return __tz::__clock::__local;
}
[[nodiscard]] static bool __parse_dst(istream& __input, seconds __offset) {
switch (__input.get()) { // case sensitive
case 's':
return false;
case 'd':
return true;
}
__input.unget();
return __offset != 0s;
}
[[nodiscard]] static __tz::__at __parse_at(istream& __input) {
return {__parse_duration(__input), __parse_clock(__input)};
}
[[nodiscard]] static __tz::__save __parse_save(istream& __input) {
seconds __time = chrono::__parse_duration(__input);
return {__time, chrono::__parse_dst(__input, __time)};
}
[[nodiscard]] static string __parse_letters(istream& __input) {
string __result = __parse_string(__input);
// Canonicalize "-" to "" since they are equivalent in the specification.
return __result != "-" ? __result : "";
}
[[nodiscard]] static __tz::__continuation::__rules_t __parse_rules(istream& __input) {
int __c = __input.peek();
// A single - is not a SAVE but a special case.
if (__c == '-') {
__input.get();
if (chrono::__is_whitespace(__input.peek()))
return monostate{};
__input.unget();
return chrono::__parse_save(__input);
}
if (std::isdigit(__c) || __c == '+')
return chrono::__parse_save(__input);
return chrono::__parse_string(__input);
}
[[nodiscard]] static __tz::__continuation __parse_continuation(__tz::__rules_storage_type& __rules, istream& __input) {
__tz::__continuation __result;
__result.__rule_database_ = std::addressof(__rules);
// Note STDOFF is specified as
// This field has the same format as the AT and SAVE fields of rule lines;
// These fields have different suffix letters, these letters seem
// not to be used so do not allow any of them.
__result.__stdoff = chrono::__parse_duration(__input);
chrono::__skip_mandatory_whitespace(__input);
__result.__rules = chrono::__parse_rules(__input);
chrono::__skip_mandatory_whitespace(__input);
__result.__format = chrono::__parse_string(__input);
chrono::__skip_optional_whitespace(__input);
if (chrono::__is_eol(__input.peek()))
return __result;
__result.__year = chrono::__parse_year(__input);
chrono::__skip_optional_whitespace(__input);
if (chrono::__is_eol(__input.peek()))
return __result;
__result.__in = chrono::__parse_month(__input);
chrono::__skip_optional_whitespace(__input);
if (chrono::__is_eol(__input.peek()))
return __result;
__result.__on = chrono::__parse_on(__input);
chrono::__skip_optional_whitespace(__input);
if (chrono::__is_eol(__input.peek()))
return __result;
__result.__at = __parse_at(__input);
return __result;
}
//===----------------------------------------------------------------------===//
// Time Zone Database entries
//===----------------------------------------------------------------------===//
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 void __parse_rule(tzdb& __tzdb, __tz::__rules_storage_type& __rules, istream& __input) {
chrono::__skip_mandatory_whitespace(__input);
string __name = chrono::__parse_string(__input);
if (__rules.empty() || __rules.back().first != __name)
__rules.emplace_back(__name, vector<__tz::__rule>{});
__tz::__rule& __rule = __rules.back().second.emplace_back();
chrono::__skip_mandatory_whitespace(__input);
__rule.__from = chrono::__parse_year(__input);
chrono::__skip_mandatory_whitespace(__input);
__rule.__to = chrono::__parse_to(__input, __rule.__from);
chrono::__skip_mandatory_whitespace(__input);
chrono::__matches(__input, '-');
chrono::__skip_mandatory_whitespace(__input);
__rule.__in = chrono::__parse_month(__input);
chrono::__skip_mandatory_whitespace(__input);
__rule.__on = chrono::__parse_on(__input);
chrono::__skip_mandatory_whitespace(__input);
__rule.__at = __parse_at(__input);
chrono::__skip_mandatory_whitespace(__input);
__rule.__save = __parse_save(__input);
chrono::__skip_mandatory_whitespace(__input);
__rule.__letters = chrono::__parse_letters(__input);
chrono::__skip_line(__input);
}
static void __parse_zone(tzdb& __tzdb, __tz::__rules_storage_type& __rules, istream& __input) {
chrono::__skip_mandatory_whitespace(__input);
auto __p = std::make_unique<time_zone::__impl>(chrono::__parse_string(__input));
vector<__tz::__continuation>& __continuations = __p->__continuations();
chrono::__skip_mandatory_whitespace(__input);
do {
// The first line must be valid, continuations are optional.
__continuations.emplace_back(__parse_continuation(__rules, __input));
chrono::__skip_line(__input);
chrono::__skip_optional_whitespace(__input);
} while (std::isdigit(__input.peek()) || __input.peek() == '-');
__tzdb.zones.emplace_back(time_zone::__create(std::move(__p)));
}
static void __parse_link(tzdb& __tzdb, istream& __input) {
chrono::__skip_mandatory_whitespace(__input);
string __target = chrono::__parse_string(__input);
chrono::__skip_mandatory_whitespace(__input);
string __name = chrono::__parse_string(__input);
chrono::__skip_line(__input);
__tzdb.links.emplace_back(time_zone_link::__constructor_tag{}, std::move(__name), std::move(__target));
}
static void __parse_tzdata(tzdb& __db, __tz::__rules_storage_type& __rules, istream& __input) {
while (true) {
int __c = std::tolower(__input.get());
switch (__c) {
case istream::traits_type::eof():
return;
case ' ':
case '\t':
case '\n':
break;
case '#':
chrono::__skip_line(__input);
break;
case 'r':
chrono::__skip(__input, "ule");
chrono::__parse_rule(__db, __rules, __input);
break;
case 'z':
chrono::__skip(__input, "one");
chrono::__parse_zone(__db, __rules, __input);
break;
case 'l':
chrono::__skip(__input, "ink");
chrono::__parse_link(__db, __input);
break;
default:
std::__throw_runtime_error("corrupt tzdb: unexpected input");
}
}
}
void __init_tzdb(tzdb& __tzdb, __tz::__rules_storage_type& __rules) {
filesystem::path __root = chrono::__libcpp_tzdb_directory();
ifstream __tzdata{__root / "tzdata.zi"};
__tzdb.version = chrono::__parse_version(__tzdata);
chrono::__parse_tzdata(__tzdb, __rules, __tzdata);
std::ranges::sort(__tzdb.zones);
std::ranges::sort(__tzdb.links);
std::ranges::sort(__rules, {}, [](const auto& p) { return p.first; });
}
//===----------------------------------------------------------------------===//
// Public API
//===----------------------------------------------------------------------===//
_LIBCPP_NODISCARD_EXT _LIBCPP_AVAILABILITY_TZDB _LIBCPP_EXPORTED_FROM_ABI tzdb_list& get_tzdb_list() {
static tzdb_list __result{new tzdb_list::__impl()};
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().__implementation().__load();
}
_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

View File

@ -10,76 +10,12 @@
#include <chrono> #include <chrono>
#include <__mutex/unique_lock.h> #include "include/tzdb/tzdb_list_private.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 _LIBCPP_BEGIN_NAMESPACE_STD
namespace chrono { 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_EXPORTED_FROM_ABI tzdb_list::~tzdb_list() { delete __impl_; }
_LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const tzdb& tzdb_list::front() const noexcept { _LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI const tzdb& tzdb_list::front() const noexcept {
@ -90,10 +26,6 @@ _LIBCPP_EXPORTED_FROM_ABI tzdb_list::const_iterator tzdb_list::erase_after(const
return __impl_->erase_after(__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 { _LIBCPP_NODISCARD_EXT _LIBCPP_EXPORTED_FROM_ABI tzdb_list::const_iterator tzdb_list::begin() const noexcept {
return __impl_->begin(); return __impl_->begin();
} }

View File

@ -23,6 +23,10 @@
#include "test_macros.h" #include "test_macros.h"
// These types have "private" constructors.
extern std::chrono::time_zone tz;
extern std::chrono::time_zone_link link;
void test() { void test() {
std::chrono::tzdb_list& list = std::chrono::get_tzdb_list(); std::chrono::tzdb_list& list = std::chrono::get_tzdb_list();
list.front(); list.front();
@ -34,4 +38,17 @@ void test() {
std::chrono::get_tzdb_list(); std::chrono::get_tzdb_list();
std::chrono::get_tzdb(); std::chrono::get_tzdb();
std::chrono::remote_version(); std::chrono::remote_version();
{
tz.name();
operator==(tz, tz);
operator<=>(tz, tz);
}
{
link.name();
link.target();
operator==(link, link);
operator<=>(link, link);
}
} }

View File

@ -20,6 +20,10 @@
#include "test_macros.h" #include "test_macros.h"
// These types have "private" constructors.
extern std::chrono::time_zone tz;
extern std::chrono::time_zone_link link;
void test() { void test() {
std::chrono::tzdb_list& list = std::chrono::get_tzdb_list(); std::chrono::tzdb_list& list = std::chrono::get_tzdb_list();
list.front(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} list.front(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@ -32,4 +36,19 @@ void test() {
crno::get_tzdb_list(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} 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::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}} crno::remote_version(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
{
tz.name(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
operator==(tz, tz); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
operator<=>(tz, tz); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
}
{
link.name(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
link.target(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
operator==(link, link);
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
operator<=>(link, link);
}
} }

View File

@ -0,0 +1,102 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17
// UNSUPPORTED: no-filesystem, no-localization, no-tzdb
// XFAIL: libcpp-has-no-incomplete-tzdb
// XFAIL: availability-tzdb-missing
// <chrono>
// Tests the IANA database rules links and operations.
// This is not part of the public tzdb interface.
#include <chrono>
#include <cassert>
#include <chrono>
#include <fstream>
#include <string>
#include <string_view>
#include "assert_macros.h"
#include "concat_macros.h"
#include "filesystem_test_helper.h"
#include "test_tzdb.h"
scoped_test_env env;
[[maybe_unused]] const std::filesystem::path dir = env.create_dir("zoneinfo");
const std::filesystem::path file = env.create_file("zoneinfo/tzdata.zi");
std::string_view std::chrono::__libcpp_tzdb_directory() {
static std::string result = dir.string();
return result;
}
void write(std::string_view input) {
static int version = 0;
std::ofstream f{file};
f << "# version " << version++ << '\n';
f.write(input.data(), input.size());
}
static const std::chrono::tzdb& parse(std::string_view input) {
write(input);
return std::chrono::reload_tzdb();
}
static void test_exception(std::string_view input, [[maybe_unused]] std::string_view what) {
write(input);
TEST_VALIDATE_EXCEPTION(
std::runtime_error,
[&]([[maybe_unused]] const std::runtime_error& e) {
TEST_LIBCPP_REQUIRE(
e.what() == what,
TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
},
TEST_IGNORE_NODISCARD std::chrono::reload_tzdb());
}
static void test_invalid() {
test_exception("L", "corrupt tzdb: expected whitespace");
test_exception("L ", "corrupt tzdb: expected a string");
test_exception("L n", "corrupt tzdb: expected whitespace");
test_exception("L n ", "corrupt tzdb: expected a string");
}
static void test_link() {
const std::chrono::tzdb& result = parse(
R"(
L z d
l b a
lInK b b
)");
assert(result.links.size() == 3);
assert(result.links[0].name() == "a");
assert(result.links[0].target() == "b");
assert(result.links[1].name() == "b");
assert(result.links[1].target() == "b");
assert(result.links[2].name() == "d");
assert(result.links[2].target() == "z");
}
int main(int, const char**) {
test_invalid();
test_link();
return 0;
}

View File

@ -0,0 +1,565 @@
//===----------------------------------------------------------------------===//
//
// 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 rules parsing and operations.
// This is not part of the public tzdb interface.
// The test uses private implementation headers.
// ADDITIONAL_COMPILE_FLAGS: -I %S/../../../../../src/include
#include <chrono>
#include <fstream>
#include <string>
#include <string_view>
#include <variant>
#include "assert_macros.h"
#include "concat_macros.h"
#include "filesystem_test_helper.h"
#include "test_tzdb.h"
// headers in the dylib
#include "tzdb/types_private.h"
#include "tzdb/tzdb_private.h"
scoped_test_env env;
[[maybe_unused]] const std::filesystem::path dir = env.create_dir("zoneinfo");
const std::filesystem::path file = 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) {
static int version = 0;
std::ofstream f{file};
f << "# version " << version++ << '\n';
f.write(input.data(), input.size());
}
struct parse_result {
explicit parse_result(std::string_view input) {
write(input);
std::chrono::tzdb tzdb; // result not needed for the tests.
std::chrono::__init_tzdb(tzdb, rules);
}
std::chrono::__tz::__rules_storage_type rules;
};
static void test_exception(std::string_view input, [[maybe_unused]] std::string_view what) {
write(input);
TEST_VALIDATE_EXCEPTION(
std::runtime_error,
[&]([[maybe_unused]] const std::runtime_error& e) {
TEST_LIBCPP_REQUIRE(
e.what() == what,
TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
},
TEST_IGNORE_NODISCARD std::chrono::reload_tzdb());
}
static void test_invalid() {
test_exception("R", "corrupt tzdb: expected whitespace");
test_exception("R ", "corrupt tzdb: expected a string");
test_exception("R r", "corrupt tzdb: expected whitespace");
test_exception("R r x", "corrupt tzdb: expected a digit");
test_exception("R r +", "corrupt tzdb: expected a digit");
test_exception("R r mx", "corrupt tzdb year: expected 'min' or 'max'");
test_exception("R r -32768", "corrupt tzdb year: year is less than the minimum");
test_exception("R r 32768", "corrupt tzdb year: year is greater than the maximum");
test_exception("R r mix", "corrupt tzdb: expected whitespace");
test_exception("R r 0", "corrupt tzdb: expected whitespace");
test_exception("R r 0 x", "corrupt tzdb: expected a digit");
test_exception("R r 0 +", "corrupt tzdb: expected a digit");
test_exception("R r 0 mx", "corrupt tzdb year: expected 'min' or 'max'");
test_exception("R r 0 mix", "corrupt tzdb: expected whitespace");
test_exception("R r 0 1", "corrupt tzdb: expected whitespace");
test_exception("R r 0 1 X", "corrupt tzdb: expected character '-'");
test_exception("R r 0 1 -", "corrupt tzdb: expected whitespace");
test_exception("R r 0 1 - j", "corrupt tzdb month: invalid name");
test_exception("R r 0 1 - Ja", "corrupt tzdb: expected whitespace");
test_exception("R r 0 1 - Ja +", "corrupt tzdb weekday: invalid name");
test_exception("R r 0 1 - Ja 32", "corrupt tzdb day: value too large");
test_exception("R r 0 1 - Ja l", "corrupt tzdb: expected string 'last'");
test_exception("R r 0 1 - Ja last", "corrupt tzdb weekday: invalid name");
test_exception("R r 0 1 - Ja lastS", "corrupt tzdb weekday: invalid name");
test_exception("R r 0 1 - Ja S", "corrupt tzdb weekday: invalid name");
test_exception("R r 0 1 - Ja Su", "corrupt tzdb on: expected '>=' or '<='");
test_exception("R r 0 1 - Ja Su>", "corrupt tzdb: expected character '='");
test_exception("R r 0 1 - Ja Su<", "corrupt tzdb: expected character '='");
test_exception("R r 0 1 - Ja Su>=+", "corrupt tzdb: expected a non-zero digit");
test_exception("R r 0 1 - Ja Su>=0", "corrupt tzdb: expected a non-zero digit");
test_exception("R r 0 1 - Ja Su>=32", "corrupt tzdb day: value too large");
test_exception("R r 0 1 - Ja Su>=31", "corrupt tzdb: expected whitespace");
test_exception("R r 0 1 - Ja Su>=31 ", "corrupt tzdb: expected a digit");
test_exception("R r 0 1 - Ja Su>=31 +", "corrupt tzdb: expected a digit");
test_exception("R r 0 1 - Ja Su>=31 1", "corrupt tzdb: expected whitespace");
test_exception("R r 0 1 - Ja Su>=31 1a", "corrupt tzdb: expected whitespace");
test_exception("R r 0 1 - Ja Su>=31 1w 2", "corrupt tzdb: expected whitespace");
test_exception("R r 0 1 - Ja Su>=31 1w 2a", "corrupt tzdb: expected whitespace");
test_exception("R r 0 1 - Ja Su>=31 1w 2s", "corrupt tzdb: expected whitespace");
test_exception("R r 0 1 - Ja Su>=31 1w 2s ", "corrupt tzdb: expected a string");
}
static void test_name() {
parse_result result{
R"(
R z 0 1 - Ja Su>=31 1w 2s -
rULE z 0 1 - Ja Su>=31 1w 2s -
RuLe z 0 1 - Ja Su>=31 1w 2s -
R a 0 1 - Ja Su>=31 1w 2s -
R a 0 1 - Ja Su>=31 1w 2s -
)"};
assert(result.rules.size() == 2);
assert(result.rules[0].first == "a");
assert(result.rules[0].second.size() == 2);
assert(result.rules[1].first == "z");
assert(result.rules[1].second.size() == 3);
}
static void test_from() {
parse_result result{
R"(
# min abbreviations
R a M 1 - Ja Su>=31 1w 2s -
R a mI 1 - Ja Su>=31 1w 2s -
R a mIN 1 - Ja Su>=31 1w 2s -
# max abbrviations
R a MA 1 - Ja Su>=31 1w 2s -
R a mAx 1 - Ja Su>=31 1w 2s -
R a -1000 1 - Ja Su>=31 1w 2s -
R a -100 1 - Ja Su>=31 1w 2s -
R a 0000 1 - Ja Su>=31 1w 2s -
R a 100 1 - Ja Su>=31 1w 2s -
R a 1000 1 - Ja Su>=31 1w 2s -
)"};
assert(result.rules.size() == 1);
assert(result.rules[0].second.size() == 10);
assert(result.rules[0].second[0].__from == std::chrono::year::min());
assert(result.rules[0].second[1].__from == std::chrono::year::min());
assert(result.rules[0].second[2].__from == std::chrono::year::min());
assert(result.rules[0].second[3].__from == std::chrono::year::max());
assert(result.rules[0].second[4].__from == std::chrono::year::max());
assert(result.rules[0].second[5].__from == std::chrono::year(-1000));
assert(result.rules[0].second[6].__from == std::chrono::year(-100));
assert(result.rules[0].second[7].__from == std::chrono::year(0));
assert(result.rules[0].second[8].__from == std::chrono::year(100));
assert(result.rules[0].second[9].__from == std::chrono::year(1000));
}
static void test_to() {
parse_result result{
R"(
# min abbreviations
R a 0 m - Ja Su>=31 1w 2s -
R a 0 mi - Ja Su>=31 1w 2s -
R a 0 min - Ja Su>=31 1w 2s -
# max abbrviations
R a 0 ma - Ja Su>=31 1w 2s -
R a 0 max - Ja Su>=31 1w 2s -
R a 0 -1000 - Ja Su>=31 1w 2s -
R a 0 -100 - Ja Su>=31 1w 2s -
R a 0 0000 - Ja Su>=31 1w 2s -
R a 0 100 - Ja Su>=31 1w 2s -
R a 0 1000 - Ja Su>=31 1w 2s -
# only abbreviations
R a m O - Ja Su>=31 1w 2s -
R a ma oN - Ja Su>=31 1w 2s -
R a -100 onL - Ja Su>=31 1w 2s -
R a 100 oNlY - Ja Su>=31 1w 2s -
)"};
assert(result.rules.size() == 1);
assert(result.rules[0].second.size() == 14);
assert(result.rules[0].second[0].__to == std::chrono::year::min());
assert(result.rules[0].second[1].__to == std::chrono::year::min());
assert(result.rules[0].second[2].__to == std::chrono::year::min());
assert(result.rules[0].second[3].__to == std::chrono::year::max());
assert(result.rules[0].second[4].__to == std::chrono::year::max());
assert(result.rules[0].second[5].__to == std::chrono::year(-1000));
assert(result.rules[0].second[6].__to == std::chrono::year(-100));
assert(result.rules[0].second[7].__to == std::chrono::year(0));
assert(result.rules[0].second[8].__to == std::chrono::year(100));
assert(result.rules[0].second[9].__to == std::chrono::year(1000));
assert(result.rules[0].second[10].__to == std::chrono::year::min());
assert(result.rules[0].second[11].__to == std::chrono::year::max());
assert(result.rules[0].second[12].__to == std::chrono::year(-100));
assert(result.rules[0].second[13].__to == std::chrono::year(100));
}
static void test_in() {
parse_result result{
R"(
# All tests in alphabetic order to validate shortest unique abbreviation
# Shortest abbreviation valid
R s 0 1 - ap Su>=31 1w 2s -
R s 0 1 - au Su>=31 1w 2s -
R s 0 1 - d Su>=31 1w 2s -
R s 0 1 - f Su>=31 1w 2s -
R s 0 1 - ja Su>=31 1w 2s -
R s 0 1 - jul Su>=31 1w 2s -
R s 0 1 - jun Su>=31 1w 2s -
R s 0 1 - May Su>=31 1w 2s -
R s 0 1 - mar Su>=31 1w 2s -
R s 0 1 - n Su>=31 1w 2s -
R s 0 1 - o Su>=31 1w 2s -
R s 0 1 - s Su>=31 1w 2s -
# 3 letter abbreviation
R a 0 1 - APR Su>=31 1w 2s -
R a 0 1 - AUG Su>=31 1w 2s -
R a 0 1 - DEC Su>=31 1w 2s -
R a 0 1 - FEB Su>=31 1w 2s -
R a 0 1 - JAN Su>=31 1w 2s -
R a 0 1 - JUL Su>=31 1w 2s -
R a 0 1 - JUN Su>=31 1w 2s -
R a 0 1 - MAY Su>=31 1w 2s -
R a 0 1 - MAR Su>=31 1w 2s -
R a 0 1 - NOV Su>=31 1w 2s -
R a 0 1 - OCT Su>=31 1w 2s -
R a 0 1 - SEP Su>=31 1w 2s -
# Full names
R f 0 1 - ApRiL Su>=31 1w 2s -
R f 0 1 - AuGuSt Su>=31 1w 2s -
R f 0 1 - DeCeMber Su>=31 1w 2s -
R f 0 1 - FeBrUary Su>=31 1w 2s -
R f 0 1 - JaNuAry Su>=31 1w 2s -
R f 0 1 - JuLy Su>=31 1w 2s -
R f 0 1 - JuNe Su>=31 1w 2s -
R f 0 1 - MaY Su>=31 1w 2s -
R f 0 1 - MaRch Su>=31 1w 2s -
R f 0 1 - NoVemBeR Su>=31 1w 2s -
R f 0 1 - OcTobEr Su>=31 1w 2s -
R f 0 1 - SePteMbEr Su>=31 1w 2s -
)"};
assert(result.rules.size() == 3);
for (std::size_t i = 0; i < result.rules.size(); ++i) {
assert(result.rules[i].second.size() == 12);
assert(result.rules[i].second[0].__in == std::chrono::April);
assert(result.rules[i].second[1].__in == std::chrono::August);
assert(result.rules[i].second[2].__in == std::chrono::December);
assert(result.rules[i].second[3].__in == std::chrono::February);
assert(result.rules[i].second[4].__in == std::chrono::January);
assert(result.rules[i].second[5].__in == std::chrono::July);
assert(result.rules[i].second[6].__in == std::chrono::June);
assert(result.rules[i].second[7].__in == std::chrono::May);
assert(result.rules[i].second[8].__in == std::chrono::March);
assert(result.rules[i].second[9].__in == std::chrono::November);
assert(result.rules[i].second[10].__in == std::chrono::October);
assert(result.rules[i].second[11].__in == std::chrono::September);
}
};
static void test_on_day() {
parse_result result{
R"(
# The parser does not validate the day as valid day of month
R a 0 1 - Fe 1 1w 2s -
R a 0 1 - Fe 10 1w 2s -
R a 0 1 - Fe 20 1w 2s -
R a 0 1 - Fe 30 1w 2s -
R a 0 1 - Fe 31 1w 2s -
)"};
assert(result.rules.size() == 1);
assert(result.rules[0].second.size() == 5);
assert(std::get<std::chrono::day>(result.rules[0].second[0].__on) == std::chrono::day(1));
assert(std::get<std::chrono::day>(result.rules[0].second[1].__on) == std::chrono::day(10));
assert(std::get<std::chrono::day>(result.rules[0].second[2].__on) == std::chrono::day(20));
assert(std::get<std::chrono::day>(result.rules[0].second[3].__on) == std::chrono::day(30));
assert(std::get<std::chrono::day>(result.rules[0].second[4].__on) == std::chrono::day(31));
}
static void test_on_last() {
parse_result result{
R"(
# All tests in alphabetic order to validate shortest unique abbreviation
# Shortest abbreviation valid
R s 0 1 - Ja lastF 1w 2s -
R s 0 1 - Ja lastM 1w 2s -
R s 0 1 - Ja lastSa 1w 2s -
R s 0 1 - Ja lastSu 1w 2s -
R s 0 1 - Ja lastTh 1w 2s -
R s 0 1 - Ja lastTu 1w 2s -
R s 0 1 - Ja lastW 1w 2s -
# 3 letter abbreviation
R a 0 1 - Ja lastFri 1w 2s -
R a 0 1 - Ja lastMon 1w 2s -
R a 0 1 - Ja lastSat 1w 2s -
R a 0 1 - Ja lastSun 1w 2s -
R a 0 1 - Ja lastThu 1w 2s -
R a 0 1 - Ja lastTue 1w 2s -
R a 0 1 - Ja lastWed 1w 2s -
# Full names
R f 0 1 - Ja lastFriday 1w 2s -
R f 0 1 - Ja lastMonday 1w 2s -
R f 0 1 - Ja lastSaturday 1w 2s -
R f 0 1 - Ja lastSunday 1w 2s -
R f 0 1 - Ja lastThursday 1w 2s -
R f 0 1 - Ja lastTuesday 1w 2s -
R f 0 1 - Ja lastWednesday 1w 2s -
)"};
assert(result.rules.size() == 3);
for (std::size_t i = 0; i < result.rules.size(); ++i) {
assert(result.rules[i].second.size() == 7);
assert(std::get<std::chrono::weekday_last>(result.rules[i].second[0].__on) ==
std::chrono::weekday_last(std::chrono::Friday));
assert(std::get<std::chrono::weekday_last>(result.rules[i].second[1].__on) ==
std::chrono::weekday_last(std::chrono::Monday));
assert(std::get<std::chrono::weekday_last>(result.rules[i].second[2].__on) ==
std::chrono::weekday_last(std::chrono::Saturday));
assert(std::get<std::chrono::weekday_last>(result.rules[i].second[3].__on) ==
std::chrono::weekday_last(std::chrono::Sunday));
assert(std::get<std::chrono::weekday_last>(result.rules[i].second[4].__on) ==
std::chrono::weekday_last(std::chrono::Thursday));
assert(std::get<std::chrono::weekday_last>(result.rules[i].second[5].__on) ==
std::chrono::weekday_last(std::chrono::Tuesday));
assert(std::get<std::chrono::weekday_last>(result.rules[i].second[6].__on) ==
std::chrono::weekday_last(std::chrono::Wednesday));
}
}
static void test_on_constrain() {
parse_result result{
R"(
# Shortest abbreviation valid
R s 0 1 - Ja F>=1 1w 2s -
R s 0 1 - Ja M<=1 1w 2s -
R s 0 1 - Ja Sa>=31 1w 2s -
R s 0 1 - Ja Su<=31 1w 2s -
R s 0 1 - Ja Th>=10 1w 2s -
R s 0 1 - Ja Tu<=20 1w 2s -
R s 0 1 - Ja W>=30 1w 2s -
# 3 letter abbreviation
R a 0 1 - Ja Fri>=1 1w 2s -
R a 0 1 - Ja Mon<=1 1w 2s -
R a 0 1 - Ja Sat>=31 1w 2s -
R a 0 1 - Ja Sun<=31 1w 2s -
R a 0 1 - Ja Thu>=10 1w 2s -
R a 0 1 - Ja Tue<=20 1w 2s -
R a 0 1 - Ja Wed>=30 1w 2s -
# Full names
R f 0 1 - Ja Friday>=1 1w 2s -
R f 0 1 - Ja Monday<=1 1w 2s -
R f 0 1 - Ja Saturday>=31 1w 2s -
R f 0 1 - Ja Sunday<=31 1w 2s -
R f 0 1 - Ja Thursday>=10 1w 2s -
R f 0 1 - Ja Tuesday<=20 1w 2s -
R f 0 1 - Ja Wednesday>=30 1w 2s -
)"};
std::chrono::__tz::__constrained_weekday r;
assert(result.rules.size() == 3);
for (std::size_t i = 0; i < result.rules.size(); ++i) {
assert(result.rules[i].second.size() == 7);
r = std::get<std::chrono::__tz::__constrained_weekday>(result.rules[i].second[0].__on);
assert(r.__weekday == std::chrono::Friday);
assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__ge);
assert(r.__day == std::chrono::day(1));
r = std::get<std::chrono::__tz::__constrained_weekday>(result.rules[i].second[1].__on);
assert(r.__weekday == std::chrono::Monday);
assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__le);
assert(r.__day == std::chrono::day(1));
r = std::get<std::chrono::__tz::__constrained_weekday>(result.rules[i].second[2].__on);
assert(r.__weekday == std::chrono::Saturday);
assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__ge);
assert(r.__day == std::chrono::day(31));
r = std::get<std::chrono::__tz::__constrained_weekday>(result.rules[i].second[3].__on);
assert(r.__weekday == std::chrono::Sunday);
assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__le);
assert(r.__day == std::chrono::day(31));
r = std::get<std::chrono::__tz::__constrained_weekday>(result.rules[i].second[4].__on);
assert(r.__weekday == std::chrono::Thursday);
assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__ge);
assert(r.__day == std::chrono::day(10));
r = std::get<std::chrono::__tz::__constrained_weekday>(result.rules[i].second[5].__on);
assert(r.__weekday == std::chrono::Tuesday);
assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__le);
assert(r.__day == std::chrono::day(20));
r = std::get<std::chrono::__tz::__constrained_weekday>(result.rules[i].second[6].__on);
assert(r.__weekday == std::chrono::Wednesday);
assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__ge);
assert(r.__day == std::chrono::day(30));
}
}
static void test_on() {
test_on_day();
test_on_last();
test_on_constrain();
}
static void test_at() {
parse_result result{
R"(
# Based on the examples in the man page.
# Note the input is not expected to have fractional seconds, they are truncated.
R a 0 1 - Ja Su>=31 2w 2s -
R a 0 1 - Ja Su>=31 2:00s 2s -
R a 0 1 - Ja Su>=31 01:28:14u 2s -
R a 0 1 - Ja Su>=31 00:19:32.10g 2s -
R a 0 1 - Ja Su>=31 12:00z 2s -
R a 0 1 - Ja Su>=31 15:00 2s -
R a 0 1 - Ja Su>=31 24:00 2s -
R a 0 1 - Ja Su>=31 260:00 2s -
R a 0 1 - Ja Su>=31 -2:30 2s -
R a 0 1 - Ja Su>=31 - 2s -
)"};
assert(result.rules.size() == 1);
assert(result.rules[0].second.size() == 10);
assert(result.rules[0].second[0].__at.__time == std::chrono::hours(2));
assert(result.rules[0].second[0].__at.__clock == std::chrono::__tz::__clock::__local);
assert(result.rules[0].second[1].__at.__time == std::chrono::hours(2));
assert(result.rules[0].second[1].__at.__clock == std::chrono::__tz::__clock::__standard);
assert(result.rules[0].second[2].__at.__time ==
std::chrono::hours(1) + std::chrono::minutes(28) + std::chrono::seconds(14));
assert(result.rules[0].second[2].__at.__clock == std::chrono::__tz::__clock::__universal);
assert(result.rules[0].second[3].__at.__time == std::chrono::minutes(19) + std::chrono::seconds(32));
assert(result.rules[0].second[3].__at.__clock == std::chrono::__tz::__clock::__universal);
assert(result.rules[0].second[4].__at.__time == std::chrono::hours(12));
assert(result.rules[0].second[4].__at.__clock == std::chrono::__tz::__clock::__universal);
assert(result.rules[0].second[5].__at.__time == std::chrono::hours(15));
assert(result.rules[0].second[5].__at.__clock == std::chrono::__tz::__clock::__local);
assert(result.rules[0].second[6].__at.__time == std::chrono::hours(24));
assert(result.rules[0].second[6].__at.__clock == std::chrono::__tz::__clock::__local);
assert(result.rules[0].second[7].__at.__time == std::chrono::hours(260));
assert(result.rules[0].second[7].__at.__clock == std::chrono::__tz::__clock::__local);
assert(result.rules[0].second[8].__at.__time == -(std::chrono::hours(2) + std::chrono::minutes(30)));
assert(result.rules[0].second[8].__at.__clock == std::chrono::__tz::__clock::__local);
assert(result.rules[0].second[9].__at.__time == std::chrono::hours(0)); // The man page expresses it in hours
assert(result.rules[0].second[9].__at.__clock == std::chrono::__tz::__clock::__local);
}
static void test_save() {
parse_result result{
R"(
R a 0 1 - Ja Su>=31 1w 2d -
R a 0 1 - Ja Su>=31 1w 2:00s -
R a 0 1 - Ja Su>=31 1w 0 -
R a 0 1 - Ja Su>=31 1w 0:00:01 -
R a 0 1 - Ja Su>=31 1w -0:00:01 -
)"};
assert(result.rules.size() == 1);
assert(result.rules[0].second.size() == 5);
assert(result.rules[0].second[0].__save.__time == std::chrono::hours(2));
assert(result.rules[0].second[0].__save.__is_dst == true);
assert(result.rules[0].second[1].__save.__time == std::chrono::hours(2));
assert(result.rules[0].second[1].__save.__is_dst == false);
assert(result.rules[0].second[2].__save.__time == std::chrono::hours(0));
assert(result.rules[0].second[2].__save.__is_dst == false);
assert(result.rules[0].second[3].__save.__time == std::chrono::seconds(1));
assert(result.rules[0].second[3].__save.__is_dst == true);
assert(result.rules[0].second[4].__save.__time == -std::chrono::seconds(1));
assert(result.rules[0].second[4].__save.__is_dst == true);
}
static void test_letter() {
parse_result result{
R"(
R a 0 1 - Ja Su>=31 1w 2s -
R a 0 1 - Ja Su>=31 1w 2s a
R a 0 1 - Ja Su>=31 1w 2s abc
)"};
assert(result.rules.size() == 1);
assert(result.rules[0].second.size() == 3);
assert(result.rules[0].second[0].__letters == "");
assert(result.rules[0].second[1].__letters == "a");
assert(result.rules[0].second[2].__letters == "abc");
}
int main(int, const char**) {
test_invalid();
test_name();
test_from();
test_to();
test_in();
test_on();
test_at();
test_save();
test_letter();
return 0;
}

View File

@ -0,0 +1,380 @@
//===----------------------------------------------------------------------===//
//
// 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 zones parsing and operations.
// This is not part of the public tzdb interface.
// The test uses private implementation headers.
// ADDITIONAL_COMPILE_FLAGS: -I %S/../../../../../src/include
#include <cassert>
#include <chrono>
#include <fstream>
#include <string>
#include <string_view>
#include <variant>
#include "assert_macros.h"
#include "concat_macros.h"
#include "filesystem_test_helper.h"
#include "test_tzdb.h"
// headers in the dylib
#include "tzdb/types_private.h"
#include "tzdb/tzdb_private.h"
#include "tzdb/time_zone_private.h"
scoped_test_env env;
[[maybe_unused]] const std::filesystem::path dir = env.create_dir("zoneinfo");
const std::filesystem::path file = 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) {
static int version = 0;
std::ofstream f{file};
f << "# version " << version++ << '\n';
f.write(input.data(), input.size());
}
static const std::chrono::tzdb& parse(std::string_view input) {
write(input);
return std::chrono::reload_tzdb();
}
static const std::vector<std::chrono::__tz::__continuation>& continuations(const std::chrono::time_zone& time_zone) {
return time_zone.__implementation().__continuations();
}
static void test_exception(std::string_view input, [[maybe_unused]] std::string_view what) {
write(input);
TEST_VALIDATE_EXCEPTION(
std::runtime_error,
[&]([[maybe_unused]] const std::runtime_error& e) {
TEST_LIBCPP_REQUIRE(
e.what() == what,
TEST_WRITE_CONCATENATED("\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
},
TEST_IGNORE_NODISCARD std::chrono::reload_tzdb());
}
static void test_invalid() {
test_exception("Z", "corrupt tzdb: expected whitespace");
test_exception("Z ", "corrupt tzdb: expected a string");
test_exception("Z n", "corrupt tzdb: expected whitespace");
test_exception("Z n ", "corrupt tzdb: expected a digit");
test_exception("Z n x", "corrupt tzdb: expected a digit");
test_exception("Z n +", "corrupt tzdb: expected a digit");
test_exception("Z n 0", "corrupt tzdb: expected whitespace");
test_exception("Z n 0 ", "corrupt tzdb: expected a string");
test_exception("Z n 0 r", "corrupt tzdb: expected whitespace");
test_exception("Z n 0 r ", "corrupt tzdb: expected a string");
}
static void test_name() {
const std::chrono::tzdb& result = parse(
R"(
Z n 0 r f
)");
assert(result.zones.size() == 1);
assert(result.zones[0].name() == "n");
}
static void test_stdoff() {
const std::chrono::tzdb& result = parse(
R"(
# Based on the examples in the man page.
# Note the input is not expected to have fractional seconds, they are truncated.
Zo na 2 r f
Zon nb 2:00 r f
Zone nc 01:28:14 r f
zONE nd 00:19:32.10 r f
zoNe ne 12:00 r f
Z nf 15:00 r f
Z ng 24:00 r f
Z nh 260:00 r f
Z ni -2:30 r f
Z nj - r f
)");
assert(result.zones.size() == 10);
for (std::size_t i = 0; i < result.zones.size(); ++i)
assert(continuations(result.zones[0]).size() == 1);
assert(continuations(result.zones[0])[0].__stdoff == std::chrono::hours(2));
assert(continuations(result.zones[1])[0].__stdoff == std::chrono::hours(2));
assert(continuations(result.zones[2])[0].__stdoff ==
std::chrono::hours(1) + std::chrono::minutes(28) + std::chrono::seconds(14));
assert(continuations(result.zones[3])[0].__stdoff == std::chrono::minutes(19) + std::chrono::seconds(32));
assert(continuations(result.zones[4])[0].__stdoff == std::chrono::hours(12));
assert(continuations(result.zones[5])[0].__stdoff == std::chrono::hours(15));
assert(continuations(result.zones[6])[0].__stdoff == std::chrono::hours(24));
assert(continuations(result.zones[7])[0].__stdoff == std::chrono::hours(260));
assert(continuations(result.zones[8])[0].__stdoff == -(std::chrono::hours(2) + std::chrono::minutes(30)));
assert(continuations(result.zones[9])[0].__stdoff == std::chrono::hours(0)); // The man page expresses it in hours
}
static void test_rules() {
const std::chrono::tzdb& result = parse(
R"(
Z na 0 - f
Z nb 0 r f
Z nc 0 2d f
Z nd 0 2:00s f
Z ne 0 0 f
Z nf 0 0:00:01 f
Z ng 0 -0:00:01 f
)");
assert(result.zones.size() == 7);
for (std::size_t i = 0; i < result.zones.size(); ++i)
assert(continuations(result.zones[0]).size() == 1);
assert(std::holds_alternative<std::monostate>(continuations(result.zones[0])[0].__rules));
assert(std::get<std::string>(continuations(result.zones[1])[0].__rules) == "r");
assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[2])[0].__rules).__time ==
std::chrono::hours(2));
assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[2])[0].__rules).__is_dst == true);
assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[3])[0].__rules).__time ==
std::chrono::hours(2));
assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[3])[0].__rules).__is_dst == false);
assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[4])[0].__rules).__time ==
std::chrono::hours(0));
assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[4])[0].__rules).__is_dst == false);
assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[5])[0].__rules).__time ==
std::chrono::seconds(1));
assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[5])[0].__rules).__is_dst == true);
assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[6])[0].__rules).__time ==
-std::chrono::seconds(1));
assert(std::get<std::chrono::__tz::__save>(continuations(result.zones[6])[0].__rules).__is_dst == true);
}
static void test_format() {
const std::chrono::tzdb& result = parse(
R"(
Z n 0 r f
)");
assert(result.zones.size() == 1);
assert(continuations(result.zones[0]).size() == 1);
assert(continuations(result.zones[0])[0].__format == "f");
}
static void test_until() {
const std::chrono::tzdb& result = parse(
R"(
Z na 0 r f
Z nb 0 r f 1000
Z nc 0 r f -1000 N
Z nd 0 r f ma S 31
Z ne 0 r f 0 jA LASTw
Z nf 0 r f -42 jUN m<=1
Z ng 0 r f 42 jul Su>=12
Z nh 0 r f 42 JUl 1 2w
Z ni 0 r f 42 July 1 01:28:14u
Z nj 0 r f 42 Jul 1 -
)");
assert(result.zones.size() == 10);
for (std::size_t i = 0; i < result.zones.size(); ++i)
assert(continuations(result.zones[0]).size() == 1);
std::chrono::__tz::__constrained_weekday r;
assert(continuations(result.zones[0])[0].__year == std::chrono::year::min());
assert(continuations(result.zones[0])[0].__in == std::chrono::January);
assert(std::get<std::chrono::day>(continuations(result.zones[0])[0].__on) == std::chrono::day(1));
assert(continuations(result.zones[0])[0].__at.__time == std::chrono::seconds(0));
assert(continuations(result.zones[0])[0].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[1])[0].__year == std::chrono::year(1000));
assert(continuations(result.zones[1])[0].__in == std::chrono::January);
assert(std::get<std::chrono::day>(continuations(result.zones[1])[0].__on) == std::chrono::day(1));
assert(continuations(result.zones[1])[0].__at.__time == std::chrono::seconds(0));
assert(continuations(result.zones[1])[0].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[2])[0].__year == std::chrono::year(-1000));
assert(continuations(result.zones[2])[0].__in == std::chrono::November);
assert(std::get<std::chrono::day>(continuations(result.zones[2])[0].__on) == std::chrono::day(1));
assert(continuations(result.zones[2])[0].__at.__time == std::chrono::seconds(0));
assert(continuations(result.zones[2])[0].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[3])[0].__year == std::chrono::year::max());
assert(continuations(result.zones[3])[0].__in == std::chrono::September);
assert(std::get<std::chrono::day>(continuations(result.zones[3])[0].__on) == std::chrono::day(31));
assert(continuations(result.zones[3])[0].__at.__time == std::chrono::seconds(0));
assert(continuations(result.zones[3])[0].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[4])[0].__year == std::chrono::year(0));
assert(continuations(result.zones[4])[0].__in == std::chrono::January);
assert(std::get<std::chrono::weekday_last>(continuations(result.zones[4])[0].__on) ==
std::chrono::weekday_last{std::chrono::Wednesday});
assert(continuations(result.zones[4])[0].__at.__time == std::chrono::seconds(0));
assert(continuations(result.zones[4])[0].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[5])[0].__year == std::chrono::year(-42));
assert(continuations(result.zones[5])[0].__in == std::chrono::June);
r = std::get<std::chrono::__tz::__constrained_weekday>(continuations(result.zones[5])[0].__on);
assert(r.__weekday == std::chrono::Monday);
assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__le);
assert(r.__day == std::chrono::day(1));
assert(continuations(result.zones[5])[0].__at.__time == std::chrono::seconds(0));
assert(continuations(result.zones[5])[0].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[6])[0].__year == std::chrono::year(42));
assert(continuations(result.zones[6])[0].__in == std::chrono::July);
r = std::get<std::chrono::__tz::__constrained_weekday>(continuations(result.zones[6])[0].__on);
assert(r.__weekday == std::chrono::Sunday);
assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__ge);
assert(r.__day == std::chrono::day(12));
assert(continuations(result.zones[6])[0].__at.__time == std::chrono::seconds(0));
assert(continuations(result.zones[6])[0].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[7])[0].__year == std::chrono::year(42));
assert(continuations(result.zones[7])[0].__in == std::chrono::July);
assert(std::get<std::chrono::day>(continuations(result.zones[7])[0].__on) == std::chrono::day(1));
assert(continuations(result.zones[7])[0].__at.__time == std::chrono::hours(2));
assert(continuations(result.zones[7])[0].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[8])[0].__year == std::chrono::year(42));
assert(continuations(result.zones[8])[0].__in == std::chrono::July);
assert(std::get<std::chrono::day>(continuations(result.zones[8])[0].__on) == std::chrono::day(1));
assert(continuations(result.zones[8])[0].__at.__time ==
std::chrono::hours(1) + std::chrono::minutes(28) + std::chrono::seconds(14));
assert(continuations(result.zones[8])[0].__at.__clock == std::chrono::__tz::__clock::__universal);
assert(continuations(result.zones[9])[0].__year == std::chrono::year(42));
assert(continuations(result.zones[9])[0].__in == std::chrono::July);
assert(std::get<std::chrono::day>(continuations(result.zones[9])[0].__on) == std::chrono::day(1));
assert(continuations(result.zones[9])[0].__at.__time == std::chrono::hours(0)); // The man page expresses it in hours
assert(continuations(result.zones[9])[0].__at.__clock == std::chrono::__tz::__clock::__local);
}
static void test_continuation() {
const std::chrono::tzdb& result = parse(
R"(
Z na 0 r f
0 r f 1000
0 r f -1000 N
0 r f ma S 31
0 r f 0 Ja lastW
0 r f -42 Jun M<=1
0 r f 42 Jul Su>=12
0 r f 42 Jul 1 2w
0 r f 42 Jul 1 01:28:14u
0 r f 42 Jul 1 -
)");
assert(result.zones.size() == 1);
assert(continuations(result.zones[0]).size() == 10);
std::chrono::__tz::__constrained_weekday r;
assert(continuations(result.zones[0])[0].__year == std::chrono::year::min());
assert(continuations(result.zones[0])[0].__in == std::chrono::January);
assert(std::get<std::chrono::day>(continuations(result.zones[0])[0].__on) == std::chrono::day(1));
assert(continuations(result.zones[0])[0].__at.__time == std::chrono::seconds(0));
assert(continuations(result.zones[0])[0].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[0])[1].__year == std::chrono::year(1000));
assert(continuations(result.zones[0])[1].__in == std::chrono::January);
assert(std::get<std::chrono::day>(continuations(result.zones[0])[1].__on) == std::chrono::day(1));
assert(continuations(result.zones[0])[1].__at.__time == std::chrono::seconds(0));
assert(continuations(result.zones[0])[1].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[0])[2].__year == std::chrono::year(-1000));
assert(continuations(result.zones[0])[2].__in == std::chrono::November);
assert(std::get<std::chrono::day>(continuations(result.zones[0])[2].__on) == std::chrono::day(1));
assert(continuations(result.zones[0])[2].__at.__time == std::chrono::seconds(0));
assert(continuations(result.zones[0])[2].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[0])[3].__year == std::chrono::year::max());
assert(continuations(result.zones[0])[3].__in == std::chrono::September);
assert(std::get<std::chrono::day>(continuations(result.zones[0])[3].__on) == std::chrono::day(31));
assert(continuations(result.zones[0])[3].__at.__time == std::chrono::seconds(0));
assert(continuations(result.zones[0])[3].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[0])[4].__year == std::chrono::year(0));
assert(continuations(result.zones[0])[4].__in == std::chrono::January);
assert(std::get<std::chrono::weekday_last>(continuations(result.zones[0])[4].__on) ==
std::chrono::weekday_last{std::chrono::Wednesday});
assert(continuations(result.zones[0])[4].__at.__time == std::chrono::seconds(0));
assert(continuations(result.zones[0])[4].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[0])[5].__year == std::chrono::year(-42));
assert(continuations(result.zones[0])[5].__in == std::chrono::June);
r = std::get<std::chrono::__tz::__constrained_weekday>(continuations(result.zones[0])[5].__on);
assert(r.__weekday == std::chrono::Monday);
assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__le);
assert(r.__day == std::chrono::day(1));
assert(continuations(result.zones[0])[5].__at.__time == std::chrono::seconds(0));
assert(continuations(result.zones[0])[5].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[0])[6].__year == std::chrono::year(42));
assert(continuations(result.zones[0])[6].__in == std::chrono::July);
r = std::get<std::chrono::__tz::__constrained_weekday>(continuations(result.zones[0])[6].__on);
assert(r.__weekday == std::chrono::Sunday);
assert(r.__comparison == std::chrono::__tz::__constrained_weekday::__ge);
assert(r.__day == std::chrono::day(12));
assert(continuations(result.zones[0])[6].__at.__time == std::chrono::seconds(0));
assert(continuations(result.zones[0])[6].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[0])[7].__year == std::chrono::year(42));
assert(continuations(result.zones[0])[7].__in == std::chrono::July);
assert(std::get<std::chrono::day>(continuations(result.zones[0])[7].__on) == std::chrono::day(1));
assert(continuations(result.zones[0])[7].__at.__time == std::chrono::hours(2));
assert(continuations(result.zones[0])[7].__at.__clock == std::chrono::__tz::__clock::__local);
assert(continuations(result.zones[0])[8].__year == std::chrono::year(42));
assert(continuations(result.zones[0])[8].__in == std::chrono::July);
assert(std::get<std::chrono::day>(continuations(result.zones[0])[8].__on) == std::chrono::day(1));
assert(continuations(result.zones[0])[8].__at.__time ==
std::chrono::hours(1) + std::chrono::minutes(28) + std::chrono::seconds(14));
assert(continuations(result.zones[0])[8].__at.__clock == std::chrono::__tz::__clock::__universal);
assert(continuations(result.zones[0])[9].__year == std::chrono::year(42));
assert(continuations(result.zones[0])[9].__in == std::chrono::July);
assert(std::get<std::chrono::day>(continuations(result.zones[0])[9].__on) == std::chrono::day(1));
assert(continuations(result.zones[0])[9].__at.__time == std::chrono::hours(0)); // The man page expresses it in hours
assert(continuations(result.zones[0])[9].__at.__clock == std::chrono::__tz::__clock::__local);
}
int main(int, const char**) {
test_invalid();
test_name();
test_stdoff();
test_rules();
test_format();
test_until();
test_continuation();
return 0;
}

View File

@ -127,6 +127,7 @@ chrono string
chrono string_view chrono string_view
chrono tuple chrono tuple
chrono type_traits chrono type_traits
chrono vector
chrono version chrono version
cinttypes cstdint cinttypes cstdint
cmath limits cmath limits
@ -834,13 +835,17 @@ system_error type_traits
system_error version system_error version
thread array thread array
thread atomic thread atomic
thread cctype
thread cerrno thread cerrno
thread chrono thread chrono
thread clocale
thread compare thread compare
thread cstddef thread cstddef
thread cstdint thread cstdint
thread cstdlib
thread cstring thread cstring
thread ctime thread ctime
thread cwchar
thread functional thread functional
thread iosfwd thread iosfwd
thread limits thread limits

1 algorithm atomic
127 chrono string_view
128 chrono tuple
129 chrono type_traits
130 chrono vector
131 chrono version
132 cinttypes cstdint
133 cmath limits
835 system_error version
836 thread array
837 thread atomic
838 thread cctype
839 thread cerrno
840 thread chrono
841 thread clocale
842 thread compare
843 thread cstddef
844 thread cstdint
845 thread cstdlib
846 thread cstring
847 thread ctime
848 thread cwchar
849 thread functional
850 thread iosfwd
851 thread limits

View File

@ -127,6 +127,7 @@ chrono string
chrono string_view chrono string_view
chrono tuple chrono tuple
chrono type_traits chrono type_traits
chrono vector
chrono version chrono version
cinttypes cstdint cinttypes cstdint
cmath limits cmath limits

1 algorithm atomic
127 chrono string_view
128 chrono tuple
129 chrono type_traits
130 chrono vector
131 chrono version
132 cinttypes cstdint
133 cmath limits

View File

@ -127,6 +127,7 @@ chrono string
chrono string_view chrono string_view
chrono tuple chrono tuple
chrono type_traits chrono type_traits
chrono vector
chrono version chrono version
cinttypes cstdint cinttypes cstdint
cmath limits cmath limits

1 algorithm atomic
127 chrono string_view
128 chrono tuple
129 chrono type_traits
130 chrono vector
131 chrono version
132 cinttypes cstdint
133 cmath limits

View File

@ -127,6 +127,7 @@ chrono string
chrono string_view chrono string_view
chrono tuple chrono tuple
chrono type_traits chrono type_traits
chrono vector
chrono version chrono version
cinttypes cstdint cinttypes cstdint
cmath limits cmath limits

1 algorithm atomic
127 chrono string_view
128 chrono tuple
129 chrono type_traits
130 chrono vector
131 chrono version
132 cinttypes cstdint
133 cmath limits

View File

@ -133,6 +133,7 @@ chrono string
chrono string_view chrono string_view
chrono tuple chrono tuple
chrono type_traits chrono type_traits
chrono vector
chrono version chrono version
cinttypes cstdint cinttypes cstdint
cmath limits cmath limits

1 algorithm atomic
133 chrono string_view
134 chrono tuple
135 chrono type_traits
136 chrono vector
137 chrono version
138 cinttypes cstdint
139 cmath limits

View File

@ -87,6 +87,7 @@ chrono stdexcept
chrono string chrono string
chrono string_view chrono string_view
chrono tuple chrono tuple
chrono vector
chrono version chrono version
cinttypes cstdint cinttypes cstdint
cmath limits cmath limits

1 algorithm climits
87 chrono string
88 chrono string_view
89 chrono tuple
90 chrono vector
91 chrono version
92 cinttypes cstdint
93 cmath limits

View File

@ -87,6 +87,7 @@ chrono stdexcept
chrono string chrono string
chrono string_view chrono string_view
chrono tuple chrono tuple
chrono vector
chrono version chrono version
cinttypes cstdint cinttypes cstdint
cmath limits cmath limits

1 algorithm climits
87 chrono string
88 chrono string_view
89 chrono tuple
90 chrono vector
91 chrono version
92 cinttypes cstdint
93 cmath limits

View File

@ -12,16 +12,16 @@
// XFAIL: libcpp-has-no-incomplete-tzdb // XFAIL: libcpp-has-no-incomplete-tzdb
// XFAIL: availability-tzdb-missing // XFAIL: availability-tzdb-missing
// TODO TZDB Enable tests // TODO TZDB (#81654) Enable tests
// UNSUPPORTED: c++20, c++23, c++26 // UNSUPPORTED: c++20, c++23, c++26
// <chrono> // <chrono>
// const tzdb& get_tzdb(); // const tzdb& get_tzdb();
#include <chrono> #include <algorithm>
#include <cassert> #include <cassert>
#include <chrono>
#include "test_macros.h" #include "test_macros.h"
@ -30,5 +30,15 @@ int main(int, const char**) {
assert(!db.version.empty()); assert(!db.version.empty());
LIBCPP_ASSERT(!db.__rules.empty());
assert(!db.zones.empty());
assert(std::ranges::is_sorted(db.zones));
assert(std::ranges::adjacent_find(db.zones) == db.zones.end()); // is unique?
assert(!db.links.empty());
assert(std::ranges::is_sorted(db.links));
assert(std::ranges::adjacent_find(db.links) == db.links.end()); // is unique?
return 0; return 0;
} }

View File

@ -12,7 +12,7 @@
// XFAIL: libcpp-has-no-incomplete-tzdb // XFAIL: libcpp-has-no-incomplete-tzdb
// XFAIL: availability-tzdb-missing // XFAIL: availability-tzdb-missing
// TODO TZDB Enable tests // TODO TZDB (#81654) Enable tests
// UNSUPPORTED: c++20, c++23, c++26 // UNSUPPORTED: c++20, c++23, c++26
// <chrono> // <chrono>

View File

@ -12,7 +12,7 @@
// XFAIL: libcpp-has-no-incomplete-tzdb // XFAIL: libcpp-has-no-incomplete-tzdb
// XFAIL: availability-tzdb-missing // XFAIL: availability-tzdb-missing
// TODO TZDB Enable tests // TODO TZDB (#81654) Enable tests
// UNSUPPORTED: c++20, c++23, c++26 // UNSUPPORTED: c++20, c++23, c++26
// <chrono> // <chrono>

View File

@ -12,7 +12,7 @@
// XFAIL: libcpp-has-no-incomplete-tzdb // XFAIL: libcpp-has-no-incomplete-tzdb
// XFAIL: availability-tzdb-missing // XFAIL: availability-tzdb-missing
// TODO TZDB Enable tests // TODO TZDB (#81654) Enable tests
// UNSUPPORTED: c++20, c++23, c++26 // UNSUPPORTED: c++20, c++23, c++26
// <chrono> // <chrono>

View File

@ -12,7 +12,7 @@
// XFAIL: libcpp-has-no-incomplete-tzdb // XFAIL: libcpp-has-no-incomplete-tzdb
// XFAIL: availability-tzdb-missing // XFAIL: availability-tzdb-missing
// TODO TZDB Enable tests // TODO TZDB (#81654) Enable tests
// UNSUPPORTED: c++20, c++23, c++26 // UNSUPPORTED: c++20, c++23, c++26
// <chrono> // <chrono>

View File

@ -12,7 +12,7 @@
// XFAIL: libcpp-has-no-incomplete-tzdb // XFAIL: libcpp-has-no-incomplete-tzdb
// XFAIL: availability-tzdb-missing // XFAIL: availability-tzdb-missing
// TODO TZDB Enable tests // TODO TZDB (#81654) Enable tests
// UNSUPPORTED: c++20, c++23, c++26 // UNSUPPORTED: c++20, c++23, c++26
// <chrono> // <chrono>

View File

@ -37,7 +37,11 @@ int main(int, const char**) {
tzdb.version = "version"; tzdb.version = "version";
assert(tzdb.version == "version"); assert(tzdb.version == "version");
// TODO TZDB add the other data members [[maybe_unused]] std::vector<std::chrono::time_zone>& zones = tzdb.zones;
[[maybe_unused]] std::vector<std::chrono::time_zone_link>& links = tzdb.links;
// TODO TZDB add the leap data member
return 0; return 0;
} }

View File

@ -0,0 +1,38 @@
//===----------------------------------------------------------------------===//
//
// 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
// TODO TZDB (#81654) Enable tests
// UNSUPPORTED: c++20, c++23, c++26
// <chrono>
// class time_zone_link;
// string_view name() const noexcept;
#include <cassert>
#include <chrono>
#include "test_macros.h"
int main(int, const char**) {
const std::chrono::tzdb& tzdb = std::chrono::get_tzdb();
assert(tzdb.links.size() > 1);
std::same_as<std::string_view> auto name = tzdb.links[0].name();
static_assert(noexcept(tzdb.links[0].name()));
assert(name != tzdb.links[1].name());
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
// TODO TZDB (#81654) Enable tests
// UNSUPPORTED: c++20, c++23, c++26
// <chrono>
// class time_zone_link;
// string_view target() const noexcept;
#include <cassert>
#include <chrono>
#include "test_macros.h"
int main(int, const char**) {
const std::chrono::tzdb& tzdb = std::chrono::get_tzdb();
assert(tzdb.links.size() > 1);
[[maybe_unused]] std::same_as<std::string_view> auto _ = tzdb.links[0].target();
static_assert(noexcept(tzdb.links[0].target()));
return 0;
}

View File

@ -0,0 +1,39 @@
//===----------------------------------------------------------------------===//
//
// 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
// TODO TZDB (#81654) Enable tests
// UNSUPPORTED: c++20, c++23, c++26
// <chrono>
// bool operator==(const time_zone_link& x, const time_zone_link& y) noexcept;
// strong_ordering operator<=>(const time_zone_link& x, const time_zone_link& y) noexcept;
#include <chrono>
#include <cassert>
#include "test_macros.h"
#include "test_comparisons.h"
int main(int, const char**) {
const std::chrono::tzdb& tzdb = std::chrono::get_tzdb();
assert(tzdb.links.size() > 2);
AssertOrderAreNoexcept<std::chrono::time_zone_link>();
AssertOrderReturn<std::strong_ordering, std::chrono::time_zone_link>();
assert(testOrder(tzdb.links[0], tzdb.links[1], std::strong_ordering::less));
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 time_zone_link
// {
// time_zone_link(time_zone_link&&) = default;
// time_zone_link& operator=(time_zone_link&&) = default;
//
// ...
// };
#include <chrono>
#include <concepts>
#include <type_traits>
#include "test_macros.h"
// It's impossible to actually obtain a non-const reference to a
// time_zone_link, and as a result the move constructor can never be exercised
// in runtime code. We still check the property pedantically.
LIBCPP_STATIC_ASSERT(!std::copy_constructible<std::chrono::time_zone_link>);
LIBCPP_STATIC_ASSERT(!std::is_copy_assignable_v<std::chrono::time_zone_link>);
static_assert(std::move_constructible<std::chrono::time_zone_link>);
static_assert(std::is_move_assignable_v<std::chrono::time_zone_link>);

View File

@ -0,0 +1,38 @@
//===----------------------------------------------------------------------===//
//
// 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
// TODO TZDB (#81654) Enable tests
// UNSUPPORTED: c++20, c++23, c++26
// <chrono>
// class time_zone;
// string_view name() const noexcept;
#include <cassert>
#include <chrono>
#include "test_macros.h"
int main(int, const char**) {
const std::chrono::tzdb& tzdb = std::chrono::get_tzdb();
assert(tzdb.zones.size() > 1);
[[maybe_unused]] std::same_as<std::string_view> auto _ = tzdb.zones[0].name();
static_assert(noexcept(tzdb.zones[0].name()));
assert(tzdb.zones[0].name() != tzdb.zones[1].name());
return 0;
}

View File

@ -0,0 +1,39 @@
//===----------------------------------------------------------------------===//
//
// 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
// TODO TZDB (#81654) Enable tests
// UNSUPPORTED: c++20, c++23, c++26
// <chrono>
// bool operator==(const time_zone& x, const time_zone& y) noexcept;
// strong_ordering operator<=>(const time_zone& x, const time_zone& y) noexcept;
#include <chrono>
#include <cassert>
#include "test_macros.h"
#include "test_comparisons.h"
int main(int, const char**) {
const std::chrono::tzdb& tzdb = std::chrono::get_tzdb();
assert(tzdb.zones.size() > 2);
AssertOrderAreNoexcept<std::chrono::time_zone>();
AssertOrderReturn<std::strong_ordering, std::chrono::time_zone>();
assert(testOrder(tzdb.zones[0], tzdb.zones[1], std::strong_ordering::less));
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 time_zone
// {
// time_zone(time_zone&&) = default;
// time_zone& operator=(time_zone&&) = default;
//
// ...
// };
#include <chrono>
#include <concepts>
#include <type_traits>
#include "test_macros.h"
// It's impossible to actually obtain a non-const reference to a time_zone, and
// as a result the move constructor can never be exercised in runtime code. We
// still check the property pedantically.
LIBCPP_STATIC_ASSERT(!std::copy_constructible<std::chrono::time_zone>);
LIBCPP_STATIC_ASSERT(!std::is_copy_assignable_v<std::chrono::time_zone>);
static_assert(std::move_constructible<std::chrono::time_zone>);
static_assert(std::is_move_assignable_v<std::chrono::time_zone>);