Skip to content

Commit 96f3033

Browse files
committed
[libc++][chrono] Adds formatter file_time.
Reviewed By: #libc, ldionne Differential Revision: https://reviews.llvm.org/D148928
1 parent 30e1e29 commit 96f3033

File tree

7 files changed

+1185
-18
lines changed

7 files changed

+1185
-18
lines changed

libcxx/docs/Status/FormatPaper.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
55
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::utc_time<Duration>``",A ``<chrono>`` implementation,Not assigned,,,
66
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::tai_time<Duration>``",A ``<chrono>`` implementation,Not assigned,,,
77
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::gps_time<Duration>``",A ``<chrono>`` implementation,Not assigned,,,
8-
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::file_time<Duration>``",,Mark de Wever,|In Progress|,,
8+
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::file_time<Duration>``",,Mark de Wever,|Complete|, Clang 17
99
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_time<Duration>``",,Mark de Wever,|In Progress|,,
1010
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local-time-format-t<Duration>``",A ``<chrono>`` implementation,Not assigned,,,
1111
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::day``",,Mark de Wever,|Complete|, Clang 16

libcxx/include/__chrono/convert_to_tm.h

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <__chrono/concepts.h>
1414
#include <__chrono/day.h>
1515
#include <__chrono/duration.h>
16+
#include <__chrono/file_clock.h>
1617
#include <__chrono/hh_mm_ss.h>
1718
#include <__chrono/month.h>
1819
#include <__chrono/month_weekday.h>
@@ -73,6 +74,24 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _Date& __date, chrono::weekday _
7374
return __result;
7475
}
7576

77+
template <class _Tm, class _Duration>
78+
_LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const chrono::sys_time<_Duration> __tp) {
79+
chrono::sys_days __days = chrono::time_point_cast<chrono::days>(__tp);
80+
chrono::year_month_day __ymd{__days};
81+
82+
_Tm __result = std::__convert_to_tm<_Tm>(chrono::year_month_day{__ymd}, chrono::weekday{__days});
83+
84+
uint64_t __sec =
85+
chrono::duration_cast<chrono::seconds>(__tp - chrono::time_point_cast<chrono::seconds>(__days)).count();
86+
__sec %= 24 * 3600;
87+
__result.tm_hour = __sec / 3600;
88+
__sec %= 3600;
89+
__result.tm_min = __sec / 60;
90+
__result.tm_sec = __sec % 60;
91+
92+
return __result;
93+
}
94+
7695
// Convert a chrono (calendar) time point, or dururation to the given _Tm type,
7796
// which must have the same properties as std::tm.
7897
template <class _Tm, class _ChronoT>
@@ -83,22 +102,11 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
83102
# endif
84103

85104
if constexpr (__is_time_point<_ChronoT>) {
86-
if constexpr (same_as<typename _ChronoT::clock, chrono::system_clock>) {
87-
chrono::sys_days __days = chrono::time_point_cast<chrono::days>(__value);
88-
chrono::year_month_day __ymd{__days};
89-
90-
__result = std::__convert_to_tm<_Tm>(chrono::year_month_day{__ymd}, chrono::weekday{__days});
91-
92-
// TODO FMT D138826 has improvements for this part.
93-
// TODO FMT Since this is identical for duration and system time it would be good to avoid code duplication.
94-
uint64_t __sec =
95-
chrono::duration_cast<chrono::seconds>(__value - chrono::time_point_cast<chrono::seconds>(__days)).count();
96-
__sec %= 24 * 3600;
97-
__result.tm_hour = __sec / 3600;
98-
__sec %= 3600;
99-
__result.tm_min = __sec / 60;
100-
__result.tm_sec = __sec % 60;
101-
} else
105+
if constexpr (same_as<typename _ChronoT::clock, chrono::system_clock>)
106+
return std::__convert_to_tm<_Tm>(__value);
107+
else if constexpr (same_as<typename _ChronoT::clock, chrono::file_clock>)
108+
return std::__convert_to_tm<_Tm>(_ChronoT::clock::to_sys(__value));
109+
else
102110
static_assert(sizeof(_ChronoT) == 0, "TODO: Add the missing clock specialization");
103111
} else if constexpr (chrono::__is_duration<_ChronoT>::value) {
104112
// [time.format]/6

libcxx/include/__chrono/formatter.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <__chrono/convert_to_tm.h>
1616
#include <__chrono/day.h>
1717
#include <__chrono/duration.h>
18+
#include <__chrono/file_clock.h>
1819
#include <__chrono/hh_mm_ss.h>
1920
#include <__chrono/month.h>
2021
#include <__chrono/month_weekday.h>
@@ -591,6 +592,17 @@ struct _LIBCPP_TEMPLATE_VIS formatter<chrono::sys_time<_Duration>, _CharT> : pub
591592
}
592593
};
593594

