Skip to content

Commit afbfb16

Browse files
authored
[libc++][TZDB] Implements zoned_time formatters. (llvm#98347)
Implements parts of: - P0355 Extending to chrono Calendars and Time Zones - P1361 Integration of chrono with text formatting - P2372 Fixing locale handling in chrono formatters
1 parent 4d8e42e commit afbfb16

File tree

8 files changed

+1434
-9
lines changed

8 files changed

+1434
-9
lines changed

libcxx/docs/Status/FormatPaper.csv

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
77
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::gps_time<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
88
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::file_time<Duration>``",,Mark de Wever,|Complete|,17.0
99
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_time<Duration>``",,Mark de Wever,|Complete|,17.0
10-
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local-time-format-t<Duration>``",A ``<chrono>`` implementation,Mark de Wever,,,
10+
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local-time-format-t<Duration>``",,,|Nothing To Do|,
1111
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::day``",,Mark de Wever,|Complete|,16.0
1212
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::month``",,Mark de Wever,|Complete|,16.0
1313
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::year``",,Mark de Wever,|Complete|,16.0
@@ -26,7 +26,7 @@ Section,Description,Dependencies,Assignee,Status,First released version
2626
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::hh_mm_ss<duration<Rep, Period>>``",,Mark de Wever,|Complete|,17.0
2727
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::sys_info``",,Mark de Wever,|Complete|,19.0
2828
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::local_info``",,Mark de Wever,|Complete|,19.0
29-
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::zoned_time<Duration, TimeZonePtr>``",A ``<chrono>`` implementation,Mark de Wever,,
29+
`[time.syn] <https://wg21.link/time.syn>`_,"Formatter ``chrono::zoned_time<Duration, TimeZonePtr>``",,Mark de Wever,|Complete|,19.0
3030

3131
"`P2693R1 <https://wg21.link/P2693R1>`__","Formatting ``thread::id`` and ``stacktrace``"
3232
`[thread.thread.id] <https://wg21.link/thread.thread.id>`_,"Formatting ``thread::id``",,Mark de Wever,|Complete|,17.0

libcxx/include/__chrono/convert_to_tm.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@
2929
#include <__chrono/year_month.h>
3030
#include <__chrono/year_month_day.h>
3131
#include <__chrono/year_month_weekday.h>
32+
#include <__chrono/zoned_time.h>
3233
#include <__concepts/same_as.h>
3334
#include <__config>
3435
#include <__format/format_error.h>
3536
#include <__memory/addressof.h>
3637
#include <__type_traits/is_convertible.h>
38+
#include <__type_traits/is_specialization.h>
3739
#include <cstdint>
3840
#include <ctime>
3941
#include <limits>
@@ -178,7 +180,13 @@ _LIBCPP_HIDE_FROM_ABI _Tm __convert_to_tm(const _ChronoT& __value) {
178180
// Has no time information.
179181
} else if constexpr (same_as<_ChronoT, chrono::local_info>) {
180182
// Has no time information.
181-
# endif
183+
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
184+
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
185+
} else if constexpr (__is_specialization_v<_ChronoT, chrono::zoned_time>) {
186+
return std::__convert_to_tm<_Tm>(
187+
chrono::sys_time<typename _ChronoT::duration>{__value.get_local_time().time_since_epoch()});
188+
# endif
189+
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
182190
} else
183191
static_assert(sizeof(_ChronoT) == 0, "Add the missing type specialization");
184192

libcxx/include/__chrono/formatter.h

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <__chrono/year_month.h>
3434
#include <__chrono/year_month_day.h>
3535
#include <__chrono/year_month_weekday.h>
36+
#include <__chrono/zoned_time.h>
3637
#include <__concepts/arithmetic.h>
3738
#include <__concepts/same_as.h>
3839
#include <__config>
@@ -44,6 +45,7 @@
4445
#include <__format/parser_std_format_spec.h>
4546
#include <__format/write_escaped.h>
4647
#include <__memory/addressof.h>
48+
#include <__type_traits/is_specialization.h>
4749
#include <cmath>
4850
#include <ctime>
4951
#include <limits>
@@ -137,10 +139,24 @@ __format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::hh_mm_ss<
137139
__value.fractional_width);
138140
}
139141

142+
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB) && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && \
143+
!defined(_LIBCPP_HAS_NO_FILESYSTEM) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
144+
template <class _CharT, class _Duration, class _TimeZonePtr>
145+
_LIBCPP_HIDE_FROM_ABI void
146+
__format_sub_seconds(basic_stringstream<_CharT>& __sstr, const chrono::zoned_time<_Duration, _TimeZonePtr>& __value) {
147+
__formatter::__format_sub_seconds(__sstr, __value.get_local_time().time_since_epoch());
148+
}
149+
# endif
150+
140151
template <class _Tp>
141152
consteval bool __use_fraction() {
142153
if constexpr (__is_time_point<_Tp>)
143154
return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;
155+
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB) && !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && \
156+
!defined(_LIBCPP_HAS_NO_FILESYSTEM) && !defined(_LIBCPP_HAS_NO_LOCALIZATION)
157+
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
158+
return chrono::hh_mm_ss<typename _Tp::duration>::fractional_width;
159+
# endif
144160
else if constexpr (chrono::__is_duration<_Tp>::value)
145161
return chrono::hh_mm_ss<_Tp>::fractional_width;
146162
else if constexpr (__is_hh_mm_ss<_Tp>)
@@ -212,8 +228,13 @@ _LIBCPP_HIDE_FROM_ABI __time_zone __convert_to_time_zone([[maybe_unused]] const
212228
# if !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
213229
if constexpr (same_as<_Tp, chrono::sys_info>)
214230
return {__value.abbrev, __value.offset};
231+
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
232+
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
233+
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
234+
return __formatter::__convert_to_time_zone(__value.get_info());
235+
# endif
215236
else
216-
# endif
237+
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
217238
return {"UTC", chrono::seconds{0}};
218239
}
219240

