[libc++][TZDB] Implements zoned_time formatters. (#98347)

Implements parts of:
- P0355 Extending to chrono Calendars and Time Zones
- P1361 Integration of chrono with text formatting
- P2372 Fixing locale handling in chrono formatters
This commit is contained in:
Mark de Wever 2024-07-20 19:24:41 +02:00 committed by GitHub
parent 4d8e42ea6a
commit afbfb16d29
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 1434 additions and 9 deletions

View File

@ -7,7 +7,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::gps_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::file_time<Duration>``",,Mark de Wever,|Complete|,17.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_time<Duration>``",,Mark de Wever,|Complete|,17.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local-time-format-t<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local-time-format-t<Duration>``",,,|Nothing To Do|,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::day``",,Mark de Wever,|Complete|,16.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::month``",,Mark de Wever,|Complete|,16.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year``",,Mark de Wever,|Complete|,16.0
@ -26,7 +26,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::hh_mm_ss<duration<Rep, Period>>``",,Mark de Wever,|Complete|,17.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_info``",,Mark de Wever,|Complete|,19.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_info``",,Mark de Wever,|Complete|,19.0
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::zoned_time<Duration, TimeZonePtr>``",A ``<chrono>`` implementation,Mark de Wever,,
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::zoned_time<Duration, TimeZonePtr>``",,Mark de Wever,|Complete|,19.0
"`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``"
`[thread.thread.id] <https://wg21.link/thread.thread.id>`_,"Formatting ``thread::id``",,Mark de Wever,|Complete|,17.0

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

View File

@ -29,11 +29,13 @@
#include <__chrono/year_month.h>
#include <__chrono/year_month_day.h>
#include <__chrono/year_month_weekday.h>
#include <__chrono/zoned_time.h>
#include <__concepts/same_as.h>
#include <__config>
#include <__format/format_error.h>
#include <__memory/addressof.h>
#include <__type_traits/is_convertible.h>
#include <__type_traits/is_specialization.h>
#include <cstdint>
#include <ctime>
#include <limits>
@ -178,7 +180,13 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
// Has no time information.
} else if constexpr (same_as<_ChronoT, chrono::local_info>) {
// Has no time information.
# endif
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
} else if constexpr (__is_specialization_v<_ChronoT, chrono::zoned_time>) {
return std::__convert_to_tm<_Tm>(
chrono::sys_time<typename _ChronoT::duration>{__value.get_local_time().time_since_epoch()});
# endif
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
} else
static_assert(sizeof(_ChronoT) == 0, "Add the missing type specialization");

View File

@ -33,6 +33,7 @@
#include <__chrono/year_month.h>
#include <__chrono/year_month_day.h>
#include <__chrono/year_month_weekday.h>
#include <__chrono/zoned_time.h>
#include <__concepts/arithmetic.h>
#include <__concepts/same_as.h>
#include <__config>
@ -44,6 +45,7 @@
#include <__format/parser_std_format_spec.h>
#include <__format/write_escaped.h>
#include <__memory/addressof.h>
#include <__type_traits/is_specialization.h>
#include <cmath>
#include <ctime>
#include <limits>
@ -137,10 +139,24 @@ __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::hh_mm_ss<
__value.fractional_width);
}
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB) && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && \
!defined(_LIBCPP_HAS_NO_FILESYSTEM) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
template <class _CharT, class _Duration, class _TimeZonePtr>
_LIBCPP_HIDE_FROM_ABI void
__format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::zoned_time<_Duration, _TimeZonePtr>& __value) {
__formatter::__format_sub_seconds(__sstr, __value.get_local_time().time_since_epoch());
}
# endif
template <class _Tp>
consteval bool __use_fraction() {
if constexpr (__is_time_point<_Tp>)
return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB) && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && \
!defined(_LIBCPP_HAS_NO_FILESYSTEM) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;
# endif
else if constexpr (chrono::__is_duration<_Tp>::value)
return chrono::hh_mm_ss<_Tp>::fractional_width;
else if constexpr (__is_hh_mm_ss<_Tp>)
@ -212,8 +228,13 @@ _LIBCPP_HIDE_FROM_ABI __time_zone __convert_to_time_zone([[maybe_unused]] const
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
if constexpr (same_as<_Tp, chrono::sys_info>)
return {__value.abbrev, __value.offset};
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
return __formatter::__convert_to_time_zone(__value.get_info());
# endif
else
# endif
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
return {"UTC", chrono::seconds{0}};
}
@ -426,7 +447,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {
return true;
else if constexpr (same_as<_Tp, chrono::local_info>)
return true;
# endif
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
return true;
# endif
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
@ -472,7 +498,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {
return true;
else if constexpr (same_as<_Tp, chrono::local_info>)
return true;
# endif
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
return true;
# endif
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
@ -518,7 +549,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {
return true;
else if constexpr (same_as<_Tp, chrono::local_info>)
return true;
# endif
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
return true;
# endif
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
@ -564,7 +600,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {
return true;
else if constexpr (same_as<_Tp, chrono::local_info>)
return true;
# endif
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
return true;
# endif
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
else
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
}
@ -924,7 +965,23 @@ public:
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags{});
}
};
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
// Note due to how libc++'s formatters are implemented there is no need to add
// the exposition only local-time-format-t abstraction.
template <class _Duration, class _TimeZonePtr, __fmt_char_type _CharT>
struct formatter<chrono::zoned_time<_Duration, _TimeZonePtr>, _CharT> : public __formatter_chrono<_CharT> {
public:
using _Base = __formatter_chrono<_CharT>;
template <class _ParseContext>
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
}
};
# endif // !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&
// !defined(_LIBCPP_HAS_NO_LOCALIZATION)
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
#endif // if _LIBCPP_STD_VER >= 20

View File

@ -27,6 +27,7 @@
#include <__chrono/year_month.h>
#include <__chrono/year_month_day.h>
#include <__chrono/year_month_weekday.h>
#include <__chrono/zoned_time.h>
#include <__concepts/same_as.h>
#include <__config>
#include <__format/format_functions.h>
@ -302,6 +303,14 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const local_info& __info) {
_LIBCPP_STATICALLY_WIDEN(_CharT, "{}: {{{}, {}}}"), __result(), __info.first, __info.second);
}
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
template <class _CharT, class _Traits, class _Duration, class _TimeZonePtr>
_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
operator<<(basic_ostream<_CharT, _Traits>& __os, const zoned_time<_Duration, _TimeZonePtr>& __tp) {
return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%F %T %Z}"), __tp);
}
# endif
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
} // namespace chrono