595+
template <class _Duration, __fmt_char_type _CharT>
596+
struct _LIBCPP_TEMPLATE_VIS formatter<chrono::file_time<_Duration>, _CharT> : public __formatter_chrono<_CharT> {
597+
public:
598+
using _Base = __formatter_chrono<_CharT>;
599+
600+
_LIBCPP_HIDE_FROM_ABI constexpr auto parse(basic_format_parse_context<_CharT>& __parse_ctx)
601+
-> decltype(__parse_ctx.begin()) {
602+
return _Base::__parse(__parse_ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
603+
}
604+
};
605+
594606
template <class _Rep, class _Period, __fmt_char_type _CharT>
595607
struct formatter<chrono::duration<_Rep, _Period>, _CharT> : public __formatter_chrono<_CharT> {
596608
public:

libcxx/include/__chrono/ostream.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include <__chrono/day.h>
1414
#include <__chrono/duration.h>
15+
#include <__chrono/file_clock.h>
1516
#include <__chrono/hh_mm_ss.h>
1617
#include <__chrono/month.h>
1718
#include <__chrono/month_weekday.h>
@@ -45,6 +46,12 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const sys_time<_Duration> __tp)
4546
return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%F %T}"), __tp);
4647
}
4748

49+
template <class _CharT, class _Traits, class _Duration>
50+
_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
51+
operator<<(basic_ostream<_CharT, _Traits>& __os, const file_time<_Duration> __tp) {
52+
return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%F %T}"), __tp);
53+
}
54+
4855
// Depending on the type the return is a const _CharT* or a basic_string<_CharT>
4956
template <class _CharT, class _Period>
5057
_LIBCPP_HIDE_FROM_ABI auto __units_suffix() {
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17
10+
// UNSUPPORTED: no-localization
11+
// UNSUPPORTED: libcpp-has-no-incomplete-format
12+
13+
// TODO FMT Investigate Windows issues.
14+
// UNSUPPORTED msvc, target={{.+}}-windows-gnu
15+
// TODO FMT It seems GCC uses too much memory in the CI and fails.
16+
// UNSUPPORTED: gcc-12
17+
18+
// TODO FMT This test should not require std::to_chars(floating-point)
19+
// XFAIL: availability-fp_to_chars-missing
20+
21+
// REQUIRES: locale.fr_FR.UTF-8
22+
// REQUIRES: locale.ja_JP.UTF-8
23+
24+
// <chrono>
25+
26+
// class system_clock;
27+
28+
// template<class charT, class traits, class Duration>
29+
// basic_ostream<charT, traits>&
30+
// operator<<(basic_ostream<charT, traits>& os, const sys_time<Duration>& tp);
31+
32+
#include <chrono>
33+
#include <cassert>
34+
#include <ratio>
35+
#include <sstream>
36+
37+
#include "make_string.h"
38+
#include "platform_support.h" // locale name macros
39+
#include "test_macros.h"
40+
41+
#define SV(S) MAKE_STRING_VIEW(CharT, S)
42+
43+
// Modeled after the system clock's usings, this make adapating the
44+
// tests easier.
45+
template <class Duration>
46+
using file_time = std::chrono::time_point<std::chrono::file_clock, Duration>;
47+
using file_seconds = file_time<std::chrono::seconds>;
48+
using file_days = file_time<std::chrono::days>;
49+
50+
template <class CharT, class Duration>
51+
static std::basic_string<CharT> stream_c_locale(file_time<Duration> time_point) {
52+
std::basic_stringstream<CharT> sstr;
53+
sstr << std::fixed << time_point;
54+
return sstr.str();
55+
}
56+
57+
template <class CharT, class Duration>
58+
static std::basic_string<CharT> stream_fr_FR_locale(file_time<Duration> time_point) {
59+
std::basic_stringstream<CharT> sstr;
60+
const std::locale locale(LOCALE_fr_FR_UTF_8);
61+
sstr.imbue(locale);
62+
sstr << std::fixed << time_point;
63+
return sstr.str();
64+
}
65+
66+
template <class CharT, class Duration>
67+
static std::basic_string<CharT> stream_ja_JP_locale(file_time<Duration> time_point) {
68+
std::basic_stringstream<CharT> sstr;
69+
const std::locale locale(LOCALE_ja_JP_UTF_8);
70+
sstr.imbue(locale);
71+
sstr << std::fixed << time_point;
72+
return sstr.str();
73+
}
74+
75+
template <class CharT>
76+
static void test_c() {
77+
using namespace std::literals::chrono_literals;
78+
79+
assert(stream_c_locale<CharT>(file_time<std::chrono::nanoseconds>{946'688'523'123'456'789ns}) ==
80+
SV("2000-01-01 01:02:03.123456789"));
81+
assert(stream_c_locale<CharT>(file_time<std::chrono::microseconds>{946'688'523'123'456us}) ==
82+
SV("2000-01-01 01:02:03.123456"));
83+
84+
assert(stream_c_locale<CharT>(file_time<std::chrono::milliseconds>{946'684'800'123ms}) ==
85+
SV("2000-01-01 00:00:00.123"));
86+
assert(stream_c_locale<CharT>(file_seconds{1'234'567'890s}) == SV("2009-02-13 23:31:30"));
87+
assert(stream_c_locale<CharT>(file_time<std::chrono::minutes>{20'576'131min}) == SV("2009-02-13 23:31:00"));
88+
assert(stream_c_locale<CharT>(file_time<std::chrono::hours>{342'935h}) == SV("2009-02-13 23:00:00"));
89+
assert(stream_c_locale<CharT>(file_days{std::chrono::days{14'288}}) == SV("2009-02-13 00:00:00"));
90+
assert(stream_c_locale<CharT>(file_time<std::chrono::weeks>{std::chrono::weeks{2041}}) == SV("2009-02-12 00:00:00"));
91+
92+
assert(stream_c_locale<CharT>(file_time<std::chrono::duration<signed char, std::ratio<2, 1>>>{
93+
std::chrono::duration<signed char, std::ratio<2, 1>>{60}}) == SV("1970-01-01 00:02:00"));
94+
assert(stream_c_locale<CharT>(file_time<std::chrono::duration<short, std::ratio<1, 2>>>{
95+
std::chrono::duration<short, std::ratio<1, 2>>{3600}}) == SV("1970-01-01 00:30:00.0"));
96+
assert(stream_c_locale<CharT>(file_time<std::chrono::duration<int, std::ratio<1, 4>>>{
97+
std::chrono::duration<int, std::ratio<1, 4>>{3600}}) == SV("1970-01-01 00:15:00.00"));
98+
assert(stream_c_locale<CharT>(file_time<std::chrono::duration<long, std::ratio<1, 10>>>{
99+
std::chrono::duration<long, std::ratio<1, 10>>{36611}}) == SV("1970-01-01 01:01:01.1"));
100+
assert(stream_c_locale<CharT>(file_time<std::chrono::duration<long long, std::ratio<1, 100>>>{
101+
std::chrono::duration<long long, std::ratio<1, 100>>{12'345'678'9010}}) == SV("2009-02-13 23:31:30.10"));
102+
103+
assert(stream_c_locale<CharT>(file_time<std::chrono::duration<float, std::ratio<1, 1>>>{
104+
std::chrono::duration<float, std::ratio<1, 1>>{123.456}}) == SV("1970-01-01 00:02:03"));
105+
assert(stream_c_locale<CharT>(file_time<std::chrono::duration<double, std::ratio<1, 10>>>{
106+
std::chrono::duration<double, std::ratio<1, 10>>{123.456}}) == SV("1970-01-01 00:00:12.3"));
107+
assert(stream_c_locale<CharT>(file_time<std::chrono::duration<long double, std::ratio<1, 100>>>{
108+
std::chrono::duration<long double, std::ratio<1, 100>>{123.456}}) == SV("1970-01-01 00:00:01.23"));
109+
}
110+
111+
template <class CharT>
112+
static void test_fr_FR() {
113+
using namespace std::literals::chrono_literals;
114+
115+
assert(stream_fr_FR_locale<CharT>(file_time<std::chrono::nanoseconds>{946'688'523'123'456'789ns}) ==
116+
SV("2000-01-01 01:02:03,123456789"));
117+
assert(stream_fr_FR_locale<CharT>(file_time<std::chrono::microseconds>{946'688'523'123'456us}) ==
118+
SV("2000-01-01 01:02:03,123456"));
119+
120+
assert(stream_fr_FR_locale<CharT>(file_time<std::chrono::milliseconds>{946'684'800'123ms}) ==
121+
SV("2000-01-01 00:00:00,123"));
122+
assert(stream_fr_FR_locale<CharT>(file_seconds{1'234'567'890s}) == SV("2009-02-13 23:31:30"));
123+
assert(stream_fr_FR_locale<CharT>(file_time<std::chrono::minutes>{20'576'131min}) == SV("2009-02-13 23:31:00"));
124+
assert(stream_fr_FR_locale<CharT>(file_time<std::chrono::hours>{342'935h}) == SV("2009-02-13 23:00:00"));
125+
assert(stream_fr_FR_locale<CharT>(file_days{std::chrono::days{14'288}}) == SV("2009-02-13 00:00:00"));
126+
assert(stream_fr_FR_locale<CharT>(file_time<std::chrono::weeks>{std::chrono::weeks{2041}}) ==
127+
SV("2009-02-12 00:00:00"));
128+
129+
assert(stream_fr_FR_locale<CharT>(file_time<std::chrono::duration<signed char, std::ratio<2, 1>>>{
130+
std::chrono::duration<signed char, std::ratio<2, 1>>{60}}) == SV("1970-01-01 00:02:00"));
131+
assert(stream_fr_FR_locale<CharT>(file_time<std::chrono::duration<short, std::ratio<1, 2>>>{
132+
std::chrono::duration<short, std::ratio<1, 2>>{3600}}) == SV("1970-01-01 00:30:00,0"));
133+
assert(stream_fr_FR_locale<CharT>(file_time<std::chrono::duration<int, std::ratio<1, 4>>>{
134+
std::chrono::duration<int, std::ratio<1, 4>>{3600}}) == SV("1970-01-01 00:15:00,00"));
135+
assert(stream_fr_FR_locale<CharT>(file_time<std::chrono::duration<long, std::ratio<1, 10>>>{
136+
std::chrono::duration<long, std::ratio<1, 10>>{36611}}) == SV("1970-01-01 01:01:01,1"));
137+
assert(stream_fr_FR_locale<CharT>(file_time<std::chrono::duration<long long, std::ratio<1, 100>>>{
138+
std::chrono::duration<long long, std::ratio<1, 100>>{12'345'678'9010}}) == SV("2009-02-13 23:31:30,10"));
139+
140+
assert(stream_fr_FR_locale<CharT>(file_time<std::chrono::duration<float, std::ratio<1, 1>>>{
141+
std::chrono::duration<float, std::ratio<1, 1>>{123.456}}) == SV("1970-01-01 00:02:03"));
142+
assert(stream_fr_FR_locale<CharT>(file_time<std::chrono::duration<double, std::ratio<1, 10>>>{
143+
std::chrono::duration<double, std::ratio<1, 10>>{123.456}}) == SV("1970-01-01 00:00:12,3"));
144+
assert(stream_fr_FR_locale<CharT>(file_time<std::chrono::duration<long double, std::ratio<1, 100>>>{
145+
std::chrono::duration<long double, std::ratio<1, 100>>{123.456}}) == SV("1970-01-01 00:00:01,23"));
146+
}
147+
148+
template <class CharT>
149+
static void test_ja_JP() {
150+
using namespace std::literals::chrono_literals;
151+
152+
assert(stream_ja_JP_locale<CharT>(file_time<std::chrono::nanoseconds>{946'688'523'123'456'789ns}) ==
153+
SV("2000-01-01 01:02:03.123456789"));
154+
assert(stream_ja_JP_locale<CharT>(file_time<std::chrono::microseconds>{946'688'523'123'456us}) ==
155+
SV("2000-01-01 01:02:03.123456"));
156+
157+
assert(stream_ja_JP_locale<CharT>(file_time<std::chrono::milliseconds>{946'684'800'123ms}) ==
158+
SV("2000-01-01 00:00:00.123"));
159+
assert(stream_ja_JP_locale<CharT>(file_seconds{1'234'567'890s}) == SV("2009-02-13 23:31:30"));
160+
assert(stream_ja_JP_locale<CharT>(file_time<std::chrono::minutes>{20'576'131min}) == SV("2009-02-13 23:31:00"));
161+
assert(stream_ja_JP_locale<CharT>(file_time<std::chrono::hours>{342'935h}) == SV("2009-02-13 23:00:00"));
162+
assert(stream_ja_JP_locale<CharT>(file_days{std::chrono::days{14'288}}) == SV("2009-02-13 00:00:00"));
163+
assert(stream_ja_JP_locale<CharT>(file_time<std::chrono::weeks>{std::chrono::weeks{2041}}) ==
164+
SV("2009-02-12 00:00:00"));
165+
166+
assert(stream_ja_JP_locale<CharT>(file_time<std::chrono::duration<signed char, std::ratio<2, 1>>>{
167+
std::chrono::duration<signed char, std::ratio<2, 1>>{60}}) == SV("1970-01-01 00:02:00"));
168+
assert(stream_ja_JP_locale<CharT>(file_time<std::chrono::duration<short, std::ratio<1, 2>>>{
169+
std::chrono::duration<short, std::ratio<1, 2>>{3600}}) == SV("1970-01-01 00:30:00.0"));
170+
assert(stream_ja_JP_locale<CharT>(file_time<std::chrono::duration<int, std::ratio<1, 4>>>{
171+
std::chrono::duration<int, std::ratio<1, 4>>{3600}}) == SV("1970-01-01 00:15:00.00"));
172+
assert(stream_ja_JP_locale<CharT>(file_time<std::chrono::duration<long, std::ratio<1, 10>>>{
173+
std::chrono::duration<long, std::ratio<1, 10>>{36611}}) == SV("1970-01-01 01:01:01.1"));
174+
assert(stream_ja_JP_locale<CharT>(file_time<std::chrono::duration<long long, std::ratio<1, 100>>>{
175+
std::chrono::duration<long long, std::ratio<1, 100>>{12'345'678'9010}}) == SV("2009-02-13 23:31:30.10"));
176+
177+
assert(stream_ja_JP_locale<CharT>(file_time<std::chrono::duration<float, std::ratio<1, 1>>>{
178+
std::chrono::duration<float, std::ratio<1, 1>>{123.456}}) == SV("1970-01-01 00:02:03"));
179+
assert(stream_ja_JP_locale<CharT>(file_time<std::chrono::duration<double, std::ratio<1, 10>>>{
180+
std::chrono::duration<double, std::ratio<1, 10>>{123.456}}) == SV("1970-01-01 00:00:12.3"));
181+
assert(stream_ja_JP_locale<CharT>(file_time<std::chrono::duration<long double, std::ratio<1, 100>>>{
182+
std::chrono::duration<long double, std::ratio<1, 100>>{123.456}}) == SV("1970-01-01 00:00:01.23"));
183+
}
184+
185+
template <class CharT>
186+
static void test() {
187+
test_c<CharT>();
188+
test_fr_FR<CharT>();
189+
test_ja_JP<CharT>();
190+
}
191+
192+
int main(int, char**) {
193+
test<char>();
194+
195+
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
196+
test<wchar_t>();
197+
#endif
198+
199+
return 0;
200+
}

0 commit comments

Comments
 (0)