mirror of
https://github.com/intel/llvm.git
synced 2026-01-26 12:26:52 +08:00
[libc++][chrono] Add hh_mm_ss formatter.
Partially implements: - P1361 Integration of chrono with text formatting - P2372 Fixing locale handling in chrono formatters - P1466 Miscellaneous minor fixes for chrono Depends on D137022 Reviewed By: ldionne, #libc Differential Revision: https://reviews.llvm.org/D139771
This commit is contained in:
@@ -23,7 +23,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
|
||||
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year_month_day_last``",,Mark de Wever,|Complete|, Clang 16
|
||||
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year_month_weekday``",,Mark de Wever,|Complete|, Clang 16
|
||||
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year_month_weekday_last``",,Mark de Wever,|Complete|, Clang 16
|
||||
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::hh_mm_ss<duration<Rep, Period>>``",,Mark de Wever,|In Progress|,
|
||||
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::hh_mm_ss<duration<Rep, Period>>``",,Mark de Wever,|Complete|, Clang 17
|
||||
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_info``",A ``<chrono>`` implementation,Mark de Wever,,
|
||||
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_info``",A ``<chrono>`` implementation,Mark de Wever,,
|
||||
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::zoned_time<Duration, TimeZonePtr>``",A ``<chrono>`` implementation,Mark de Wever,,
|
||||
|
||||
|
@@ -216,6 +216,7 @@ set(files
|
||||
__charconv/to_chars_base_10.h
|
||||
__charconv/to_chars_result.h
|
||||
__chrono/calendar.h
|
||||
__chrono/concepts.h
|
||||
__chrono/convert_to_timespec.h
|
||||
__chrono/convert_to_tm.h
|
||||
__chrono/day.h
|
||||
|
||||
32
libcxx/include/__chrono/concepts.h
Normal file
32
libcxx/include/__chrono/concepts.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// -*- C++ -*-
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP___CHRONO_CONCEPTS_H
|
||||
#define _LIBCPP___CHRONO_CONCEPTS_H
|
||||
|
||||
#include <__chrono/hh_mm_ss.h>
|
||||
#include <__config>
|
||||
#include <__type_traits/is_specialization.h>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if _LIBCPP_STD_VER > 17
|
||||
|
||||
template <class _Tp>
|
||||
concept __is_hh_mm_ss = __is_specialization_v<_Tp, chrono::hh_mm_ss>;
|
||||
|
||||
#endif // _LIBCPP_STD_VER > 17
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP___CHRONO_CONCEPTS_H
|
||||
@@ -10,6 +10,7 @@
|
||||
#ifndef _LIBCPP___CHRONO_CONVERT_TO_TM_H
|
||||
#define _LIBCPP___CHRONO_CONVERT_TO_TM_H
|
||||
|
||||
#include <__chrono/concepts.h>
|
||||
#include <__chrono/day.h>
|
||||
#include <__chrono/duration.h>
|
||||
#include <__chrono/hh_mm_ss.h>
|
||||
@@ -26,14 +27,19 @@
|
||||
#include <__chrono/year_month_weekday.h>
|
||||
#include <__concepts/same_as.h>
|
||||
#include <__config>
|
||||
#include <__format/format_error.h>
|
||||
#include <__memory/addressof.h>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
|
||||
#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 > 17
|
||||
@@ -114,6 +120,16 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
|
||||
} else if constexpr (same_as<_ChronoT, chrono::year_month_weekday> ||
|
||||
same_as<_ChronoT, chrono::year_month_weekday_last>) {
|
||||
return std::__convert_to_tm<_Tm>(chrono::year_month_day{static_cast<chrono::sys_days>(__value)}, __value.weekday());
|
||||
} else if constexpr (__is_hh_mm_ss<_ChronoT>) {
|
||||
__result.tm_sec = __value.seconds().count();
|
||||
__result.tm_min = __value.minutes().count();
|
||||
// In libc++ hours is stored as a long. The type in std::tm is an int. So
|
||||
// the overflow can only occur when hour uses more bits than an int
|
||||
// provides.
|
||||
if constexpr (sizeof(std::chrono::hours::rep) > sizeof(__result.tm_hour))
|
||||
if (__value.hours().count() > std::numeric_limits<decltype(__result.tm_hour)>::max())
|
||||
std::__throw_format_error("Formatting hh_mm_ss, encountered an hour overflow");
|
||||
__result.tm_hour = __value.hours().count();
|
||||
} else
|
||||
static_assert(sizeof(_ChronoT) == 0, "Add the missing type specialization");
|
||||
|
||||
@@ -124,4 +140,6 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
_LIBCPP_POP_MACROS
|
||||
|
||||
#endif // _LIBCPP___CHRONO_CONVERT_TO_TM_H
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#define _LIBCPP___CHRONO_FORMATTER_H
|
||||
|
||||
#include <__chrono/calendar.h>
|
||||
#include <__chrono/concepts.h>
|
||||
#include <__chrono/convert_to_tm.h>
|
||||
#include <__chrono/day.h>
|
||||
#include <__chrono/duration.h>
|
||||
@@ -75,13 +76,15 @@ namespace __formatter {
|
||||
// For tiny ratios it's not possible to convert a duration to a hh_mm_ss. This
|
||||
// fails compile-time due to the limited precision of the ratio (64-bit is too
|
||||
// small). Therefore a duration uses its own conversion.
|
||||
template <class _CharT, class _Tp>
|
||||
requires(chrono::__is_duration<_Tp>::value)
|
||||
_LIBCPP_HIDE_FROM_ABI void __format_sub_seconds(const _Tp& __value, basic_stringstream<_CharT>& __sstr) {
|
||||
template <class _CharT, class _Rep, class _Period>
|
||||
_LIBCPP_HIDE_FROM_ABI void
|
||||
__format_sub_seconds(const chrono::duration<_Rep, _Period>& __value, basic_stringstream<_CharT>& __sstr) {
|
||||
__sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point();
|
||||
|
||||
using __duration = chrono::duration<_Rep, _Period>;
|
||||
|
||||
auto __fraction = __value - chrono::duration_cast<chrono::seconds>(__value);
|
||||
if constexpr (chrono::treat_as_floating_point_v<typename _Tp::rep>)
|
||||
if constexpr (chrono::treat_as_floating_point_v<_Rep>)
|
||||
// When the floating-point value has digits itself they are ignored based
|
||||
// on the wording in [tab:time.format.spec]
|
||||
// If the precision of the input cannot be exactly represented with
|
||||
@@ -97,18 +100,36 @@ _LIBCPP_HIDE_FROM_ABI void __format_sub_seconds(const _Tp& __value, basic_string
|
||||
std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
|
||||
_LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"),
|
||||
__fraction.count(),
|
||||
chrono::hh_mm_ss<_Tp>::fractional_width);
|
||||
chrono::hh_mm_ss<__duration>::fractional_width);
|
||||
else
|
||||
std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
|
||||
_LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"),
|
||||
__fraction.count(),
|
||||
chrono::hh_mm_ss<_Tp>::fractional_width);
|
||||
chrono::hh_mm_ss<__duration>::fractional_width);
|
||||
}
|
||||
|
||||
template <class _CharT, class _Duration>
|
||||
_LIBCPP_HIDE_FROM_ABI void
|
||||
__format_sub_seconds(const chrono::hh_mm_ss<_Duration>& __value, basic_stringstream<_CharT>& __sstr) {
|
||||
__sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point();
|
||||
if constexpr (chrono::treat_as_floating_point_v<typename _Duration::rep>)
|
||||
std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
|
||||
_LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"),
|
||||
__value.subseconds().count(),
|
||||
__value.fractional_width);
|
||||
else
|
||||
std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
|
||||
_LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}}"),
|
||||
__value.subseconds().count(),
|
||||
__value.fractional_width);
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
consteval bool __use_fraction() {
|
||||
if constexpr (chrono::__is_duration<_Tp>::value)
|
||||
return chrono::hh_mm_ss<_Tp>::fractional_width;
|
||||
else if constexpr (__is_hh_mm_ss<_Tp>)
|
||||
return _Tp::fractional_width;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
@@ -322,6 +343,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {
|
||||
return __value.weekday().ok();
|
||||
else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
|
||||
return __value.weekday().ok();
|
||||
else if constexpr (__is_hh_mm_ss<_Tp>)
|
||||
return true;
|
||||
else
|
||||
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
|
||||
}
|
||||
@@ -358,6 +381,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {
|
||||
return __value.weekday().ok();
|
||||
else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
|
||||
return __value.weekday().ok();
|
||||
else if constexpr (__is_hh_mm_ss<_Tp>)
|
||||
return true;
|
||||
else
|
||||
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
|
||||
}
|
||||
@@ -394,6 +419,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {
|
||||
return __value.ok();
|
||||
else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
|
||||
return __value.ok();
|
||||
else if constexpr (__is_hh_mm_ss<_Tp>)
|
||||
return true;
|
||||
else
|
||||
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
|
||||
}
|
||||
@@ -430,6 +457,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {
|
||||
return __value.month().ok();
|
||||
else if constexpr (same_as<_Tp, chrono::year_month_weekday_last>)
|
||||
return __value.month().ok();
|
||||
else if constexpr (__is_hh_mm_ss<_Tp>)
|
||||
return true;
|
||||
else
|
||||
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
|
||||
}
|
||||
@@ -478,6 +507,29 @@ __format_chrono(const _Tp& __value,
|
||||
if (__specs.__chrono_.__month_name_ && !__formatter::__month_name_ok(__value))
|
||||
std::__throw_format_error("formatting a month name from an invalid month number");
|
||||
|
||||
if constexpr (__is_hh_mm_ss<_Tp>) {
|
||||
// Note this is a pedantic intepretation of the Standard. A hh_mm_ss
|
||||
// is no longer a time_of_day and can store an arbitrary number of
|
||||
// hours. A number of hours in a 12 or 24 hour clock can't represent
|
||||
// 24 hours or more. The functions std::chrono::make12 and
|
||||
// std::chrono::make24 reaffirm this view point.
|
||||
//
|
||||
// Interestingly this will be the only output stream function that
|
||||
// throws.
|
||||
//
|
||||
// TODO FMT The wording probably needs to be adapted to
|
||||
// - The displayed hours is hh_mm_ss.hours() % 24
|
||||
// - It should probably allow %j in the same fashion as duration.
|
||||
// - The stream formatter should change its output when hours >= 24
|
||||
// - Write it as not valid,
|
||||
// - or write the number of days.
|
||||
if (__specs.__chrono_.__hour_ && __value.hours().count() > 23)
|
||||
std::__throw_format_error("formatting a hour needs a valid value");
|
||||
|
||||
if (__value.is_negative())
|
||||
__sstr << _CharT('-');
|
||||
}
|
||||
|
||||
__formatter::__format_chrono_using_chrono_specs(__value, __sstr, __chrono_specs);
|
||||
}
|
||||
}
|
||||
@@ -709,6 +761,16 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Duration, __fmt_char_type _CharT>
|
||||
struct formatter<chrono::hh_mm_ss<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
|
||||
public:
|
||||
using _Base = __formatter_chrono<_CharT>;
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr auto parse(basic_format_parse_context<_CharT>& __parse_ctx)
|
||||
-> decltype(__parse_ctx.begin()) {
|
||||
return _Base::__parse(__parse_ctx, __format_spec::__fields_chrono, __format_spec::__flags::__time);
|
||||
}
|
||||
};
|
||||
#endif // if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
@@ -85,6 +85,7 @@ private:
|
||||
chrono::seconds __s_;
|
||||
precision __f_;
|
||||
};
|
||||
_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(hh_mm_ss);
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr bool is_am(const hours& __h) noexcept { return __h >= hours( 0) && __h < hours(12); }
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr bool is_pm(const hours& __h) noexcept { return __h >= hours(12) && __h < hours(24); }
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include <__chrono/day.h>
|
||||
#include <__chrono/duration.h>
|
||||
#include <__chrono/hh_mm_ss.h>
|
||||
#include <__chrono/month.h>
|
||||
#include <__chrono/month_weekday.h>
|
||||
#include <__chrono/monthday.h>
|
||||
@@ -229,6 +230,12 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const year_month_weekday_last&
|
||||
__ymwdl.weekday_last());
|
||||
}
|
||||
|
||||
template <class _CharT, class _Traits, class _Duration>
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT basic_ostream<_CharT, _Traits>&
|
||||
operator<<(basic_ostream<_CharT, _Traits>& __os, const hh_mm_ss<_Duration> __hms) {
|
||||
return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%T}"), __hms);
|
||||
}
|
||||
|
||||
} // namespace chrono
|
||||
|
||||
#endif //if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
|
||||
|
||||
@@ -214,6 +214,7 @@ private:
|
||||
case _CharT('p'): // TODO FMT does the formater require an hour or a time?
|
||||
case _CharT('H'):
|
||||
case _CharT('I'):
|
||||
__parser_.__hour_ = true;
|
||||
__validate_hour(__flags);
|
||||
break;
|
||||
|
||||
@@ -221,6 +222,7 @@ private:
|
||||
case _CharT('R'):
|
||||
case _CharT('T'):
|
||||
case _CharT('X'):
|
||||
__parser_.__hour_ = true;
|
||||
__format_spec::__validate_time(__flags);
|
||||
break;
|
||||
|
||||
@@ -313,6 +315,7 @@ private:
|
||||
|
||||
switch (*__begin) {
|
||||
case _CharT('X'):
|
||||
__parser_.__hour_ = true;
|
||||
__format_spec::__validate_time(__flags);
|
||||
break;
|
||||
|
||||
@@ -361,6 +364,7 @@ private:
|
||||
|
||||
case _CharT('I'):
|
||||
case _CharT('H'):
|
||||
__parser_.__hour_ = true;
|
||||
__format_spec::__validate_hour(__flags);
|
||||
break;
|
||||
|
||||
|
||||
@@ -199,6 +199,7 @@ struct __std {
|
||||
struct __chrono {
|
||||
__alignment __alignment_ : 3;
|
||||
bool __locale_specific_form_ : 1;
|
||||
bool __hour_ : 1;
|
||||
bool __weekday_name_ : 1;
|
||||
bool __weekday_ : 1;
|
||||
bool __day_of_year_ : 1;
|
||||
@@ -329,6 +330,7 @@ public:
|
||||
.__chrono_ =
|
||||
__chrono{.__alignment_ = __alignment_,
|
||||
.__locale_specific_form_ = __locale_specific_form_,
|
||||
.__hour_ = __hour_,
|
||||
.__weekday_name_ = __weekday_name_,
|
||||
.__weekday_ = __weekday_,
|
||||
.__day_of_year_ = __day_of_year_,
|
||||
@@ -348,6 +350,8 @@ public:
|
||||
|
||||
// These flags are only used for formatting chrono. Since the struct has
|
||||
// padding space left it's added to this structure.
|
||||
bool __hour_ : 1 {false};
|
||||
|
||||
bool __weekday_name_ : 1 {false};
|
||||
bool __weekday_ : 1 {false};
|
||||
|
||||
@@ -356,7 +360,7 @@ public:
|
||||
|
||||
bool __month_name_ : 1 {false};
|
||||
|
||||
uint8_t __reserved_1_ : 3 {0};
|
||||
uint8_t __reserved_1_ : 2 {0};
|
||||
uint8_t __reserved_2_ : 6 {0};
|
||||
// These two flags are only used internally and not part of the
|
||||
// __parsed_specifications. Therefore put them at the end.
|
||||
|
||||
@@ -656,6 +656,10 @@ public:
|
||||
constexpr precision to_duration() const noexcept;
|
||||
};
|
||||
|
||||
template<class charT, class traits, class Duration>
|
||||
basic_ostream<charT, traits>&
|
||||
operator<<(basic_ostream<charT, traits>& os, const hh_mm_ss<Duration>& hms); // C++20
|
||||
|
||||
// 26.10, 12/24 hour functions
|
||||
constexpr bool is_am(hours const& h) noexcept;
|
||||
constexpr bool is_pm(hours const& h) noexcept;
|
||||
@@ -691,6 +695,8 @@ namespace std {
|
||||
template<class charT> struct formatter<chrono::year_month_day_last, charT>; // C++20
|
||||
template<class charT> struct formatter<chrono::year_month_weekday, charT>; // C++20
|
||||
template<class charT> struct formatter<chrono::year_month_weekday_last, charT>; // C++20
|
||||
template<class Rep, class Period, class charT>
|
||||
struct formatter<chrono::hh_mm_ss<duration<Rep, Period>>, charT>; // C++20
|
||||
} // namespace std
|
||||
|
||||
namespace chrono {
|
||||
|
||||
@@ -674,6 +674,7 @@ module std [system] {
|
||||
|
||||
module __chrono {
|
||||
module calendar { private header "__chrono/calendar.h" }
|
||||
module concepts { private header "__chrono/concepts.h" }
|
||||
module convert_to_timespec { private header "__chrono/convert_to_timespec.h" }
|
||||
module convert_to_tm { private header "__chrono/convert_to_tm.h" }
|
||||
module day { private header "__chrono/day.h" }
|
||||
|
||||
@@ -250,6 +250,7 @@ END-SCRIPT
|
||||
#include <__charconv/to_chars_base_10.h> // expected-error@*:* {{use of private header from outside its module: '__charconv/to_chars_base_10.h'}}
|
||||
#include <__charconv/to_chars_result.h> // expected-error@*:* {{use of private header from outside its module: '__charconv/to_chars_result.h'}}
|
||||
#include <__chrono/calendar.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/calendar.h'}}
|
||||
#include <__chrono/concepts.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/concepts.h'}}
|
||||
#include <__chrono/convert_to_timespec.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/convert_to_timespec.h'}}
|
||||
#include <__chrono/convert_to_tm.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/convert_to_tm.h'}}
|
||||
#include <__chrono/day.h> // expected-error@*:* {{use of private header from outside its module: '__chrono/day.h'}}
|
||||
|
||||
62
libcxx/test/libcxx/time/convert_to_tm.pass.cpp
Normal file
62
libcxx/test/libcxx/time/convert_to_tm.pass.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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: libcpp-has-no-incomplete-format
|
||||
|
||||
// <chrono>
|
||||
|
||||
// template <class _Tm, class _ChronoT>
|
||||
// _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value)
|
||||
|
||||
// Most of the code is tested indirectly in the chrono formatters. This only
|
||||
// tests the hour overflow.
|
||||
|
||||
#include <chrono>
|
||||
#include <cassert>
|
||||
#include <format>
|
||||
#include <string_view>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
// libc++ uses a long as representation in std::chrono::hours.
|
||||
// std::tm uses an int for its integral members. The overflow in the hour
|
||||
// conversion can only occur on platforms where sizeof(long) > sizeof(int).
|
||||
// Instead emulate this error by using a "tm" with shorts.
|
||||
// (The function is already templated to this is quite easy to do,)
|
||||
struct minimal_short_tm {
|
||||
short tm_sec;
|
||||
short tm_min;
|
||||
short tm_hour;
|
||||
const char* tm_zone;
|
||||
};
|
||||
|
||||
int main(int, char**) {
|
||||
{ // Test with the maximum number of hours that fit in a short.
|
||||
std::chrono::hh_mm_ss time{std::chrono::hours{32767}};
|
||||
minimal_short_tm result = std::__convert_to_tm<minimal_short_tm>(time);
|
||||
assert(result.tm_sec == 0);
|
||||
assert(result.tm_min == 0);
|
||||
assert(result.tm_hour == 32767);
|
||||
}
|
||||
|
||||
#ifndef TEST_HAS_NO_EXCEPTIONS
|
||||
{ // Test above the maximum number of hours that fit in a short.
|
||||
std::chrono::hh_mm_ss time{std::chrono::hours{32768}};
|
||||
try {
|
||||
TEST_IGNORE_NODISCARD std::__convert_to_tm<minimal_short_tm>(time);
|
||||
assert(false);
|
||||
} catch ([[maybe_unused]] const std::format_error& e) {
|
||||
LIBCPP_ASSERT(e.what() == std::string_view("Formatting hh_mm_ss, encountered an hour overflow"));
|
||||
return 0;
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
#endif // TEST_HAS_NO_EXCEPTIONS
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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-localization
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-format
|
||||
|
||||
// TODO FMT Evaluate gcc-12 status
|
||||
// UNSUPPORTED: gcc-12
|
||||
|
||||
// TODO FMT Investigate Windows issues.
|
||||
// UNSUPPORTED: msvc, target={{.+}}-windows-gnu
|
||||
|
||||
// REQUIRES: locale.fr_FR.UTF-8
|
||||
// REQUIRES: locale.ja_JP.UTF-8
|
||||
|
||||
// <chrono>
|
||||
|
||||
// class hh_mm_ss;
|
||||
|
||||
// template<class charT, class traits, class Duration>
|
||||
// basic_ostream<charT, traits>&
|
||||
// operator<<(basic_ostream<charT, traits>& os, const hh_mm_ss<Duration>& hms);
|
||||
|
||||
#include <chrono>
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
|
||||
#include "make_string.h"
|
||||
#include "platform_support.h" // locale name macros
|
||||
#include "test_macros.h"
|
||||
|
||||
#define SV(S) MAKE_STRING_VIEW(CharT, S)
|
||||
|
||||
template <class CharT, class Duration>
|
||||
static std::basic_string<CharT> stream_c_locale(std::chrono::hh_mm_ss<Duration> hms) {
|
||||
std::basic_stringstream<CharT> sstr;
|
||||
sstr << hms;
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
template <class CharT, class Duration>
|
||||
static std::basic_string<CharT> stream_fr_FR_locale(std::chrono::hh_mm_ss<Duration> hms) {
|
||||
std::basic_stringstream<CharT> sstr;
|
||||
const std::locale locale(LOCALE_fr_FR_UTF_8);
|
||||
sstr.imbue(locale);
|
||||
sstr << hms;
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
template <class CharT, class Duration>
|
||||
static std::basic_string<CharT> stream_ja_JP_locale(std::chrono::hh_mm_ss<Duration> hms) {
|
||||
std::basic_stringstream<CharT> sstr;
|
||||
const std::locale locale(LOCALE_ja_JP_UTF_8);
|
||||
sstr.imbue(locale);
|
||||
sstr << hms;
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
static void test() {
|
||||
// Note std::atto can't be tested since the ratio conversion from std::atto
|
||||
// std::chrono::seconds to std::chrono::hours overflows when intmax_t is a
|
||||
// 64-bit type. This is a limitiation in the constructor of
|
||||
// std::chrono::hh_mm_ss.
|
||||
|
||||
// C locale - integral power of 10 ratios
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::femto>{1'234'567'890}}) ==
|
||||
SV("00:00:00.000001234567890"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::pico>{1'234'567'890}}) ==
|
||||
SV("00:00:00.001234567890"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::nano>{1'234'567'890}}) ==
|
||||
SV("00:00:01.234567890"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::micro>{1'234'567}}) ==
|
||||
SV("00:00:01.234567"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::milli>{123'456}}) ==
|
||||
SV("00:02:03.456"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::centi>{12'345}}) ==
|
||||
SV("00:02:03.45"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::deci>{1'234}}) ==
|
||||
SV("00:02:03.4"));
|
||||
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t>{123}}) == SV("00:02:03"));
|
||||
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::deca>{-366}}) ==
|
||||
SV("-01:01:00"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::hecto>{-72}}) ==
|
||||
SV("-02:00:00"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::kilo>{-86}}) ==
|
||||
SV("-23:53:20"));
|
||||
|
||||
// Starting at mega it will pass one day
|
||||
|
||||
// fr_FR locale - integral power of not 10 ratios
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{
|
||||
std::chrono::duration<intmax_t, std::ratio<1, 5'000'000>>{5'000}}) == SV("00:00:00,0010000"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 8'000>>{3}}) ==
|
||||
SV("00:00:00,000375"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 4'000>>{1}}) ==
|
||||
SV("00:00:00,00025"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 5'000>>{5}}) ==
|
||||
SV("00:00:00,0010"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 8>>{-4}}) ==
|
||||
SV("-00:00:00,500"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 4>>{-8}}) ==
|
||||
SV("-00:00:02,00"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 5>>{-5}}) ==
|
||||
SV("-00:00:01,0"));
|
||||
|
||||
// TODO FMT Note there's no wording on the rounding
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 9>>{5}}) ==
|
||||
SV("00:00:00,555555"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 7>>{7}}) ==
|
||||
SV("00:00:01,000000"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 6>>{1}}) ==
|
||||
SV("00:00:00,166666"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<intmax_t, std::ratio<1, 3>>{2}}) ==
|
||||
SV("00:00:00,666666"));
|
||||
|
||||
// ja_JP locale - floating points
|
||||
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{
|
||||
std::chrono::duration<long double, std::femto>{1'234'567'890.123}}) == SV("00:00:00.000001234567890"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{
|
||||
std::chrono::duration<long double, std::pico>{1'234'567'890.123}}) == SV("00:00:00.001234567890"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{
|
||||
std::chrono::duration<long double, std::nano>{1'234'567'890.123}}) == SV("00:00:01.234567890"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<double, std::micro>{1'234'567.123}}) ==
|
||||
SV("00:00:01.234567"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<double, std::milli>{123'456.123}}) ==
|
||||
SV("00:02:03.456"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<double, std::centi>{12'345.123}}) ==
|
||||
SV("00:02:03.45"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<float, std::deci>{1'234.123}}) ==
|
||||
SV("00:02:03.4"));
|
||||
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<float>{123.123}}) == SV("00:02:03"));
|
||||
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<double, std::deca>{-366.5}}) ==
|
||||
SV("-01:01:05"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<double, std::hecto>{-72.64}}) ==
|
||||
SV("-02:01:04"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::hh_mm_ss{std::chrono::duration<double, std::kilo>{-86}}) ==
|
||||
SV("-23:53:20"));
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test<char>();
|
||||
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
test<wchar_t>();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
556
libcxx/test/std/time/time.syn/formatter.hh_mm_ss.pass.cpp
Normal file
556
libcxx/test/std/time/time.syn/formatter.hh_mm_ss.pass.cpp
Normal file
@@ -0,0 +1,556 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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-localization
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-format
|
||||
|
||||
// TODO FMT Evaluate gcc-12 status
|
||||
// UNSUPPORTED: gcc-12
|
||||
|
||||
// TODO FMT Investigate Windows issues.
|
||||
// UNSUPPORTED: msvc, target={{.+}}-windows-gnu
|
||||
|
||||
// REQUIRES: locale.fr_FR.UTF-8
|
||||
// REQUIRES: locale.ja_JP.UTF-8
|
||||
|
||||
// <chrono>
|
||||
|
||||
// template<class Rep, class Period, class charT>
|
||||
// struct formatter<chrono::hh_mm_ss<duration<Rep, Period>>, 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 "string_literal.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
template <class CharT>
|
||||
static void test_no_chrono_specs() {
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
std::locale::global(std::locale(LOCALE_fr_FR_UTF_8));
|
||||
|
||||
// Non localized output
|
||||
check(SV("00:00:00.000"), SV("{}"), std::chrono::hh_mm_ss{0ms});
|
||||
check(SV("*00:00:00.000*"), SV("{:*^14}"), std::chrono::hh_mm_ss{0ms});
|
||||
check(SV("*00:00:00.000"), SV("{:*>13}"), std::chrono::hh_mm_ss{0ms});
|
||||
|
||||
std::locale::global(std::locale::classic());
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
static void test_valid_values() {
|
||||
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::hh_mm_ss(0s));
|
||||
|
||||
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::hh_mm_ss(23h + 31min + 30s + 123ms));
|
||||
|
||||
check(SV("-%H='03'\t"
|
||||
"%OH='03'\t"
|
||||
"%I='03'\t"
|
||||
"%OI='03'\t"
|
||||
"%M='02'\t"
|
||||
"%OM='02'\t"
|
||||
"%S='01.123456789012'\t"
|
||||
"%OS='01.123456789012'\t"
|
||||
"%p='AM'\t"
|
||||
"%R='03:02'\t"
|
||||
"%T='03:02:01.123456789012'\t"
|
||||
"%r='03:02:01 AM'\t"
|
||||
"%X='03:02:01'\t"
|
||||
"%EX='03:02:01'\t"
|
||||
"\n"),
|
||||
fmt,
|
||||
std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<int64_t, std::pico>(123456789012))));
|
||||
|
||||
// The number of fractional seconds is 0 according to the Standard
|
||||
// TODO FMT Determine what to do.
|
||||
check(SV("%H='01'\t"
|
||||
"%OH='01'\t"
|
||||
"%I='01'\t"
|
||||
"%OI='01'\t"
|
||||
"%M='01'\t"
|
||||
"%OM='01'\t"
|
||||
"%S='01'\t"
|
||||
"%OS='01'\t"
|
||||
"%p='AM'\t"
|
||||
"%R='01:01'\t"
|
||||
"%T='01:01:01'\t"
|
||||
"%r='01:01:01 AM'\t"
|
||||
"%X='01:01:01'\t"
|
||||
"%EX='01:01:01'\t"
|
||||
"\n"),
|
||||
fmt,
|
||||
std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456)));
|
||||
|
||||
// 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__)
|
||||
"%r=''\t"
|
||||
#else
|
||||
"%r='12:00:00 '\t"
|
||||
#endif
|
||||
"%X='00:00:00'\t"
|
||||
"%EX='00:00:00'\t"
|
||||
"\n"),
|
||||
lfmt,
|
||||
std::chrono::hh_mm_ss(0s));
|
||||
|
||||
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__)
|
||||
"%r=''\t"
|
||||
#else
|
||||
"%r='11:31:30 '\t"
|
||||
#endif
|
||||
"%X='23:31:30'\t"
|
||||
"%EX='23:31:30'\t"
|
||||
"\n"),
|
||||
lfmt,
|
||||
std::chrono::hh_mm_ss(23h + 31min + 30s + 123ms));
|
||||
|
||||
check(SV("-%H='03'\t"
|
||||
"%OH='03'\t"
|
||||
"%I='03'\t"
|
||||
"%OI='03'\t"
|
||||
"%M='02'\t"
|
||||
"%OM='02'\t"
|
||||
"%S='01,123456789012'\t"
|
||||
"%OS='01,123456789012'\t"
|
||||
#if defined(_AIX)
|
||||
"%p='AM'\t"
|
||||
#else
|
||||
"%p=''\t"
|
||||
#endif
|
||||
"%R='03:02'\t"
|
||||
"%T='03:02:01,123456789012'\t"
|
||||
#ifdef _WIN32
|
||||
"%r='03:02:01'\t"
|
||||
#elif defined(_AIX)
|
||||
"%r='03:02:01 AM'\t"
|
||||
#elif defined(__APPLE__)
|
||||
"%r=''\t"
|
||||
#else
|
||||
"%r='03:02:01 '\t"
|
||||
#endif
|
||||
"%X='03:02:01'\t"
|
||||
"%EX='03:02:01'\t"
|
||||
"\n"),
|
||||
lfmt,
|
||||
std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<int64_t, std::pico>(123456789012))));
|
||||
|
||||
check(SV("%H='01'\t"
|
||||
"%OH='01'\t"
|
||||
"%I='01'\t"
|
||||
"%OI='01'\t"
|
||||
"%M='01'\t"
|
||||
"%OM='01'\t"
|
||||
"%S='01'\t"
|
||||
"%OS='01'\t"
|
||||
#if defined(_AIX)
|
||||
"%p='AM'\t"
|
||||
#else
|
||||
"%p=''\t"
|
||||
#endif
|
||||
"%R='01:01'\t"
|
||||
"%T='01:01:01'\t"
|
||||
#ifdef _WIN32
|
||||
"%r='01:01:01'\t"
|
||||
#elif defined(_AIX)
|
||||
"%r='01:01:01 AM'\t"
|
||||
#elif defined(__APPLE__)
|
||||
"%r=''\t"
|
||||
#else
|
||||
"%r='01:01:01 '\t"
|
||||
#endif
|
||||
"%X='01:01:01'\t"
|
||||
"%EX='01:01:01'\t"
|
||||
"\n"),
|
||||
lfmt,
|
||||
std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456)));
|
||||
|
||||
// Use supplied locale (ja_JP). This locale has a different alternate.
|
||||
#if defined(__APPLE__) || defined(_AIX)
|
||||
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__)
|
||||
"%r='12:00:00 AM'\t"
|
||||
"%X='00時00分00秒'\t"
|
||||
"%EX='00時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__)
|
||||
"%r='11:31:30 PM'\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));
|
||||
|
||||
check(loc,
|
||||
SV("-%H='03'\t"
|
||||
"%OH='03'\t"
|
||||
"%I='03'\t"
|
||||
"%OI='03'\t"
|
||||
"%M='02'\t"
|
||||
"%OM='02'\t"
|
||||
"%S='01.123456789012'\t"
|
||||
"%OS='01.123456789012'\t"
|
||||
# if defined(__APPLE__)
|
||||
"%p='AM'\t"
|
||||
# else
|
||||
"%p='午前'\t"
|
||||
# endif
|
||||
"%R='03:02'\t"
|
||||
"%T='03:02:01.123456789012'\t"
|
||||
# if defined(__APPLE__)
|
||||
"%r='03:02:01 AM'\t"
|
||||
"%X='03時02分01秒'\t"
|
||||
"%EX='03時02分01秒'\t"
|
||||
# else
|
||||
"%r='午前03:02:01'\t"
|
||||
"%X='03:02:01'\t"
|
||||
"%EX='03:02:01'\t"
|
||||
# endif
|
||||
"\n"),
|
||||
lfmt,
|
||||
std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<int64_t, std::pico>(123456789012))));
|
||||
|
||||
check(loc,
|
||||
SV("%H='01'\t"
|
||||
"%OH='01'\t"
|
||||
"%I='01'\t"
|
||||
"%OI='01'\t"
|
||||
"%M='01'\t"
|
||||
"%OM='01'\t"
|
||||
"%S='01'\t"
|
||||
"%OS='01'\t"
|
||||
# if defined(__APPLE__)
|
||||
"%p='AM'\t"
|
||||
# else
|
||||
"%p='午前'\t"
|
||||
# endif
|
||||
"%R='01:01'\t"
|
||||
"%T='01:01:01'\t"
|
||||
# if defined(__APPLE__)
|
||||
"%r='01:01:01 AM'\t"
|
||||
"%X='01時01分01秒'\t"
|
||||
"%EX='01時01分01秒'\t"
|
||||
# else
|
||||
"%r='午前01:01:01'\t"
|
||||
"%X='01:01:01'\t"
|
||||
"%EX='01:01:01'\t"
|
||||
# endif
|
||||
"\n"),
|
||||
lfmt,
|
||||
std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456)));
|
||||
#else // defined(__APPLE__) || defined(_AIX)
|
||||
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::hh_mm_ss(0s));
|
||||
|
||||
// TODO FMT What should fractions be in alternate display mode?
|
||||
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::hh_mm_ss(23h + 31min + 30s + 123ms));
|
||||
|
||||
check(loc,
|
||||
SV("-%H='03'\t"
|
||||
"%OH='三'\t"
|
||||
"%I='03'\t"
|
||||
"%OI='三'\t"
|
||||
"%M='02'\t"
|
||||
"%OM='二'\t"
|
||||
"%S='01.123456789012'\t"
|
||||
"%OS='一.123456789012'\t"
|
||||
"%p='午前'\t"
|
||||
"%R='03:02'\t"
|
||||
"%T='03:02:01.123456789012'\t"
|
||||
"%r='午前03時02分01秒'\t"
|
||||
"%X='03時02分01秒'\t"
|
||||
"%EX='03時02分01秒'\t"
|
||||
"\n"),
|
||||
lfmt,
|
||||
std::chrono::hh_mm_ss(-(3h + 2min + 1s + std::chrono::duration<int64_t, std::pico>(123456789012))));
|
||||
|
||||
check(loc,
|
||||
SV("%H='01'\t"
|
||||
"%OH='一'\t"
|
||||
"%I='01'\t"
|
||||
"%OI='一'\t"
|
||||
"%M='01'\t"
|
||||
"%OM='一'\t"
|
||||
"%S='01'\t"
|
||||
"%OS='一'\t"
|
||||
"%p='午前'\t"
|
||||
"%R='01:01'\t"
|
||||
"%T='01:01:01'\t"
|
||||
"%r='午前01時01分01秒'\t"
|
||||
"%X='01時01分01秒'\t"
|
||||
"%EX='01時01分01秒'\t"
|
||||
"\n"),
|
||||
lfmt,
|
||||
std::chrono::hh_mm_ss(std::chrono::duration<double>(3661.123456)));
|
||||
#endif // defined(__APPLE__) || defined(_AIX)
|
||||
|
||||
std::locale::global(std::locale::classic());
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
static void test_invalid_values() {
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
// This looks odd, however the 24 hours is not valid for a 24 hour clock.
|
||||
// TODO FMT discuss what the "proper" behaviour is.
|
||||
check_exception("formatting a hour needs a valid value", SV("{:%H"), std::chrono::hh_mm_ss{24h});
|
||||
check_exception("formatting a hour needs a valid value", SV("{:%OH"), std::chrono::hh_mm_ss{24h});
|
||||
check_exception("formatting a hour needs a valid value", SV("{:%I"), std::chrono::hh_mm_ss{24h});
|
||||
check_exception("formatting a hour needs a valid value", SV("{:%OI"), std::chrono::hh_mm_ss{24h});
|
||||
check(SV("00"), SV("{:%M}"), std::chrono::hh_mm_ss{24h});
|
||||
check(SV("00"), SV("{:%OM}"), std::chrono::hh_mm_ss{24h});
|
||||
check(SV("00"), SV("{:%S}"), std::chrono::hh_mm_ss{24h});
|
||||
check(SV("00"), SV("{:%OS}"), std::chrono::hh_mm_ss{24h});
|
||||
check_exception("formatting a hour needs a valid value", SV("{:%p"), std::chrono::hh_mm_ss{24h});
|
||||
check_exception("formatting a hour needs a valid value", SV("{:%R"), std::chrono::hh_mm_ss{24h});
|
||||
check_exception("formatting a hour needs a valid value", SV("{:%T"), std::chrono::hh_mm_ss{24h});
|
||||
check_exception("formatting a hour needs a valid value", SV("{:%r"), std::chrono::hh_mm_ss{24h});
|
||||
check_exception("formatting a hour needs a valid value", SV("{:%X"), std::chrono::hh_mm_ss{24h});
|
||||
check_exception("formatting a hour needs a valid value", SV("{:%EX"), std::chrono::hh_mm_ss{24h});
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
static void test() {
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
test_no_chrono_specs<CharT>();
|
||||
test_valid_values<CharT>();
|
||||
test_invalid_values<CharT>();
|
||||
check_invalid_types<CharT>(
|
||||
{SV("H"),
|
||||
SV("I"),
|
||||
SV("M"),
|
||||
SV("S"),
|
||||
SV("p"),
|
||||
SV("r"),
|
||||
SV("R"),
|
||||
SV("T"),
|
||||
SV("X"),
|
||||
SV("OH"),
|
||||
SV("OI"),
|
||||
SV("OM"),
|
||||
SV("OS"),
|
||||
SV("EX")},
|
||||
std::chrono::hh_mm_ss{0ms});
|
||||
|
||||
check_exception("Expected '%' or '}' in the chrono format-string", SV("{:A"), std::chrono::hh_mm_ss{0ms});
|
||||
check_exception("The chrono-specs contains a '{'", SV("{:%%{"), std::chrono::hh_mm_ss{0ms});
|
||||
check_exception(
|
||||
"End of input while parsing the modifier chrono conversion-spec", SV("{:%"), std::chrono::hh_mm_ss{0ms});
|
||||
check_exception("End of input while parsing the modifier E", SV("{:%E"), std::chrono::hh_mm_ss{0ms});
|
||||
check_exception("End of input while parsing the modifier O", SV("{:%O"), std::chrono::hh_mm_ss{0ms});
|
||||
|
||||
check_exception("Expected '%' or '}' in the chrono format-string", SV("{:.3}"), std::chrono::hh_mm_ss{0ms});
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test<char>();
|
||||
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
test<wchar_t>();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -161,7 +161,7 @@ void test_P1361() {
|
||||
assert_is_formattable<std::chrono::year_month_weekday, CharT>();
|
||||
assert_is_formattable<std::chrono::year_month_weekday_last, CharT>();
|
||||
|
||||
assert_is_not_formattable<std::chrono::hh_mm_ss<std::chrono::microseconds>, CharT>();
|
||||
assert_is_formattable<std::chrono::hh_mm_ss<std::chrono::microseconds>, CharT>();
|
||||
|
||||
//assert_is_formattable<std::chrono::sys_info, CharT>();
|
||||
//assert_is_formattable<std::chrono::local_info, CharT>();
|
||||
|
||||
Reference in New Issue
Block a user