View File

@ -799,6 +799,11 @@ template<class Duration1, class Duration2, class TimeZonePtr>
bool operator==(const zoned_time<Duration1, TimeZonePtr>& x,
const zoned_time<Duration2, TimeZonePtr>& y);
template<class charT, class traits, class Duration, class TimeZonePtr> // C++20
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>& os,
const zoned_time<Duration, TimeZonePtr>& t);
// [time.zone.leap], leap second support
class leap_second { // C++20
public:
@ -881,6 +886,8 @@ namespace std {
struct formatter<chrono::hh_mm_ss<duration<Rep, Period>>, charT>; // C++20
template<class charT> struct formatter<chrono::sys_info, charT>; // C++20
template<class charT> struct formatter<chrono::local_info, charT>; // C++20
template<class Duration, class TimeZonePtr, class charT> // C++20
struct formatter<chrono::zoned_time<Duration, TimeZonePtr>, charT>;
} // namespace std
namespace chrono {

View File

@ -0,0 +1,974 @@
//===----------------------------------------------------------------------===//
// 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
// TODO FMT This test should not require std::to_chars(floating-point)
// XFAIL: availability-fp_to_chars-missing
// XFAIL: libcpp-has-no-experimental-tzdb
// REQUIRES: locale.fr_FR.UTF-8
// REQUIRES: locale.ja_JP.UTF-8
// <chrono>
//
// template<class Duration, class TimeZonePtr, class charT>
// struct formatter<chrono::zoned_time<Duration, TimeZonePtr>, charT>
#include <chrono>
#include <format>
#include <cassert>
#include <concepts>
#include <locale>
#include <iostream>
#include <type_traits>
#include "formatter_tests.h"
#include "make_string.h"
#include "platform_support.h" // locale name macros
#include "test_macros.h"
template <class CharT>
static void test_no_chrono_specs() {
using namespace std::literals::chrono_literals;
check(SV("1970-01-01 01:00:00.000000042 +01"),
SV("{}"),
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::nanoseconds>{42ns}));
check(SV("1970-01-01 01:00:00.000042 +01"),
SV("{}"),
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::microseconds>{42us}));
check(SV("1970-01-01 01:00:00.042 +01"),
SV("{}"),
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::milliseconds>{42ms}));
check(SV("1970-01-01 01:00:42 +01"),
SV("{}"),
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::seconds>{42s}));
check(SV("1970-02-12 01:00:00 +01"),
SV("{}"),
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::days>{std::chrono::days{42}}));
check(SV("1970-10-22 01:00:00 +01"),
SV("{}"),
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::weeks>{std::chrono::weeks{42}}));
}
template <class CharT>
static void test_valid_values_year() {
using namespace std::literals::chrono_literals;
constexpr std::basic_string_view<CharT> fmt =
SV("{:%%C='%C'%t%%EC='%EC'%t%%y='%y'%t%%Oy='%Oy'%t%%Ey='%Ey'%t%%Y='%Y'%t%%EY='%EY'%n}");
constexpr std::basic_string_view<CharT> lfmt =
SV("{:L%%C='%C'%t%%EC='%EC'%t%%y='%y'%t%%Oy='%Oy'%t%%Ey='%Ey'%t%%Y='%Y'%t%%EY='%EY'%n}");
const std::locale loc(LOCALE_ja_JP_UTF_8);
std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
// Non localized output using C-locale
check(SV("%C='19'\t%EC='19'\t%y='70'\t%Oy='70'\t%Ey='70'\t%Y='1970'\t%EY='1970'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%C='20'\t%EC='20'\t%y='09'\t%Oy='09'\t%Ey='09'\t%Y='2009'\t%EY='2009'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
// Use the global locale (fr_FR)
check(SV("%C='19'\t%EC='19'\t%y='70'\t%Oy='70'\t%Ey='70'\t%Y='1970'\t%EY='1970'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%C='20'\t%EC='20'\t%y='09'\t%Oy='09'\t%Ey='09'\t%Y='2009'\t%EY='2009'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
// Use supplied locale (ja_JP). This locale has a different alternate.
#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
check(loc,
SV("%C='19'\t%EC='19'\t%y='70'\t%Oy='70'\t%Ey='70'\t%Y='1970'\t%EY='1970'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%C='20'\t%EC='20'\t%y='09'\t%Oy='09'\t%Ey='09'\t%Y='2009'\t%EY='2009'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX)|| defined(__FreeBSD__)
check(loc,
SV("%C='19'\t%EC='昭和'\t%y='70'\t%Oy='七十'\t%Ey='45'\t%Y='1970'\t%EY='昭和45年'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%C='20'\t%EC='平成'\t%y='09'\t%Oy='九'\t%Ey='21'\t%Y='2009'\t%EY='平成21年'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX)|| defined(__FreeBSD__)
std::locale::global(std::locale::classic());
}
template <class CharT>
static void test_valid_values_month() {
using namespace std::literals::chrono_literals;
constexpr std::basic_string_view<CharT> fmt = SV("{:%%b='%b'%t%%h='%h'%t%%B='%B'%t%%m='%m'%t%%Om='%Om'%n}");
constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%b='%b'%t%%h='%h'%t%%B='%B'%t%%m='%m'%t%%Om='%Om'%n}");
const std::locale loc(LOCALE_ja_JP_UTF_8);
std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
// Non localized output using C-locale
check(SV("%b='Jan'\t%h='Jan'\t%B='January'\t%m='01'\t%Om='01'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%b='May'\t%h='May'\t%B='May'\t%m='05'\t%Om='05'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{2'000'000'000s})); // 03:33:20 UTC on Wednesday, 18 May 2033
// Use the global locale (fr_FR)
#if defined(__APPLE__)
check(SV("%b='jan'\t%h='jan'\t%B='janvier'\t%m='01'\t%Om='01'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
#else
check(SV("%b='janv.'\t%h='janv.'\t%B='janvier'\t%m='01'\t%Om='01'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
#endif
check(SV("%b='mai'\t%h='mai'\t%B='mai'\t%m='05'\t%Om='05'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{2'000'000'000s})); // 03:33:20 UTC on Wednesday, 18 May 2033
// Use supplied locale (ja_JP). This locale has a different alternate.
#ifdef _WIN32
check(loc,
SV("%b='1'\t%h='1'\t%B='1月'\t%m='01'\t%Om='01'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%b='5'\t%h='5'\t%B='5月'\t%m='05'\t%Om='05'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{2'000'000'000s})); // 03:33:20 UTC on Wednesday, 18 May 2033
#elif defined(_AIX) // _WIN32
check(loc,
SV("%b='1月'\t%h='1月'\t%B='1月'\t%m='01'\t%Om='01'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%b='5月'\t%h='5月'\t%B='5月'\t%m='05'\t%Om='05'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{2'000'000'000s})); // 03:33:20 UTC on Wednesday, 18 May 2033
#elif defined(__APPLE__) // _WIN32
check(loc,
SV("%b=' 1'\t%h=' 1'\t%B='1月'\t%m='01'\t%Om='01'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%b=' 5'\t%h=' 5'\t%B='5月'\t%m='05'\t%Om='05'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{2'000'000'000s})); // 03:33:20 UTC on Wednesday, 18 May 2033
#elif defined(__FreeBSD__) // _WIN32
check(loc,
SV("%b=' 1月'\t%h=' 1月'\t%B='1月'\t%m='01'\t%Om='01'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%b=' 5月'\t%h=' 5月'\t%B='5月'\t%m='05'\t%Om='05'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{2'000'000'000s})); // 03:33:20 UTC on Wednesday, 18 May 2033
#else // _WIN32
check(loc,
SV("%b=' 1月'\t%h=' 1月'\t%B='1月'\t%m='01'\t%Om='一'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%b=' 5月'\t%h=' 5月'\t%B='5月'\t%m='05'\t%Om='五'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{2'000'000'000s})); // 03:33:20 UTC on Wednesday, 18 May 2033
#endif // _WIN32
std::locale::global(std::locale::classic());
}
template <class CharT>
static void test_valid_values_day() {
using namespace std::literals::chrono_literals;
constexpr std::basic_string_view<CharT> fmt = SV("{:%%d='%d'%t%%Od='%Od'%t%%e='%e'%t%%Oe='%Oe'%n}");
constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%d='%d'%t%%Od='%Od'%t%%e='%e'%t%%Oe='%Oe'%n}");
const std::locale loc(LOCALE_ja_JP_UTF_8);
std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
// Non localized output using C-locale
check(SV("%d='01'\t%Od='01'\t%e=' 1'\t%Oe=' 1'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%d='13'\t%Od='13'\t%e='13'\t%Oe='13'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
// Use the global locale (fr_FR)
check(SV("%d='01'\t%Od='01'\t%e=' 1'\t%Oe=' 1'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%d='13'\t%Od='13'\t%e='13'\t%Oe='13'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
// Use supplied locale (ja_JP). This locale has a different alternate.
#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
check(loc,
SV("%d='01'\t%Od='01'\t%e=' 1'\t%Oe=' 1'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%d='13'\t%Od='13'\t%e='13'\t%Oe='13'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
check(loc,
SV("%d='01'\t%Od='一'\t%e=' 1'\t%Oe='一'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%d='13'\t%Od='十三'\t%e='13'\t%Oe='十三'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
std::locale::global(std::locale::classic());
}
template <class CharT>
static void test_valid_values_weekday() {
using namespace std::literals::chrono_literals;
constexpr std::basic_string_view<CharT> fmt =
SV("{:%%a='%a'%t%%A='%A'%t%%u='%u'%t%%Ou='%Ou'%t%%w='%w'%t%%Ow='%Ow'%n}");
constexpr std::basic_string_view<CharT> lfmt =
SV("{:L%%a='%a'%t%%A='%A'%t%%u='%u'%t%%Ou='%Ou'%t%%w='%w'%t%%Ow='%Ow'%n}");
const std::locale loc(LOCALE_ja_JP_UTF_8);
std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
// Non localized output using C-locale
check(SV("%a='Thu'\t%A='Thursday'\t%u='4'\t%Ou='4'\t%w='4'\t%Ow='4'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%a='Sun'\t%A='Sunday'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{4'294'967'295s})); // 06:28:15 UTC on Sunday, 7 February 2106
// Use the global locale (fr_FR)
#if defined(__APPLE__)
check(SV("%a='Jeu'\t%A='Jeudi'\t%u='4'\t%Ou='4'\t%w='4'\t%Ow='4'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%a='Dim'\t%A='Dimanche'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{4'294'967'295s})); // 06:28:15 UTC on Sunday, 7 February 2106
#else
check(SV("%a='jeu.'\t%A='jeudi'\t%u='4'\t%Ou='4'\t%w='4'\t%Ow='4'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%a='dim.'\t%A='dimanche'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{4'294'967'295s})); // 06:28:15 UTC on Sunday, 7 February 2106
#endif
// Use supplied locale (ja_JP).
// This locale has a different alternate, but not on all platforms
#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
check(loc,
SV("%a='木'\t%A='木曜日'\t%u='4'\t%Ou='4'\t%w='4'\t%Ow='4'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%a='日'\t%A='日曜日'\t%u='7'\t%Ou='7'\t%w='0'\t%Ow='0'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{4'294'967'295s})); // 06:28:15 UTC on Sunday, 7 February 2106
#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
check(loc,
SV("%a='木'\t%A='木曜日'\t%u='4'\t%Ou='四'\t%w='4'\t%Ow='四'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%a='日'\t%A='日曜日'\t%u='7'\t%Ou='七'\t%w='0'\t%Ow=''\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{4'294'967'295s})); // 06:28:15 UTC on Sunday, 7 February 2106
#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
std::locale::global(std::locale::classic());
}
template <class CharT>
static void test_valid_values_day_of_year() {
using namespace std::literals::chrono_literals;
constexpr std::basic_string_view<CharT> fmt = SV("{:%%j='%j'%n}");
constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%j='%j'%n}");
const std::locale loc(LOCALE_ja_JP_UTF_8);
std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
// Non localized output using C-locale
check(SV("%j='001'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%j='138'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{2'000'000'000s})); // 03:33:20 UTC on Wednesday, 18 May 2033
// Use the global locale (fr_FR)
check(SV("%j='001'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%j='138'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{2'000'000'000s})); // 03:33:20 UTC on Wednesday, 18 May 2033
// Use supplied locale (ja_JP). This locale has a different alternate.
check(loc,
SV("%j='001'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%j='138'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{2'000'000'000s})); // 03:33:20 UTC on Wednesday, 18 May 2033
std::locale::global(std::locale::classic());
}
template <class CharT>
static void test_valid_values_week() {
using namespace std::literals::chrono_literals;
constexpr std::basic_string_view<CharT> fmt = SV("{:%%U='%U'%t%%OU='%OU'%t%%W='%W'%t%%OW='%OW'%n}");
constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%U='%U'%t%%OU='%OU'%t%%W='%W'%t%%OW='%OW'%n}");
const std::locale loc(LOCALE_ja_JP_UTF_8);
std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
// Non localized output using C-locale
check(SV("%U='00'\t%OU='00'\t%W='00'\t%OW='00'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%U='20'\t%OU='20'\t%W='20'\t%OW='20'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{2'000'000'000s})); // 03:33:20 UTC on Wednesday, 18 May 2033
// Use the global locale (fr_FR)
check(SV("%U='00'\t%OU='00'\t%W='00'\t%OW='00'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%U='20'\t%OU='20'\t%W='20'\t%OW='20'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{2'000'000'000s})); // 03:33:20 UTC on Wednesday, 18 May 2033
// Use supplied locale (ja_JP). This locale has a different alternate.
#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
check(loc,
SV("%U='00'\t%OU='00'\t%W='00'\t%OW='00'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%U='20'\t%OU='20'\t%W='20'\t%OW='20'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{2'000'000'000s})); // 03:33:20 UTC on Wednesday, 18 May 2033
#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
check(loc,
SV("%U='00'\t%OU=''\t%W='00'\t%OW=''\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%U='20'\t%OU='二十'\t%W='20'\t%OW='二十'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{2'000'000'000s})); // 03:33:20 UTC on Wednesday, 18 May 2033
#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
std::locale::global(std::locale::classic());
}
template <class CharT>
static void test_valid_values_iso_8601_week() {
using namespace std::literals::chrono_literals;
constexpr std::basic_string_view<CharT> fmt = SV("{:%%g='%g'%t%%G='%G'%t%%V='%V'%t%%OV='%OV'%n}");
constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%g='%g'%t%%G='%G'%t%%V='%V'%t%%OV='%OV'%n}");
const std::locale loc(LOCALE_ja_JP_UTF_8);
std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
// Non localized output using C-locale
check(SV("%g='70'\t%G='1970'\t%V='01'\t%OV='01'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%g='09'\t%G='2009'\t%V='07'\t%OV='07'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
// Use the global locale (fr_FR)
check(SV("%g='70'\t%G='1970'\t%V='01'\t%OV='01'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%g='09'\t%G='2009'\t%V='07'\t%OV='07'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
// Use supplied locale (ja_JP). This locale has a different alternate.
#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
check(loc,
SV("%g='70'\t%G='1970'\t%V='01'\t%OV='01'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%g='09'\t%G='2009'\t%V='07'\t%OV='07'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
check(loc,
SV("%g='70'\t%G='1970'\t%V='01'\t%OV='一'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%g='09'\t%G='2009'\t%V='07'\t%OV='七'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
std::locale::global(std::locale::classic());
}
template <class CharT>
static void test_valid_values_date() {
using namespace std::literals::chrono_literals;
constexpr std::basic_string_view<CharT> fmt = SV("{:%%D='%D'%t%%F='%F'%t%%x='%x'%t%%Ex='%Ex'%n}");
constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%D='%D'%t%%F='%F'%t%%x='%x'%t%%Ex='%Ex'%n}");
const std::locale loc(LOCALE_ja_JP_UTF_8);
std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
// Non localized output using C-locale
check(SV("%D='01/01/70'\t%F='1970-01-01'\t%x='01/01/70'\t%Ex='01/01/70'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%D='02/13/09'\t%F='2009-02-13'\t%x='02/13/09'\t%Ex='02/13/09'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
// Use the global locale (fr_FR)
#if defined(__APPLE__) || defined(__FreeBSD__)
check(SV("%D='01/01/70'\t%F='1970-01-01'\t%x='01.01.1970'\t%Ex='01.01.1970'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%D='02/13/09'\t%F='2009-02-13'\t%x='13.02.2009'\t%Ex='13.02.2009'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
#else
check(SV("%D='01/01/70'\t%F='1970-01-01'\t%x='01/01/1970'\t%Ex='01/01/1970'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%D='02/13/09'\t%F='2009-02-13'\t%x='13/02/2009'\t%Ex='13/02/2009'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
#endif
// Use supplied locale (ja_JP). This locale has a different alternate.
#if defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
check(loc,
SV("%D='01/01/70'\t%F='1970-01-01'\t%x='1970/01/01'\t%Ex='1970/01/01'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%D='02/13/09'\t%F='2009-02-13'\t%x='2009/02/13'\t%Ex='2009/02/13'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
#else // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
check(loc,
SV("%D='01/01/70'\t%F='1970-01-01'\t%x='1970年01月01日'\t%Ex='昭和45年01月01日'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%D='02/13/09'\t%F='2009-02-13'\t%x='2009年02月13日'\t%Ex='平成21年02月13日'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
#endif // defined(_WIN32) || defined(__APPLE__) || defined(_AIX) || defined(__FreeBSD__)
std::locale::global(std::locale::classic());
}
template <class CharT>
static void test_valid_values_time() {
using namespace std::literals::chrono_literals;
constexpr std::basic_string_view<CharT> fmt = SV(
"{:"
"%%H='%H'%t"
"%%OH='%OH'%t"
"%%I='%I'%t"
"%%OI='%OI'%t"
"%%M='%M'%t"
"%%OM='%OM'%t"
"%%S='%S'%t"
"%%OS='%OS'%t"
"%%p='%p'%t"
"%%R='%R'%t"
"%%T='%T'%t"
"%%r='%r'%t"
"%%X='%X'%t"
"%%EX='%EX'%t"
"%n}");
constexpr std::basic_string_view<CharT> lfmt = SV(
"{:L"
"%%H='%H'%t"
"%%OH='%OH'%t"
"%%I='%I'%t"
"%%OI='%OI'%t"
"%%M='%M'%t"
"%%OM='%OM'%t"
"%%S='%S'%t"
"%%OS='%OS'%t"
"%%p='%p'%t"
"%%R='%R'%t"
"%%T='%T'%t"
"%%r='%r'%t"
"%%X='%X'%t"
"%%EX='%EX'%t"
"%n}");
const std::locale loc(LOCALE_ja_JP_UTF_8);
std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
// Non localized output using C-locale
check(SV("%H='00'\t"
"%OH='00'\t"
"%I='12'\t"
"%OI='12'\t"
"%M='00'\t"
"%OM='00'\t"
"%S='00'\t"
"%OS='00'\t"
"%p='AM'\t"
"%R='00:00'\t"
"%T='00:00:00'\t"
"%r='12:00:00 AM'\t"
"%X='00:00:00'\t"
"%EX='00:00:00'\t"
"\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%H='23'\t"
"%OH='23'\t"
"%I='11'\t"
"%OI='11'\t"
"%M='31'\t"
"%OM='31'\t"
"%S='30.123'\t"
"%OS='30.123'\t"
"%p='PM'\t"
"%R='23:31'\t"
"%T='23:31:30.123'\t"
"%r='11:31:30 PM'\t"
"%X='23:31:30'\t"
"%EX='23:31:30'\t"
"\n"),
fmt,
std::chrono::sys_time<std::chrono::milliseconds>(
1'234'567'890'123ms)); // 23:31:30 UTC on Friday, 13 February 2009
// Use the global locale (fr_FR)
check(SV("%H='00'\t"
"%OH='00'\t"
"%I='12'\t"
"%OI='12'\t"
"%M='00'\t"
"%OM='00'\t"
"%S='00'\t"
"%OS='00'\t"
#if defined(_AIX)
"%p='AM'\t"
#else
"%p=''\t"
#endif
"%R='00:00'\t"
"%T='00:00:00'\t"
#ifdef _WIN32
"%r='00:00:00'\t"
#elif defined(_AIX)
"%r='12:00:00 AM'\t"
#elif defined(__APPLE__) || defined(__FreeBSD__)
"%r=''\t"
#else
"%r='12:00:00 '\t"
#endif
"%X='00:00:00'\t"
"%EX='00:00:00'\t"
"\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%H='23'\t"
"%OH='23'\t"
"%I='11'\t"
"%OI='11'\t"
"%M='31'\t"
"%OM='31'\t"
"%S='30,123'\t"
"%OS='30,123'\t"
#if defined(_AIX)
"%p='PM'\t"
#else
"%p=''\t"
#endif
"%R='23:31'\t"
"%T='23:31:30,123'\t"
#ifdef _WIN32
"%r='23:31:30'\t"
#elif defined(_AIX)
"%r='11:31:30 PM'\t"
#elif defined(__APPLE__) || defined(__FreeBSD__)
"%r=''\t"
#elif defined(_WIN32)
"%r='23:31:30 '\t"
#else
"%r='11:31:30 '\t"
#endif
"%X='23:31:30'\t"
"%EX='23:31:30'\t"
"\n"),
lfmt,
std::chrono::sys_time<std::chrono::milliseconds>(
1'234'567'890'123ms)); // 23:31:30 UTC on Friday, 13 February 2009
// Use supplied locale (ja_JP). This locale has a different alternate.a
#if defined(__APPLE__) || defined(_AIX) || defined(_WIN32) || defined(__FreeBSD__)
check(loc,
SV("%H='00'\t"
"%OH='00'\t"
"%I='12'\t"
"%OI='12'\t"
"%M='00'\t"
"%OM='00'\t"
"%S='00'\t"
"%OS='00'\t"
# if defined(__APPLE__)
"%p='AM'\t"
# else
"%p='午前'\t"
# endif
"%R='00:00'\t"
"%T='00:00:00'\t"
# if defined(__APPLE__) || defined(__FreeBSD__)
# if defined(__APPLE__)
"%r='12:00:00 AM'\t"
# else
"%r='12:00:00 午前'\t"
# endif
"%X='00時00分00秒'\t"
"%EX='00時00分00秒'\t"
# elif defined(_WIN32)
"%r='0:00:00'\t"
"%X='0:00:00'\t"
"%EX='0:00:00'\t"
# else
"%r='午前12:00:00'\t"
"%X='00:00:00'\t"
"%EX='00:00:00'\t"
# endif
"\n"),
lfmt,
std::chrono::hh_mm_ss(0s));
check(loc,
SV("%H='23'\t"
"%OH='23'\t"
"%I='11'\t"
"%OI='11'\t"
"%M='31'\t"
"%OM='31'\t"
"%S='30.123'\t"
"%OS='30.123'\t"
# if defined(__APPLE__)
"%p='PM'\t"
# else
"%p='午後'\t"
# endif
"%R='23:31'\t"
"%T='23:31:30.123'\t"
# if defined(__APPLE__) || defined(__FreeBSD__)
# if defined(__APPLE__)
"%r='11:31:30 PM'\t"
# else
"%r='11:31:30 午後'\t"
# endif
"%X='23時31分30秒'\t"
"%EX='23時31分30秒'\t"
# elif defined(_WIN32)
"%r='23:31:30'\t"
"%X='23:31:30'\t"
"%EX='23:31:30'\t"
# else
"%r='午後11:31:30'\t"
"%X='23:31:30'\t"
"%EX='23:31:30'\t"
# endif
"\n"),
lfmt,
std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms));
#else // defined(__APPLE__) || defined(_AIX) || defined(_WIN32) || defined(__FreeBSD__)
check(loc,
SV("%H='00'\t"
"%OH=''\t"
"%I='12'\t"
"%OI='十二'\t"
"%M='00'\t"
"%OM=''\t"
"%S='00'\t"
"%OS=''\t"
"%p='午前'\t"
"%R='00:00'\t"
"%T='00:00:00'\t"
"%r='午前12時00分00秒'\t"
"%X='00時00分00秒'\t"
"%EX='00時00分00秒'\t"
"\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%H='23'\t"
"%OH='二十三'\t"
"%I='11'\t"
"%OI='十一'\t"
"%M='31'\t"
"%OM='三十一'\t"
"%S='30.123'\t"
"%OS='三十.123'\t"
"%p='午後'\t"
"%R='23:31'\t"
"%T='23:31:30.123'\t"
"%r='午後11時31分30秒'\t"
"%X='23時31分30秒'\t"
"%EX='23時31分30秒'\t"
"\n"),
lfmt,
std::chrono::sys_time<std::chrono::milliseconds>(
1'234'567'890'123ms)); // 23:31:30 UTC on Friday, 13 February 2009
#endif // defined(__APPLE__) || defined(_AIX) || defined(_WIN32) || defined(__FreeBSD__)
std::locale::global(std::locale::classic());
}
template <class CharT>
static void test_valid_values_date_time() {
using namespace std::literals::chrono_literals;
constexpr std::basic_string_view<CharT> fmt = SV("{:%%c='%c'%t%%Ec='%Ec'%n}");
constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%c='%c'%t%%Ec='%Ec'%n}");
const std::locale loc(LOCALE_ja_JP_UTF_8);
std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
// Non localized output using C-locale
check(SV("%c='Thu Jan 1 00:00:00 1970'\t%Ec='Thu Jan 1 00:00:00 1970'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(SV("%c='Fri Feb 13 23:31:30 2009'\t%Ec='Fri Feb 13 23:31:30 2009'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
// Use the global locale (fr_FR)
check(
// https://sourceware.org/bugzilla/show_bug.cgi?id=24054
#if defined(__powerpc__) && defined(__linux__)
SV("%c='jeu. 01 janv. 1970 00:00:00 UTC'\t%Ec='jeu. 01 janv. 1970 00:00:00 UTC'\n"),
#elif defined(__GLIBC__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 29
SV("%c='jeu. 01 janv. 1970 00:00:00 GMT'\t%Ec='jeu. 01 janv. 1970 00:00:00 GMT'\n"),
#elif defined(_AIX)
SV("%c=' 1 janvier 1970 à 00:00:00 UTC'\t%Ec=' 1 janvier 1970 à 00:00:00 UTC'\n"),
#elif defined(__APPLE__)
SV("%c='Jeu 1 jan 00:00:00 1970'\t%Ec='Jeu 1 jan 00:00:00 1970'\n"),
#elif defined(_WIN32)
SV("%c='01/01/1970 00:00:00'\t%Ec='01/01/1970 00:00:00'\n"),
#elif defined(__FreeBSD__)
SV("%c='jeu. 1 janv. 00:00:00 1970'\t%Ec='jeu. 1 janv. 00:00:00 1970'\n"),
#else
SV("%c='jeu. 01 janv. 1970 00:00:00'\t%Ec='jeu. 01 janv. 1970 00:00:00'\n"),
#endif
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(
// https://sourceware.org/bugzilla/show_bug.cgi?id=24054
#if defined(__powerpc__) && defined(__linux__)
SV("%c='ven. 13 févr. 2009 23:31:30 UTC'\t%Ec='ven. 13 févr. 2009 23:31:30 UTC'\n"),
#elif defined(__GLIBC__) && __GLIBC__ <= 2 && __GLIBC_MINOR__ < 29
SV("%c='ven. 13 févr. 2009 23:31:30 GMT'\t%Ec='ven. 13 févr. 2009 23:31:30 GMT'\n"),
#elif defined(_AIX)
SV("%c='13 février 2009 à 23:31:30 UTC'\t%Ec='13 février 2009 à 23:31:30 UTC'\n"),
#elif defined(__APPLE__)
SV("%c='Ven 13 fév 23:31:30 2009'\t%Ec='Ven 13 fév 23:31:30 2009'\n"),
#elif defined(_WIN32)
SV("%c='13/02/2009 23:31:30'\t%Ec='13/02/2009 23:31:30'\n"),
#elif defined(__FreeBSD__)
SV("%c='ven. 13 févr. 23:31:30 2009'\t%Ec='ven. 13 févr. 23:31:30 2009'\n"),
#else
SV("%c='ven. 13 févr. 2009 23:31:30'\t%Ec='ven. 13 févr. 2009 23:31:30'\n"),
#endif
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
// Use supplied locale (ja_JP). This locale has a different alternate.a
#if defined(__APPLE__) || defined(__FreeBSD__)
check(loc,
SV("%c='木 1/ 1 00:00:00 1970'\t%Ec='木 1/ 1 00:00:00 1970'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%c='金 2/13 23:31:30 2009'\t%Ec='金 2/13 23:31:30 2009'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
#elif defined(_AIX) // defined(__APPLE__)|| defined(__FreeBSD__)
check(loc,
SV("%c='1970年01月 1日 00:00:00 UTC'\t%Ec='1970年01月 1日 00:00:00 UTC'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%c='2009年02月13日 23:31:30 UTC'\t%Ec='2009年02月13日 23:31:30 UTC'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
#elif defined(_WIN32) // defined(__APPLE__)|| defined(__FreeBSD__)
check(loc,
SV("%c='1970/01/01 0:00:00'\t%Ec='1970/01/01 0:00:00'\n"),
lfmt,
std::chrono::sys_seconds(0s)); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%c='2009/02/13 23:31:30'\t%Ec='2009/02/13 23:31:30'\n"),
lfmt,
std::chrono::sys_seconds(1'234'567'890s)); // 23:31:30 UTC on Friday, 13 February 2009
#else // defined(__APPLE__)|| defined(__FreeBSD__)
check(loc,
SV("%c='1970年01月01日 00時00分00秒'\t%Ec='昭和45年01月01日 00時00分00秒'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
check(loc,
SV("%c='2009年02月13日 23時31分30秒'\t%Ec='平成21年02月13日 23時31分30秒'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{1'234'567'890s})); // 23:31:30 UTC on Friday, 13 February 2009
#endif // defined(__APPLE__)|| defined(__FreeBSD__)
std::locale::global(std::locale::classic());
}
template <class CharT>
static void test_valid_values_time_zone() {
using namespace std::literals::chrono_literals;
constexpr std::basic_string_view<CharT> fmt = SV("{:%%z='%z'%t%%Ez='%Ez'%t%%Oz='%Oz'%t%%Z='%Z'%n}");
constexpr std::basic_string_view<CharT> lfmt = SV("{:L%%z='%z'%t%%Ez='%Ez'%t%%Oz='%Oz'%t%%Z='%Z'%n}");
const std::locale loc(LOCALE_ja_JP_UTF_8);
std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
// Non localized output using C-locale
check(SV("%z='+0000'\t%Ez='+00:00'\t%Oz='+00:00'\t%Z='UTC'\n"),
fmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
// Use the global locale (fr_FR)
check(SV("%z='+0000'\t%Ez='+00:00'\t%Oz='+00:00'\t%Z='UTC'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
// Use supplied locale (ja_JP).
check(loc,
SV("%z='+0000'\t%Ez='+00:00'\t%Oz='+00:00'\t%Z='UTC'\n"),
lfmt,
std::chrono::zoned_time(std::chrono::sys_seconds{0s})); // 00:00:00 UTC Thursday, 1 January 1970
std::locale::global(std::locale::classic());
}
template <class CharT>
static void test_valid_values() {
test_valid_values_year<CharT>();
test_valid_values_month<CharT>();
test_valid_values_day<CharT>();
test_valid_values_weekday<CharT>();
test_valid_values_day_of_year<CharT>();
test_valid_values_week<CharT>();
test_valid_values_iso_8601_week<CharT>();
test_valid_values_date<CharT>();
test_valid_values_time<CharT>();
test_valid_values_date_time<CharT>();
test_valid_values_time_zone<CharT>();
}
template <class CharT>
static void test() {
test_no_chrono_specs<CharT>();
test_valid_values<CharT>();
check_invalid_types<CharT>(
{SV("a"), SV("A"), SV("b"), SV("B"), SV("c"), SV("C"), SV("d"), SV("D"), SV("e"), SV("F"), SV("g"),
SV("G"), SV("h"), SV("H"), SV("I"), SV("j"), SV("m"), SV("M"), SV("p"), SV("r"), SV("R"), SV("S"),
SV("T"), SV("u"), SV("U"), SV("V"), SV("w"), SV("W"), SV("x"), SV("X"), SV("y"), SV("Y"), SV("z"),
SV("Z"), SV("Ec"), SV("EC"), SV("Ex"), SV("EX"), SV("Ey"), SV("EY"), SV("Ez"), SV("Od"), SV("Oe"), SV("OH"),
SV("OI"), SV("Om"), SV("OM"), SV("OS"), SV("Ou"), SV("OU"), SV("OV"), SV("Ow"), SV("OW"), SV("Oy"), SV("Oz")},
std::chrono::zoned_time{});
}
int main(int, char**) {
test<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<wchar_t>();
#endif
return 0;
}

View File

@ -13,6 +13,7 @@
#include <cassert>
#include <charconv>
#include <chrono>
#include <format>
#include <string_view>
#include <type_traits>
@ -42,6 +43,8 @@ public:
offset_time_zone* operator->() { return this; }
const offset_time_zone* operator->() const { return this; }
template <class Duration>
std::chrono::sys_time<std::common_type_t<Duration, std::chrono::seconds>>
to_sys(const std::chrono::local_time<Duration>& local) const {
@ -49,6 +52,22 @@ public:
local.time_since_epoch() + offset_};
}
template <class Duration>
std::chrono::local_time<std::common_type_t<Duration, std::chrono::seconds>>
to_local(const std::chrono::sys_time<Duration>& sys) const {
return std::chrono::local_time<std::common_type_t<Duration, std::chrono::seconds>>{
sys.time_since_epoch() - offset_};
}
template <class Duration>
std::chrono::sys_info get_info(const std::chrono::sys_time<Duration>&) const {
return {std::chrono::sys_seconds::min(),
std::chrono::sys_seconds::max(),
offset_,
std::chrono::minutes{0},
std::format("{:+03d}s", offset_.count())};
}
private:
std::chrono::seconds offset_;
};

View File

@ -0,0 +1,351 @@
//===----------------------------------------------------------------------===//
//
// 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
// TODO FMT This test should not require std::to_chars(floating-point)
// XFAIL: availability-fp_to_chars-missing
// XFAIL: libcpp-has-no-experimental-tzdb
// REQUIRES: locale.fr_FR.UTF-8
// REQUIRES: locale.ja_JP.UTF-8
// <chrono>
// template<class charT, class traits, class Duration, class TimeZonePtr>
// basic_ostream<charT, traits>&
// operator<<(basic_ostream<charT, traits>& os,
// const zoned_time<Duration, TimeZonePtr>& t);
#include <chrono>
#include <cassert>
#include <sstream>
#include "assert_macros.h"
#include "concat_macros.h"
#include "make_string.h"
#include "platform_support.h" // locale name macros
#include "test_macros.h"
#include "../test_offset_time_zone.h"
#define SV(S) MAKE_STRING_VIEW(CharT, S)
#define TEST_EQUAL(OUT, EXPECTED) \
TEST_REQUIRE(OUT == EXPECTED, \
TEST_WRITE_CONCATENATED( \
"\nExpression ", #OUT, "\nExpected output ", EXPECTED, "\nActual output ", OUT, '\n'));
template <class CharT, class Duration, class TimeZonePtr>
static std::basic_string<CharT> stream_c_locale(std::chrono::zoned_time<Duration, TimeZonePtr> time_point) {
std::basic_stringstream<CharT> sstr;
sstr << time_point;
return sstr.str();
}
template <class CharT, class Duration, class TimeZonePtr>
static std::basic_string<CharT> stream_fr_FR_locale(std::chrono::zoned_time<Duration, TimeZonePtr> time_point) {
std::basic_stringstream<CharT> sstr;
const std::locale locale(LOCALE_fr_FR_UTF_8);
sstr.imbue(locale);
sstr << time_point;
return sstr.str();
}
template <class CharT, class Duration, class TimeZonePtr>
static std::basic_string<CharT> stream_ja_JP_locale(std::chrono::zoned_time<Duration, TimeZonePtr> time_point) {
std::basic_stringstream<CharT> sstr;
const std::locale locale(LOCALE_ja_JP_UTF_8);
sstr.imbue(locale);
sstr << time_point;
return sstr.str();
}
template <class CharT>
static void test_c() {
using namespace std::literals::chrono_literals;
{ // Different durations
TEST_EQUAL(stream_c_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::nanoseconds>{42ns})),
SV("1970-01-01 01:00:00.000000042 +01"));
TEST_EQUAL(stream_c_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::microseconds>{42us})),
SV("1970-01-01 01:00:00.000042 +01"));
TEST_EQUAL(stream_c_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::milliseconds>{42ms})),
SV("1970-01-01 01:00:00.042 +01"));
TEST_EQUAL(
stream_c_locale<CharT>(std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::seconds>{42s})),
SV("1970-01-01 01:00:42 +01"));
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Etc/GMT-1", std::chrono::sys_time<std::chrono::days>{std::chrono::days{42}})),
SV("1970-02-12 01:00:00 +01"));
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Etc/GMT-1", std::chrono::sys_time<std::chrono::weeks>{std::chrono::weeks{42}})),
SV("1970-10-22 01:00:00 +01"));
}
{ // Daylight saving time switches
// Pick an historic date where it's well known what the time zone rules were.
// This makes it unlikely updates to the database change these rules.
// Z Europe/Berlin 0:53:28 - LMT 1893 Ap
// ...
// 1 DE CE%sT 1980
// 1 E CE%sT
//
// ...
// R E 1979 1995 - S lastSu 1u 0 -
// R E 1981 ma - Mar lastSu 1u 1 S
// Pick an historic date where it's well known what the time zone rules were.
// This makes it unlikely updates to the database change these rules.
// Start of daylight saving time
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 0h + 59min + 59s)),
SV("1986-03-30 01:59:59 CET"));
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 1h)),
SV("1986-03-30 03:00:00 CEST"));
// End of daylight saving time
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 0h + 59min + 59s)),
SV("1986-09-28 02:59:59 CEST"));
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h)),
SV("1986-09-28 02:00:00 CET"));
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h + 59min + 59s)),
SV("1986-09-28 02:59:59 CET"));
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 2h)),
SV("1986-09-28 03:00:00 CET"));
}
{ // offset pointer
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{}, std::chrono::sys_seconds{})),
SV("1970-01-01 00:00:00 +00s"));
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{"42"}, std::chrono::sys_seconds{})),
SV("1969-12-31 23:59:18 +42s"));
TEST_EQUAL(stream_c_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{"-42"}, std::chrono::sys_seconds{})),
SV("1970-01-01 00:00:42 -42s"));
}
}
template <class CharT>
static void test_fr_FR() {
using namespace std::literals::chrono_literals;
{ // Different durations
TEST_EQUAL(stream_fr_FR_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::nanoseconds>{42ns})),
SV("1970-01-01 01:00:00,000000042 +01"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::microseconds>{42us})),
SV("1970-01-01 01:00:00,000042 +01"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::milliseconds>{42ms})),
SV("1970-01-01 01:00:00,042 +01"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::seconds>{42s})),
SV("1970-01-01 01:00:42 +01"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Etc/GMT-1", std::chrono::sys_time<std::chrono::days>{std::chrono::days{42}})),
SV("1970-02-12 01:00:00 +01"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Etc/GMT-1", std::chrono::sys_time<std::chrono::weeks>{std::chrono::weeks{42}})),
SV("1970-10-22 01:00:00 +01"));
}
{ // Daylight saving time switches
// Pick an historic date where it's well known what the time zone rules were.
// This makes it unlikely updates to the database change these rules.
// Z Europe/Berlin 0:53:28 - LMT 1893 Ap
// ...
// 1 DE CE%sT 1980
// 1 E CE%sT
//
// ...
// R E 1979 1995 - S lastSu 1u 0 -
// R E 1981 ma - Mar lastSu 1u 1 S
// Pick an historic date where it's well known what the time zone rules were.
// This makes it unlikely updates to the database change these rules.
// Start of daylight saving time
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 0h + 59min + 59s)),
SV("1986-03-30 01:59:59 CET"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 1h)),
SV("1986-03-30 03:00:00 CEST"));
// End of daylight saving time
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 0h + 59min + 59s)),
SV("1986-09-28 02:59:59 CEST"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h)),
SV("1986-09-28 02:00:00 CET"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h + 59min + 59s)),
SV("1986-09-28 02:59:59 CET"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 2h)),
SV("1986-09-28 03:00:00 CET"));
}
{ // offset pointer
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{}, std::chrono::sys_seconds{})),
SV("1970-01-01 00:00:00 +00s"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{"42"}, std::chrono::sys_seconds{})),
SV("1969-12-31 23:59:18 +42s"));
TEST_EQUAL(stream_fr_FR_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{"-42"}, std::chrono::sys_seconds{})),
SV("1970-01-01 00:00:42 -42s"));
}
}
template <class CharT>
static void test_ja_JP() {
using namespace std::literals::chrono_literals;
{ // Different durations
TEST_EQUAL(stream_ja_JP_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::nanoseconds>{42ns})),
SV("1970-01-01 01:00:00.000000042 +01"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::microseconds>{42us})),
SV("1970-01-01 01:00:00.000042 +01"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::milliseconds>{42ms})),
SV("1970-01-01 01:00:00.042 +01"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(
std::chrono::zoned_time("Etc/GMT-1", std::chrono::sys_time<std::chrono::seconds>{42s})),
SV("1970-01-01 01:00:42 +01"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Etc/GMT-1", std::chrono::sys_time<std::chrono::days>{std::chrono::days{42}})),
SV("1970-02-12 01:00:00 +01"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Etc/GMT-1", std::chrono::sys_time<std::chrono::weeks>{std::chrono::weeks{42}})),
SV("1970-10-22 01:00:00 +01"));
}
{ // Daylight saving time switches
// Pick an historic date where it's well known what the time zone rules were.
// This makes it unlikely updates to the database change these rules.
// Z Europe/Berlin 0:53:28 - LMT 1893 Ap
// ...
// 1 DE CE%sT 1980
// 1 E CE%sT
//
// ...
// R E 1979 1995 - S lastSu 1u 0 -
// R E 1981 ma - Mar lastSu 1u 1 S
// Pick an historic date where it's well known what the time zone rules were.
// This makes it unlikely updates to the database change these rules.
// Start of daylight saving time
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 0h + 59min + 59s)),
SV("1986-03-30 01:59:59 CET"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::March / 30 / 1986} + 1h)),
SV("1986-03-30 03:00:00 CEST"));
// End of daylight saving time
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 0h + 59min + 59s)),
SV("1986-09-28 02:59:59 CEST"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h)),
SV("1986-09-28 02:00:00 CET"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 1h + 59min + 59s)),
SV("1986-09-28 02:59:59 CET"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
"Europe/Berlin", std::chrono::sys_days{std::chrono::September / 28 / 1986} + 2h)),
SV("1986-09-28 03:00:00 CET"));
}
{ // offset pointer
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{}, std::chrono::sys_seconds{})),
SV("1970-01-01 00:00:00 +00s"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{"42"}, std::chrono::sys_seconds{})),
SV("1969-12-31 23:59:18 +42s"));
TEST_EQUAL(stream_ja_JP_locale<CharT>(std::chrono::zoned_time(
offset_time_zone<offset_time_zone_flags::none>{"-42"}, std::chrono::sys_seconds{})),
SV("1970-01-01 00:00:42 -42s"));
}
}
template <class CharT>
static void test() {
test_c<CharT>();
test_fr_FR<CharT>();
test_ja_JP<CharT>();
}
int main(int, char**) {
test<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<wchar_t>();
#endif
return 0;
}