@@ -426,7 +447,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_ok(const _Tp& __value) {
426447
return true;
427448
else if constexpr (same_as<_Tp, chrono::local_info>)
428449
return true;
429-
# endif
450+
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
451+
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
452+
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
453+
return true;
454+
# endif
455+
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
430456
else
431457
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
432458
}
@@ -472,7 +498,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __weekday_name_ok(const _Tp& __value) {
472498
return true;
473499
else if constexpr (same_as<_Tp, chrono::local_info>)
474500
return true;
475-
# endif
501+
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
502+
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
503+
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
504+
return true;
505+
# endif
506+
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
476507
else
477508
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
478509
}
@@ -518,7 +549,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __date_ok(const _Tp& __value) {
518549
return true;
519550
else if constexpr (same_as<_Tp, chrono::local_info>)
520551
return true;
521-
# endif
552+
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
553+
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
554+
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
555+
return true;
556+
# endif
557+
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
522558
else
523559
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
524560
}
@@ -564,7 +600,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __month_name_ok(const _Tp& __value) {
564600
return true;
565601
else if constexpr (same_as<_Tp, chrono::local_info>)
566602
return true;
567-
# endif
603+
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
604+
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
605+
else if constexpr (__is_specialization_v<_Tp, chrono::zoned_time>)
606+
return true;
607+
# endif
608+
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
568609
else
569610
static_assert(sizeof(_Tp) == 0, "Add the missing type specialization");
570611
}
@@ -924,7 +965,23 @@ struct formatter<chrono::local_info, _CharT> : public __formatter_chrono<_CharT>
924965
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags{});
925966
}
926967
};
927-
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
968+
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
969+
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
970+
// Note due to how libc++'s formatters are implemented there is no need to add
971+
// the exposition only local-time-format-t abstraction.
972+
template <class _Duration, class _TimeZonePtr, __fmt_char_type _CharT>
973+
struct formatter<chrono::zoned_time<_Duration, _TimeZonePtr>, _CharT> : public __formatter_chrono<_CharT> {
974+
public:
975+
using _Base = __formatter_chrono<_CharT>;
976+
977+
template <class _ParseContext>
978+
_LIBCPP_HIDE_FROM_ABI constexpr typename _ParseContext::iterator parse(_ParseContext& __ctx) {
979+
return _Base::__parse(__ctx, __format_spec::__fields_chrono, __format_spec::__flags::__clock);
980+
}
981+
};
982+
# endif // !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) &&
983+
// !defined(_LIBCPP_HAS_NO_LOCALIZATION)
984+
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
928985

929986
#endif // if _LIBCPP_STD_VER >= 20
930987

libcxx/include/__chrono/ostream.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <__chrono/year_month.h>
2828
#include <__chrono/year_month_day.h>
2929
#include <__chrono/year_month_weekday.h>
30+
#include <__chrono/zoned_time.h>
3031
#include <__concepts/same_as.h>
3132
#include <__config>
3233
#include <__format/format_functions.h>
@@ -302,6 +303,14 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, const local_info& __info) {
302303
_LIBCPP_STATICALLY_WIDEN(_CharT, "{}: {{{}, {}}}"), __result(), __info.first, __info.second);
303304
}
304305

306+
# if !defined(_LIBCPP_HAS_NO_TIME_ZONE_DATABASE) && !defined(_LIBCPP_HAS_NO_FILESYSTEM) && \
307+
!defined(_LIBCPP_HAS_NO_LOCALIZATION)
308+
template <class _CharT, class _Traits, class _Duration, class _TimeZonePtr>
309+
_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
310+
operator<<(basic_ostream<_CharT, _Traits>& __os, const zoned_time<_Duration, _TimeZonePtr>& __tp) {
311+
return __os << std::format(__os.getloc(), _LIBCPP_STATICALLY_WIDEN(_CharT, "{:L%F %T %Z}"), __tp);
312+
}
313+
# endif
305314
# endif // !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_TZDB)
306315

307316
} // namespace chrono

libcxx/include/chrono

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,11 @@ template<class Duration1, class Duration2, class TimeZonePtr>
799799
bool operator==(const zoned_time<Duration1, TimeZonePtr>& x,
800800
const zoned_time<Duration2, TimeZonePtr>& y);
801801
802+
template<class charT, class traits, class Duration, class TimeZonePtr> // C++20
803+
basic_ostream<charT, traits>&
804+
operator<<(basic_ostream<charT, traits>& os,
805+
const zoned_time<Duration, TimeZonePtr>& t);
806+
802807
// [time.zone.leap], leap second support
803808
class leap_second { // C++20
804809
public:
@@ -881,6 +886,8 @@ namespace std {
881886
struct formatter<chrono::hh_mm_ss<duration<Rep, Period>>, charT>; // C++20
882887
template<class charT> struct formatter<chrono::sys_info, charT>; // C++20
883888
template<class charT> struct formatter<chrono::local_info, charT>; // C++20
889+
template<class Duration, class TimeZonePtr, class charT> // C++20
890+
struct formatter<chrono::zoned_time<Duration, TimeZonePtr>, charT>;
884891
} // namespace std
885892
886893
namespace chrono {

0 commit comments

Comments
 (0)