[libc++][chrono] Implements formatter duration.
Partially implements: - P1361 Integration of chrono with text formatting - P2372 Fixing locale handling in chrono formatters - LWG3270 Parsing and formatting %j with durations Completes: - P1650R0 std::chrono::days with 'd' suffix - LWG3262 Formatting of negative durations is not specified - LWG3314 Is stream insertion behavior locale dependent when Period::type is micro? Reviewed By: ldionne, #libc Differential Revision: https://reviews.llvm.org/D134742
This commit is contained in:
@@ -213,10 +213,10 @@
|
||||
"`3254 <https://wg21.link/LWG3254>`__","Strike ``stop_token``\ 's ``operator!=``\ ","Prague","",""
|
||||
"`3255 <https://wg21.link/LWG3255>`__","``span``\ 's ``array``\ constructor is too strict","Prague","|Complete|",""
|
||||
"`3260 <https://wg21.link/LWG3260>`__","``year_month*``\ arithmetic rejects durations convertible to years","Prague","","","|chrono|"
|
||||
"`3262 <https://wg21.link/LWG3262>`__","Formatting of negative durations is not specified","Prague","","","|chrono| |format|"
|
||||
"`3262 <https://wg21.link/LWG3262>`__","Formatting of negative durations is not specified","Prague","|Complete|","16.0","|chrono| |format|"
|
||||
"`3264 <https://wg21.link/LWG3264>`__","``sized_range``\ and ``ranges::size``\ redundantly use ``disable_sized_range``\ ","Prague","|Complete|","15.0","|ranges|"
|
||||
"`3269 <https://wg21.link/LWG3269>`__","Parse manipulators do not specify the result of the extraction from stream","Prague","","","|chrono|"
|
||||
"`3270 <https://wg21.link/LWG3270>`__","Parsing and formatting ``%j``\ with ``duration``\ s","Prague","","","|chrono| |format|"
|
||||
"`3270 <https://wg21.link/LWG3270>`__","Parsing and formatting ``%j``\ with ``duration``\ s","Prague","|Partial|","","|chrono| |format|"
|
||||
"`3280 <https://wg21.link/LWG3280>`__","View converting constructors can cause constraint recursion and are unneeded","Prague","|Complete|","15.0","|ranges|"
|
||||
"`3281 <https://wg21.link/LWG3281>`__","Conversion from ``*pair-like*``\ types to ``subrange``\ is a silent semantic promotion","Prague","|Complete|","15.0","|ranges|"
|
||||
"`3282 <https://wg21.link/LWG3282>`__","``subrange``\ converting constructor should disallow derived to base conversions","Prague","|Complete|","15.0","|ranges|"
|
||||
@@ -236,7 +236,7 @@
|
||||
"`3307 <https://wg21.link/LWG3307>`__","``std::allocator<void>().allocate(n)``\ ","Prague","",""
|
||||
"`3310 <https://wg21.link/LWG3310>`__","Replace ``SIZE_MAX``\ with ``numeric_limits<size_t>::max()``\ ","Prague","",""
|
||||
"`3313 <https://wg21.link/LWG3313>`__","``join_view::iterator::operator--``\ is incorrectly constrained","Prague","|Complete|","14.0","|ranges|"
|
||||
"`3314 <https://wg21.link/LWG3314>`__","Is stream insertion behavior locale dependent when ``Period::type``\ is ``micro``\ ?","Prague","","","|chrono|"
|
||||
"`3314 <https://wg21.link/LWG3314>`__","Is stream insertion behavior locale dependent when ``Period::type``\ is ``micro``\ ?","Prague","|Complete|","16.0","|chrono|"
|
||||
"`3315 <https://wg21.link/LWG3315>`__","Correct Allocator Default Behavior","Prague","",""
|
||||
"`3316 <https://wg21.link/LWG3316>`__","Correctly define epoch for ``utc_clock``\ / ``utc_timepoint``\ ","Prague","","","|chrono|"
|
||||
"`3317 <https://wg21.link/LWG3317>`__","Incorrect ``operator<<``\ for floating-point durations","Prague","","","|chrono|"
|
||||
|
||||
|
@@ -127,7 +127,7 @@
|
||||
"`P1638R1 <https://wg21.link/P1638R1>`__","LWG","basic_istream_view::iterator should not be copyable","Cologne","|Complete|","16.0"
|
||||
"`P1643R1 <https://wg21.link/P1643R1>`__","LWG","Add wait/notify to atomic_ref","Cologne","",""
|
||||
"`P1644R0 <https://wg21.link/P1644R0>`__","LWG","Add wait/notify to atomic<shared_ptr>","Cologne","",""
|
||||
"`P1650R0 <https://wg21.link/P1650R0>`__","LWG","Output std::chrono::days with 'd' suffix","Cologne","",""
|
||||
"`P1650R0 <https://wg21.link/P1650R0>`__","LWG","Output std::chrono::days with 'd' suffix","Cologne","","|Complete|","16.0"
|
||||
"`P1651R0 <https://wg21.link/P1651R0>`__","LWG","bind_front should not unwrap reference_wrapper","Cologne","|Complete|","13.0"
|
||||
"`P1652R1 <https://wg21.link/P1652R1>`__","LWG","Printf corner cases in std::format","Cologne","|Complete|","14.0"
|
||||
"`P1661R1 <https://wg21.link/P1661R1>`__","LWG","Remove dedicated precalculated hash lookup interface","Cologne","|Nothing To Do|",""
|
||||
@@ -201,7 +201,7 @@
|
||||
"`P2328R1 <https://wg21.link/P2328R1>`__","LWG",join_view should join all views of ranges,"June 2021","",""
|
||||
"`P2367R0 <https://wg21.link/P2367R0>`__","LWG",Remove misuses of list-initialization from Clause 24,"June 2021","",""
|
||||
"","","","","",""
|
||||
"`P2372R3 <https://wg21.link/P2372R3>`__","LWG","Fixing locale handling in chrono formatters","October 2021","",""
|
||||
"`P2372R3 <https://wg21.link/P2372R3>`__","LWG","Fixing locale handling in chrono formatters","October 2021","|In Progress|",""
|
||||
"`P2415R2 <https://wg21.link/P2415R2>`__","LWG","What is a ``view``","October 2021","|Complete|","14.0"
|
||||
"`P2418R2 <https://wg21.link/P2418R2>`__","LWG","Add support for ``std::generator``-like types to ``std::format``","October 2021","|Complete|","15.0"
|
||||
"`P2432R1 <https://wg21.link/P2432R1>`__","LWG","Fix ``istream_view``","October 2021","|Complete|","16.0"
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
Section,Description,Dependencies,Assignee,Status,First released version
|
||||
`P1361 <https://wg21.link/P1361>`__ `P2372 <https://wg21.link/P2372>`__,"Formatting chrono"
|
||||
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::duration<Rep, Period>``",,Mark de Wever,|In Progress|,
|
||||
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::duration<Rep, Period>``",,Mark de Wever,|Complete|, Clang 16
|
||||
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_time<Duration>``",,Mark de Wever,|In Progress|,
|
||||
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::utc_time<Duration>``",A ``<chrono>`` implementation,Not assigned,,,
|
||||
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::tai_time<Duration>``",A ``<chrono>`` implementation,Not assigned,,,
|
||||
|
||||
|
@@ -11,10 +11,12 @@
|
||||
#define _LIBCPP___CHRONO_CONVERT_TO_TM_H
|
||||
|
||||
#include <__chrono/day.h>
|
||||
#include <__chrono/duration.h>
|
||||
#include <__chrono/month.h>
|
||||
#include <__chrono/year.h>
|
||||
#include <__concepts/same_as.h>
|
||||
#include <__config>
|
||||
#include <cstdint>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
@@ -24,23 +26,34 @@ _LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if _LIBCPP_STD_VER > 17
|
||||
|
||||
// Convert a chrono calendar time point to the given tm type,
|
||||
// Convert a chrono (calendar) time point, or dururation to the given _Tm type,
|
||||
// which must have the same properties as std::tm.
|
||||
template <class _Tm, class _ChronoCalendarTimePoint>
|
||||
_LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoCalendarTimePoint& __value) {
|
||||
template <class _Tm, class _ChronoT>
|
||||
_LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
|
||||
_Tm __result = {};
|
||||
# ifdef __GLIBC__
|
||||
__result.tm_zone = "UTC";
|
||||
# endif
|
||||
|
||||
if constexpr (same_as<_ChronoCalendarTimePoint, chrono::day>)
|
||||
if constexpr (chrono::__is_duration<_ChronoT>::value) {
|
||||
// [time.format]/6
|
||||
// ... However, if a flag refers to a "time of day" (e.g. %H, %I, %p,
|
||||
// etc.), then a specialization of duration is interpreted as the time of
|
||||
// day elapsed since midnight.
|
||||
uint64_t __sec = chrono::duration_cast<chrono::seconds>(__value).count();
|
||||
__sec %= 24 * 3600;
|
||||
__result.tm_hour = __sec / 3600;
|
||||
__sec %= 3600;
|
||||
__result.tm_min = __sec / 60;
|
||||
__result.tm_sec = __sec % 60;
|
||||
} else if constexpr (same_as<_ChronoT, chrono::day>)
|
||||
__result.tm_mday = static_cast<unsigned>(__value);
|
||||
else if constexpr (same_as<_ChronoCalendarTimePoint, chrono::month>)
|
||||
else if constexpr (same_as<_ChronoT, chrono::month>)
|
||||
__result.tm_mon = static_cast<unsigned>(__value) - 1;
|
||||
else if constexpr (same_as<_ChronoCalendarTimePoint, chrono::year>)
|
||||
else if constexpr (same_as<_ChronoT, chrono::year>)
|
||||
__result.tm_year = static_cast<int>(__value) - 1900;
|
||||
else
|
||||
static_assert(sizeof(_ChronoCalendarTimePoint) == 0, "Add the missing type specialization");
|
||||
static_assert(sizeof(_ChronoT) == 0, "Add the missing type specialization");
|
||||
|
||||
return __result;
|
||||
}
|
||||
|
||||
@@ -12,10 +12,15 @@
|
||||
|
||||
#include <__chrono/convert_to_tm.h>
|
||||
#include <__chrono/day.h>
|
||||
#include <__chrono/duration.h>
|
||||
#include <__chrono/hh_mm_ss.h>
|
||||
#include <__chrono/month.h>
|
||||
#include <__chrono/ostream.h>
|
||||
#include <__chrono/parser_std_format_spec.h>
|
||||
#include <__chrono/statically_widen.h>
|
||||
#include <__chrono/time_point.h>
|
||||
#include <__chrono/year.h>
|
||||
#include <__concepts/arithmetic.h>
|
||||
#include <__concepts/same_as.h>
|
||||
#include <__config>
|
||||
#include <__format/concepts.h>
|
||||
@@ -60,6 +65,47 @@ namespace __formatter {
|
||||
///
|
||||
/// When no chrono-specs are provided it uses the stream 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) {
|
||||
__sstr << std::use_facet<numpunct<_CharT>>(__sstr.getloc()).decimal_point();
|
||||
|
||||
auto __fraction = __value - chrono::duration_cast<chrono::seconds>(__value);
|
||||
if constexpr (chrono::treat_as_floating_point_v<typename _Tp::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
|
||||
// seconds, then the format is a decimal floating-point number with a
|
||||
// fixed format and a precision matching that of the precision of the
|
||||
// input (or to a microseconds precision if the conversion to
|
||||
// floating-point decimal seconds cannot be made within 18 fractional
|
||||
// digits).
|
||||
//
|
||||
// This matches the behaviour of MSVC STL, fmtlib interprets this
|
||||
// differently and uses 3 decimals.
|
||||
// https://godbolt.org/z/6dsbnW8ba
|
||||
std::format_to(std::ostreambuf_iterator<_CharT>{__sstr},
|
||||
_LIBCPP_STATICALLY_WIDEN(_CharT, "{:0{}.0f}"),
|
||||
__fraction.count(),
|
||||
chrono::hh_mm_ss<_Tp>::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);
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
consteval bool __use_fraction() {
|
||||
if constexpr (chrono::__is_duration<_Tp>::value)
|
||||
return chrono::hh_mm_ss<_Tp>::fractional_width;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class _CharT>
|
||||
_LIBCPP_HIDE_FROM_ABI void __format_year(int __year, basic_stringstream<_CharT>& __sstr) {
|
||||
if (__year < 0) {
|
||||
@@ -119,32 +165,73 @@ _LIBCPP_HIDE_FROM_ABI void __format_chrono_using_chrono_specs(
|
||||
__facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), __s, __it + 1);
|
||||
} break;
|
||||
|
||||
// Unlike time_put and strftime the formatting library requires %Y
|
||||
//
|
||||
// [tab:time.format.spec]
|
||||
// The year as a decimal number. If the result is less than four digits
|
||||
// it is left-padded with 0 to four digits.
|
||||
//
|
||||
// This means years in the range (-1000, 1000) need manual formatting.
|
||||
// It's unclear whether %EY needs the same treatment. For example the
|
||||
// Japanese EY contains the era name and year. This is zero-padded to 2
|
||||
// digits in time_put (note that older glibc versions didn't do
|
||||
// padding.) However most eras won't reach 100 years, let alone 1000.
|
||||
// So padding to 4 digits seems unwanted for Japanese.
|
||||
//
|
||||
// The same applies to %Ex since that too depends on the era.
|
||||
//
|
||||
// %x the locale's date representation is currently doesn't handle the
|
||||
// zero-padding too.
|
||||
//
|
||||
// The 4 digits can be implemented better at a later time. On POSIX
|
||||
// systems the required information can be extracted by nl_langinfo
|
||||
// https://man7.org/linux/man-pages/man3/nl_langinfo.3.html
|
||||
//
|
||||
// Note since year < -1000 is expected to be rare it uses the more
|
||||
// expensive year routine.
|
||||
//
|
||||
// TODO FMT evaluate the comment above.
|
||||
case _CharT('j'):
|
||||
if constexpr (chrono::__is_duration<_Tp>::value)
|
||||
// Converting a duration where the period has a small ratio to days
|
||||
// may fail to compile. This due to loss of precision in the
|
||||
// conversion. In order to avoid that issue convert to seconds as
|
||||
// an intemediate step.
|
||||
__sstr << chrono::duration_cast<chrono::days>(chrono::duration_cast<chrono::seconds>(__value)).count();
|
||||
else
|
||||
__facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), __s, __it + 1);
|
||||
break;
|
||||
|
||||
case _CharT('q'):
|
||||
if constexpr (chrono::__is_duration<_Tp>::value) {
|
||||
__sstr << chrono::__units_suffix<_CharT, typename _Tp::period>();
|
||||
break;
|
||||
}
|
||||
__builtin_unreachable();
|
||||
|
||||
case _CharT('Q'):
|
||||
// TODO FMT Determine the proper ideas
|
||||
// - Should it honour the precision?
|
||||
// - Shoult it honour the locale setting for the separators?
|
||||
// The wording for Q doesn't use the word locale and the effect of
|
||||
// precision is unspecified.
|
||||
//
|
||||
// MSVC STL ignores precision but uses separator
|
||||
// FMT honours precision and has a bug for separator
|
||||
// https://godbolt.org/z/78b7sMxns
|
||||
if constexpr (chrono::__is_duration<_Tp>::value) {
|
||||
__sstr << format(_LIBCPP_STATICALLY_WIDEN(_CharT, "{}"), __value.count());
|
||||
break;
|
||||
}
|
||||
__builtin_unreachable();
|
||||
|
||||
case _CharT('S'):
|
||||
case _CharT('T'):
|
||||
__facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), __s, __it + 1);
|
||||
if constexpr (__use_fraction<_Tp>())
|
||||
__formatter::__format_sub_seconds(__value, __sstr);
|
||||
break;
|
||||
|
||||
// Unlike time_put and strftime the formatting library requires %Y
|
||||
//
|
||||
// [tab:time.format.spec]
|
||||
// The year as a decimal number. If the result is less than four digits
|
||||
// it is left-padded with 0 to four digits.
|
||||
//
|
||||
// This means years in the range (-1000, 1000) need manual formatting.
|
||||
// It's unclear whether %EY needs the same treatment. For example the
|
||||
// Japanese EY contains the era name and year. This is zero-padded to 2
|
||||
// digits in time_put (note that older glibc versions didn't do
|
||||
// padding.) However most eras won't reach 100 years, let alone 1000.
|
||||
// So padding to 4 digits seems unwanted for Japanese.
|
||||
//
|
||||
// The same applies to %Ex since that too depends on the era.
|
||||
//
|
||||
// %x the locale's date representation is currently doesn't handle the
|
||||
// zero-padding too.
|
||||
//
|
||||
// The 4 digits can be implemented better at a later time. On POSIX
|
||||
// systems the required information can be extracted by nl_langinfo
|
||||
// https://man7.org/linux/man-pages/man3/nl_langinfo.3.html
|
||||
//
|
||||
// Note since year < -1000 is expected to be rare it uses the more
|
||||
// expensive year routine.
|
||||
//
|
||||
// TODO FMT evaluate the comment above.
|
||||
|
||||
# if defined(__GLIBC__) || defined(_AIX)
|
||||
case _CharT('y'):
|
||||
@@ -162,6 +249,18 @@ _LIBCPP_HIDE_FROM_ABI void __format_chrono_using_chrono_specs(
|
||||
} break;
|
||||
|
||||
case _CharT('O'):
|
||||
if constexpr (__use_fraction<_Tp>()) {
|
||||
// Handle OS using the normal representation for the non-fractional
|
||||
// part. There seems to be no locale information regarding how the
|
||||
// fractional part should be formatted.
|
||||
if (*(__it + 1) == 'S') {
|
||||
++__it;
|
||||
__facet.put({__sstr}, __sstr, _CharT(' '), std::addressof(__t), __s, __it + 1);
|
||||
__formatter::__format_sub_seconds(__value, __sstr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
[[fallthrough]];
|
||||
case _CharT('E'):
|
||||
++__it;
|
||||
[[fallthrough]];
|
||||
@@ -207,10 +306,19 @@ __format_chrono(const _Tp& __value,
|
||||
if (__chrono_specs.empty())
|
||||
__sstr << __value;
|
||||
else {
|
||||
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 (chrono::__is_duration<_Tp>::value) {
|
||||
if (__value < __value.zero())
|
||||
__sstr << _CharT('-');
|
||||
__formatter::__format_chrono_using_chrono_specs(chrono::abs(__value), __sstr, __chrono_specs);
|
||||
// TODO FMT When keeping the precision it will truncate the string.
|
||||
// Note that the behaviour what the precision does isn't specified.
|
||||
__specs.__precision_ = -1;
|
||||
} else {
|
||||
if (__specs.__chrono_.__month_name_ && !__formatter::__month_name_ok(__value))
|
||||
std::__throw_format_error("formatting a month name from an invalid month number");
|
||||
|
||||
__formatter::__format_chrono_using_chrono_specs(__value, __sstr, __chrono_specs);
|
||||
__formatter::__format_chrono_using_chrono_specs(__value, __sstr, __chrono_specs);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO FMT Use the stringstream's view after P0408R7 has been implemented.
|
||||
@@ -238,6 +346,28 @@ public:
|
||||
__format_spec::__parser_chrono<_CharT> __parser_;
|
||||
};
|
||||
|
||||
template <class _Rep, class _Period, __fmt_char_type _CharT>
|
||||
struct formatter<chrono::duration<_Rep, _Period>, _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()) {
|
||||
// [time.format]/1
|
||||
// Giving a precision specification in the chrono-format-spec is valid only
|
||||
// for std::chrono::duration types where the representation type Rep is a
|
||||
// floating-point type. For all other Rep types, an exception of type
|
||||
// format_error is thrown if the chrono-format-spec contains a precision
|
||||
// specification.
|
||||
//
|
||||
// Note this doesn't refer to chrono::treat_as_floating_point_v<_Rep>.
|
||||
if constexpr (std::floating_point<_Rep>)
|
||||
return _Base::__parse(__parse_ctx, __format_spec::__fields_chrono_fractional, __format_spec::__flags::__duration);
|
||||
else
|
||||
return _Base::__parse(__parse_ctx, __format_spec::__fields_chrono, __format_spec::__flags::__duration);
|
||||
}
|
||||
};
|
||||
|
||||
template <__fmt_char_type _CharT>
|
||||
struct _LIBCPP_TEMPLATE_VIS _LIBCPP_AVAILABILITY_FORMAT formatter<chrono::day, _CharT>
|
||||
: public __formatter_chrono<_CharT> {
|
||||
|
||||
@@ -11,12 +11,15 @@
|
||||
#define _LIBCPP___CHRONO_OSTREAM_H
|
||||
|
||||
#include <__chrono/day.h>
|
||||
#include <__chrono/duration.h>
|
||||
#include <__chrono/month.h>
|
||||
#include <__chrono/statically_widen.h>
|
||||
#include <__chrono/year.h>
|
||||
#include <__concepts/same_as.h>
|
||||
#include <__config>
|
||||
#include <__format/format_functions.h>
|
||||
#include <ostream>
|
||||
#include <ratio>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
@@ -28,6 +31,71 @@ _LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
namespace chrono {
|
||||
|
||||
// Depending on the type the return is a const _CharT* or a basic_string<_CharT>
|
||||
template <class _CharT, class _Period>
|
||||
_LIBCPP_HIDE_FROM_ABI auto __units_suffix() {
|
||||
// TODO FMT LWG issue the suffixes are always char and not STATICALLY-WIDEN'ed.
|
||||
if constexpr (same_as<typename _Period::type, atto>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "as");
|
||||
else if constexpr (same_as<typename _Period::type, femto>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "fs");
|
||||
else if constexpr (same_as<typename _Period::type, pico>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "ps");
|
||||
else if constexpr (same_as<typename _Period::type, nano>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "ns");
|
||||
else if constexpr (same_as<typename _Period::type, micro>)
|
||||
# ifndef _LIBCPP_HAS_NO_UNICODE
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "\u00b5s");
|
||||
# else
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "us");
|
||||
# endif
|
||||
else if constexpr (same_as<typename _Period::type, milli>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "ms");
|
||||
else if constexpr (same_as<typename _Period::type, centi>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "cs");
|
||||
else if constexpr (same_as<typename _Period::type, deci>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "ds");
|
||||
else if constexpr (same_as<typename _Period::type, ratio<1>>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "s");
|
||||
else if constexpr (same_as<typename _Period::type, deca>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "das");
|
||||
else if constexpr (same_as<typename _Period::type, hecto>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "hs");
|
||||
else if constexpr (same_as<typename _Period::type, kilo>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "ks");
|
||||
else if constexpr (same_as<typename _Period::type, mega>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "Ms");
|
||||
else if constexpr (same_as<typename _Period::type, giga>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "Gs");
|
||||
else if constexpr (same_as<typename _Period::type, tera>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "Ts");
|
||||
else if constexpr (same_as<typename _Period::type, peta>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "Ps");
|
||||
else if constexpr (same_as<typename _Period::type, exa>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "Es");
|
||||
else if constexpr (same_as<typename _Period::type, ratio<60>>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "min");
|
||||
else if constexpr (same_as<typename _Period::type, ratio<3600>>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "h");
|
||||
else if constexpr (same_as<typename _Period::type, ratio<86400>>)
|
||||
return _LIBCPP_STATICALLY_WIDEN(_CharT, "d");
|
||||
else if constexpr (_Period::den == 1)
|
||||
return std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "[{}]s"), _Period::num);
|
||||
else
|
||||
return std::format(_LIBCPP_STATICALLY_WIDEN(_CharT, "[{}/{}]s"), _Period::num, _Period::den);
|
||||
}
|
||||
|
||||
template <class _CharT, class _Traits, class _Rep, class _Period>
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT basic_ostream<_CharT, _Traits>&
|
||||
operator<<(basic_ostream<_CharT, _Traits>& __os, const duration<_Rep, _Period>& __d) {
|
||||
basic_ostringstream<_CharT, _Traits> __s;
|
||||
__s.flags(__os.flags());
|
||||
__s.imbue(__os.getloc());
|
||||
__s.precision(__os.precision());
|
||||
__s << __d.count() << chrono::__units_suffix<_CharT, _Period>();
|
||||
return __os << __s.str();
|
||||
}
|
||||
|
||||
template <class _CharT, class _Traits>
|
||||
_LIBCPP_HIDE_FROM_ABI _LIBCPP_AVAILABILITY_FORMAT basic_ostream<_CharT, _Traits>&
|
||||
operator<<(basic_ostream<_CharT, _Traits>& __os, const day& __d) {
|
||||
|
||||
@@ -29,6 +29,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
|
||||
namespace __format_spec {
|
||||
|
||||
// By not placing this constant in the formatter class it's not duplicated for char and wchar_t
|
||||
inline constexpr __fields __fields_chrono_fractional{
|
||||
.__precision_ = true, .__locale_specific_form_ = true, .__type_ = false};
|
||||
inline constexpr __fields __fields_chrono{.__locale_specific_form_ = true, .__type_ = false};
|
||||
|
||||
/// Flags available or required in a chrono type.
|
||||
|
||||
@@ -207,7 +207,11 @@ template <class ToDuration, class Rep, class Period>
|
||||
template <class ToDuration, class Rep, class Period>
|
||||
constexpr ToDuration round(const duration<Rep, Period>& d); // C++17
|
||||
|
||||
// duration I/O is elsewhere
|
||||
// duration I/O
|
||||
template<class charT, class traits, class Rep, class Period> // C++20
|
||||
basic_ostream<charT, traits>&
|
||||
operator<<(basic_ostream<charT, traits>& os,
|
||||
const duration<Rep, Period>& d);
|
||||
|
||||
// time_point arithmetic (all constexpr in C++14)
|
||||
template <class Clock, class Duration1, class Rep2, class Period2>
|
||||
@@ -625,6 +629,8 @@ bool operator>=(const time_zone& x, const time_zone& y) noexcept;
|
||||
} // chrono
|
||||
|
||||
namespace std {
|
||||
template<class Rep, class Period, class charT>
|
||||
struct formatter<chrono::duration<Rep, Period>, charT>; // C++20
|
||||
template<class charT> struct formatter<chrono::day, charT>; // C++20
|
||||
template<class charT> struct formatter<chrono::month, charT>; // C++20
|
||||
template<class charT> struct formatter<chrono::year, charT>; // C++20
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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 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 = ratio<1>> class duration;
|
||||
|
||||
// template<class charT, class traits, class Rep, class Period>
|
||||
// basic_ostream<charT, traits>&
|
||||
// operator<<(basic_ostream<charT, traits>& os,
|
||||
// const duration<Rep, Period>& d);
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#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 Rep, class Period>
|
||||
static std::basic_string<CharT> stream_c_locale(std::chrono::duration<Rep, Period> duration) {
|
||||
std::basic_stringstream<CharT> sstr;
|
||||
sstr.precision(4);
|
||||
sstr << std::fixed << duration;
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
template <class CharT, class Rep, class Period>
|
||||
static std::basic_string<CharT> stream_fr_FR_locale(std::chrono::duration<Rep, Period> duration) {
|
||||
std::basic_stringstream<CharT> sstr;
|
||||
const std::locale locale(LOCALE_fr_FR_UTF_8);
|
||||
sstr.imbue(locale);
|
||||
sstr.precision(4);
|
||||
sstr << std::fixed << duration;
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
template <class CharT, class Rep, class Period>
|
||||
static std::basic_string<CharT> stream_ja_JP_locale(std::chrono::duration<Rep, Period> duration) {
|
||||
std::basic_stringstream<CharT> sstr;
|
||||
const std::locale locale(LOCALE_ja_JP_UTF_8);
|
||||
sstr.imbue(locale);
|
||||
sstr.precision(4);
|
||||
sstr << std::fixed << duration;
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
static void test_values() {
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
assert(stream_c_locale<CharT>(-1'000'000s) == SV("-1000000s"));
|
||||
assert(stream_c_locale<CharT>(1'000'000s) == SV("1000000s"));
|
||||
assert(stream_c_locale<CharT>(-1'000.123456s) == SV("-1000.1235s"));
|
||||
assert(stream_c_locale<CharT>(1'000.123456s) == SV("1000.1235s"));
|
||||
|
||||
if constexpr (std::same_as<CharT, char>) {
|
||||
#if defined(__APPLE__)
|
||||
assert(stream_fr_FR_locale<CharT>(-1'000'000s) == SV("-1000000s"));
|
||||
assert(stream_fr_FR_locale<CharT>(1'000'000s) == SV("1000000s"));
|
||||
assert(stream_fr_FR_locale<CharT>(-1'000.123456s) == SV("-1000,1235s"));
|
||||
assert(stream_fr_FR_locale<CharT>(1'000.123456s) == SV("1000,1235s"));
|
||||
#else
|
||||
assert(stream_fr_FR_locale<CharT>(-1'000'000s) == SV("-1 000 000s"));
|
||||
assert(stream_fr_FR_locale<CharT>(1'000'000s) == SV("1 000 000s"));
|
||||
assert(stream_fr_FR_locale<CharT>(-1'000.123456s) == SV("-1 000,1235s"));
|
||||
assert(stream_fr_FR_locale<CharT>(1'000.123456s) == SV("1 000,1235s"));
|
||||
#endif
|
||||
} else {
|
||||
#ifdef _WIN32
|
||||
assert(stream_fr_FR_locale<CharT>(-1'000'000s) == SV("-1\u00A0000\u00A0000s"));
|
||||
assert(stream_fr_FR_locale<CharT>(1'000'000s) == SV("1\u00A0000\u00A0000s"));
|
||||
assert(stream_fr_FR_locale<CharT>(-1'000.123456s) == SV("-1\u00A0000,1235s"));
|
||||
assert(stream_fr_FR_locale<CharT>(1'000.123456s) == SV("1\u00A0000,1235s"));
|
||||
#elif defined(__APPLE__)
|
||||
assert(stream_fr_FR_locale<CharT>(-1'000'000s) == SV("-1000000s"));
|
||||
assert(stream_fr_FR_locale<CharT>(1'000'000s) == SV("1000000s"));
|
||||
assert(stream_fr_FR_locale<CharT>(-1'000.123456s) == SV("-1000,1235s"));
|
||||
assert(stream_fr_FR_locale<CharT>(1'000.123456s) == SV("1000,1235s"));
|
||||
#else
|
||||
assert(stream_fr_FR_locale<CharT>(-1'000'000s) == SV("-1\u202f000\u202f000s"));
|
||||
assert(stream_fr_FR_locale<CharT>(1'000'000s) == SV("1\u202f000\u202f000s"));
|
||||
assert(stream_fr_FR_locale<CharT>(-1'000.123456s) == SV("-1\u202f000,1235s"));
|
||||
assert(stream_fr_FR_locale<CharT>(1'000.123456s) == SV("1\u202f000,1235s"));
|
||||
#endif
|
||||
}
|
||||
|
||||
assert(stream_ja_JP_locale<CharT>(-1'000'000s) == SV("-1,000,000s"));
|
||||
assert(stream_ja_JP_locale<CharT>(1'000'000s) == SV("1,000,000s"));
|
||||
assert(stream_ja_JP_locale<CharT>(-1'000.123456s) == SV("-1,000.1235s"));
|
||||
assert(stream_ja_JP_locale<CharT>(1'000.123456s) == SV("1,000.1235s"));
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
static void test_units() {
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
// C locale
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::atto>(0)) == SV("0as"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::femto>(0)) == SV("0fs"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::pico>(0)) == SV("0ps"));
|
||||
assert(stream_c_locale<CharT>(0ns) == SV("0ns"));
|
||||
#ifndef TEST_HAS_NO_UNICODE
|
||||
assert(stream_c_locale<CharT>(0us) == SV("0\u00b5s"));
|
||||
#else
|
||||
assert(stream_c_locale<CharT>(0us) == SV("0us"));
|
||||
#endif
|
||||
assert(stream_c_locale<CharT>(0ms) == SV("0ms"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::centi>(0)) == SV("0cs"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::deci>(0)) == SV("0ds"));
|
||||
|
||||
assert(stream_c_locale<CharT>(0s) == SV("0s"));
|
||||
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::deca>(0)) == SV("0das"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::hecto>(0)) == SV("0hs"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::kilo>(0)) == SV("0ks"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::mega>(0)) == SV("0Ms"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::giga>(0)) == SV("0Gs"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::tera>(0)) == SV("0Ts"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::peta>(0)) == SV("0Ps"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::exa>(0)) == SV("0Es"));
|
||||
|
||||
assert(stream_c_locale<CharT>(0min) == SV("0min"));
|
||||
assert(stream_c_locale<CharT>(0h) == SV("0h"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::ratio<86400>>(0)) == SV("0d"));
|
||||
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::ratio<42>>(0)) == SV("0[42]s"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::ratio<33, 3>>(0)) == SV("0[11]s"));
|
||||
assert(stream_c_locale<CharT>(std::chrono::duration<int, std::ratio<11, 9>>(0)) == SV("0[11/9]s"));
|
||||
|
||||
// fr_FR locale
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::atto>(0)) == SV("0as"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::femto>(0)) == SV("0fs"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::pico>(0)) == SV("0ps"));
|
||||
assert(stream_fr_FR_locale<CharT>(0ns) == SV("0ns"));
|
||||
#ifndef TEST_HAS_NO_UNICODE
|
||||
assert(stream_fr_FR_locale<CharT>(0us) == SV("0\u00b5s"));
|
||||
#else
|
||||
assert(stream_fr_FR_locale<CharT>(0us) == SV("0us"));
|
||||
#endif
|
||||
assert(stream_fr_FR_locale<CharT>(0ms) == SV("0ms"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::centi>(0)) == SV("0cs"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::deci>(0)) == SV("0ds"));
|
||||
|
||||
assert(stream_fr_FR_locale<CharT>(0s) == SV("0s"));
|
||||
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::deca>(0)) == SV("0das"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::hecto>(0)) == SV("0hs"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::kilo>(0)) == SV("0ks"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::mega>(0)) == SV("0Ms"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::giga>(0)) == SV("0Gs"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::tera>(0)) == SV("0Ts"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::peta>(0)) == SV("0Ps"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::exa>(0)) == SV("0Es"));
|
||||
|
||||
assert(stream_fr_FR_locale<CharT>(0min) == SV("0min"));
|
||||
assert(stream_fr_FR_locale<CharT>(0h) == SV("0h"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::ratio<86400>>(0)) == SV("0d"));
|
||||
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::ratio<42>>(0)) == SV("0[42]s"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::ratio<33, 3>>(0)) == SV("0[11]s"));
|
||||
assert(stream_fr_FR_locale<CharT>(std::chrono::duration<int, std::ratio<11, 9>>(0)) == SV("0[11/9]s"));
|
||||
|
||||
// ja_JP locale
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::atto>(0)) == SV("0as"));
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::femto>(0)) == SV("0fs"));
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::pico>(0)) == SV("0ps"));
|
||||
assert(stream_ja_JP_locale<CharT>(0ns) == SV("0ns"));
|
||||
#ifndef TEST_HAS_NO_UNICODE
|
||||
assert(stream_ja_JP_locale<CharT>(0us) == SV("0\u00b5s"));
|
||||
#else
|
||||
assert(stream_ja_JP_locale<CharT>(0us) == SV("0us"));
|
||||
#endif
|
||||
assert(stream_ja_JP_locale<CharT>(0ms) == SV("0ms"));
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::centi>(0)) == SV("0cs"));
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::deci>(0)) == SV("0ds"));
|
||||
|
||||
assert(stream_ja_JP_locale<CharT>(0s) == SV("0s"));
|
||||
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::deca>(0)) == SV("0das"));
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::hecto>(0)) == SV("0hs"));
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::kilo>(0)) == SV("0ks"));
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::mega>(0)) == SV("0Ms"));
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::giga>(0)) == SV("0Gs"));
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::tera>(0)) == SV("0Ts"));
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::peta>(0)) == SV("0Ps"));
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::exa>(0)) == SV("0Es"));
|
||||
|
||||
assert(stream_ja_JP_locale<CharT>(0min) == SV("0min"));
|
||||
assert(stream_ja_JP_locale<CharT>(0h) == SV("0h"));
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::ratio<86400>>(0)) == SV("0d"));
|
||||
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::ratio<42>>(0)) == SV("0[42]s"));
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::ratio<33, 3>>(0)) == SV("0[11]s"));
|
||||
assert(stream_ja_JP_locale<CharT>(std::chrono::duration<int, std::ratio<11, 9>>(0)) == SV("0[11/9]s"));
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
static void test() {
|
||||
test_values<CharT>();
|
||||
test_units<CharT>();
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test<char>();
|
||||
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
test<wchar_t>();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
1091
libcxx/test/std/time/time.syn/formatter.duration.pass.cpp
Normal file
1091
libcxx/test/std/time/time.syn/formatter.duration.pass.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -130,7 +130,7 @@ void test_P1361() {
|
||||
// In libc++ std:::ostringstream requires localization support.
|
||||
#ifndef TEST_HAS_NO_LOCALIZATION
|
||||
|
||||
assert_is_not_formattable<std::chrono::microseconds, CharT>();
|
||||
assert_is_formattable<std::chrono::microseconds, CharT>();
|
||||
|
||||
assert_is_not_formattable<std::chrono::sys_time<std::chrono::microseconds>, CharT>();
|
||||
//assert_is_formattable<std::chrono::utc_time<std::chrono::microseconds>, CharT>();
|
||||
|
||||
Reference in New Issue
Block a user