8 #ifndef FMTQUILL_CHRONO_H_ 9 #define FMTQUILL_CHRONO_H_ 11 #ifndef FMTQUILL_MODULE 20 # include <type_traits> 25 FMTQUILL_BEGIN_NAMESPACE
28 #ifndef FMTQUILL_SAFE_DURATION_CAST 29 # define FMTQUILL_SAFE_DURATION_CAST 1 31 #if FMTQUILL_SAFE_DURATION_CAST 41 template <
typename To,
typename From,
42 FMTQUILL_ENABLE_IF(!std::is_same<From, To>::value &&
43 std::numeric_limits<From>::is_signed ==
44 std::numeric_limits<To>::is_signed)>
45 FMTQUILL_CONSTEXPR
auto lossless_integral_conversion(
const From from,
int& ec)
48 using F = std::numeric_limits<From>;
49 using T = std::numeric_limits<To>;
50 static_assert(F::is_integer,
"From must be integral");
51 static_assert(T::is_integer,
"To must be integral");
54 if (detail::const_check(F::digits <= T::digits)) {
58 if (from < (T::min)() || from > (T::max)()) {
64 return static_cast<To
>(from);
69 template <
typename To,
typename From,
70 FMTQUILL_ENABLE_IF(!std::is_same<From, To>::value &&
71 std::numeric_limits<From>::is_signed !=
72 std::numeric_limits<To>::is_signed)>
73 FMTQUILL_CONSTEXPR
auto lossless_integral_conversion(
const From from,
int& ec)
76 using F = std::numeric_limits<From>;
77 using T = std::numeric_limits<To>;
78 static_assert(F::is_integer,
"From must be integral");
79 static_assert(T::is_integer,
"To must be integral");
81 if (detail::const_check(F::is_signed && !T::is_signed)) {
83 if (fmtquill::detail::is_negative(from)) {
88 if (detail::const_check(F::digits > T::digits) &&
89 from > static_cast<From>(detail::max_value<To>())) {
95 if (detail::const_check(!F::is_signed && T::is_signed &&
96 F::digits >= T::digits) &&
97 from > static_cast<From>(detail::max_value<To>())) {
101 return static_cast<To
>(from);
104 template <
typename To,
typename From,
105 FMTQUILL_ENABLE_IF(std::is_same<From, To>::value)>
106 FMTQUILL_CONSTEXPR
auto lossless_integral_conversion(
const From from,
int& ec)
126 template <
typename To,
typename From,
127 FMTQUILL_ENABLE_IF(!std::is_same<From, To>::value)>
128 FMTQUILL_CONSTEXPR
auto safe_float_conversion(
const From from,
int& ec) -> To {
130 using T = std::numeric_limits<To>;
131 static_assert(std::is_floating_point<From>::value,
"From must be floating");
132 static_assert(std::is_floating_point<To>::value,
"To must be floating");
135 if (std::isfinite(from)) {
136 if (from >= T::lowest() && from <= (T::max)()) {
137 return static_cast<To
>(from);
145 return static_cast<To
>(from);
148 template <
typename To,
typename From,
149 FMTQUILL_ENABLE_IF(std::is_same<From, To>::value)>
150 FMTQUILL_CONSTEXPR
auto safe_float_conversion(
const From from,
int& ec) -> To {
152 static_assert(std::is_floating_point<From>::value,
"From must be floating");
157 template <
typename To,
typename FromRep,
typename FromPeriod,
158 FMTQUILL_ENABLE_IF(std::is_floating_point<FromRep>::value),
159 FMTQUILL_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
162 using From = std::chrono::duration<FromRep, FromPeriod>;
164 if (std::isnan(from.count())) {
166 return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
172 if (std::isinf(from.count())) {
173 return To{from.count()};
179 : std::ratio_divide<typename From::period, typename To::period> {};
181 static_assert(Factor::num > 0,
"num must be positive");
182 static_assert(Factor::den > 0,
"den must be positive");
188 using IntermediateRep =
189 typename std::common_type<
typename From::rep,
typename To::rep,
190 decltype(Factor::num)>::type;
194 IntermediateRep count =
195 safe_float_conversion<IntermediateRep>(from.count(), ec);
201 if (detail::const_check(Factor::num != 1)) {
202 constexpr
auto max1 = detail::max_value<IntermediateRep>() /
203 static_cast<IntermediateRep>(Factor::num);
208 constexpr
auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
209 static_cast<IntermediateRep
>(Factor::num);
214 count *=
static_cast<IntermediateRep
>(Factor::num);
218 if (detail::const_check(Factor::den != 1)) {
219 using common_t =
typename std::common_type<IntermediateRep, intmax_t>::type;
220 count /=
static_cast<common_t
>(Factor::den);
224 using ToRep =
typename To::rep;
226 const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
238 #ifdef FMTQUILL_USE_UTC_TIME 240 #elif defined(__cpp_lib_chrono) 241 # define FMTQUILL_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) 243 # define FMTQUILL_USE_UTC_TIME 0 245 #if FMTQUILL_USE_UTC_TIME 246 using utc_clock = std::chrono::utc_clock;
249 template <
typename T>
void to_sys(T);
254 #ifdef FMTQUILL_USE_LOCAL_TIME 256 #elif defined(__cpp_lib_chrono) 257 # define FMTQUILL_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) 259 # define FMTQUILL_USE_LOCAL_TIME 0 261 #if FMTQUILL_USE_LOCAL_TIME 262 using local_t = std::chrono::local_t;
269 template <
typename Duration>
270 using sys_time = std::chrono::time_point<std::chrono::system_clock, Duration>;
272 template <
typename Duration>
273 using utc_time = std::chrono::time_point<detail::utc_clock, Duration>;
275 template <
class Duration>
276 using local_time = std::chrono::time_point<detail::local_t, Duration>;
282 #define FMTQUILL_NOMACRO 284 template <
typename T =
void>
struct null {};
285 inline auto localtime_r FMTQUILL_NOMACRO(...) ->
null<> {
return null<>(); }
286 inline auto localtime_s(...) ->
null<> {
return null<>(); }
287 inline auto gmtime_r(...) ->
null<> {
return null<>(); }
288 inline auto gmtime_s(...) ->
null<> {
return null<>(); }
292 template <
typename StreamBuf>
class formatbuf :
public StreamBuf {
294 using char_type =
typename StreamBuf::char_type;
295 using streamsize = decltype(std::declval<StreamBuf>().sputn(
nullptr, 0));
296 using int_type =
typename StreamBuf::int_type;
297 using traits_type =
typename StreamBuf::traits_type;
311 auto overflow(int_type ch) -> int_type
override {
312 if (!traits_type::eq_int_type(ch, traits_type::eof()))
313 buffer_.push_back(static_cast<char_type>(ch));
317 auto xsputn(
const char_type* s, streamsize count) -> streamsize
override {
318 buffer_.
append(s, s + count);
323 inline auto get_classic_locale() ->
const std::locale& {
324 static const auto& locale = std::locale::classic();
329 static constexpr
const size_t max_size = 32;
330 CodeUnit buf[max_size];
334 template <
typename CodeUnit>
336 const std::locale& loc) {
337 FMTQUILL_PRAGMA_CLANG(diagnostic push)
338 FMTQUILL_PRAGMA_CLANG(diagnostic ignored
"-Wdeprecated")
339 auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
340 FMTQUILL_PRAGMA_CLANG(diagnostic pop)
341 auto mb = std::mbstate_t();
342 const char* from_next =
nullptr;
343 auto result = f.in(mb, in.begin(), in.end(), from_next, std::begin(out.buf),
344 std::end(out.buf), out.end);
345 if (result != std::codecvt_base::ok)
346 FMTQUILL_THROW(format_error(
"failed to format time"));
349 template <
typename OutputIt>
350 auto write_encoded_tm_str(OutputIt out,
string_view in,
const std::locale& loc)
352 if (const_check(detail::use_utf8) && loc != get_classic_locale()) {
355 #if FMTQUILL_MSC_VERSION != 0 || \ 356 (defined(__GLIBCXX__) && \ 357 (!defined(_GLIBCXX_USE_DUAL_ABI) || _GLIBCXX_USE_DUAL_ABI == 0)) 360 using code_unit = wchar_t;
362 using code_unit = char32_t;
367 write_codecvt(unit, in, loc);
371 if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)}))
372 FMTQUILL_THROW(format_error(
"failed to format time"));
373 return copy<char>(u.c_str(), u.c_str() + u.size(), out);
375 return copy<char>(in.
data(), in.
data() + in.
size(), out);
378 template <
typename Char,
typename OutputIt,
379 FMTQUILL_ENABLE_IF(!std::is_same<Char, char>::value)>
380 auto write_tm_str(OutputIt out,
string_view sv,
const std::locale& loc)
383 write_codecvt(unit, sv, loc);
384 return copy<Char>(unit.buf, unit.end, out);
387 template <
typename Char,
typename OutputIt,
388 FMTQUILL_ENABLE_IF(std::is_same<Char, char>::value)>
389 auto write_tm_str(OutputIt out,
string_view sv,
const std::locale& loc)
391 return write_encoded_tm_str(out, sv, loc);
394 template <
typename Char>
395 inline void do_write(
buffer<Char>& buf,
const std::tm& time,
396 const std::locale& loc,
char format,
char modifier) {
400 const auto& facet = std::use_facet<std::time_put<Char>>(loc);
401 auto end = facet.put(os, os, Char(
' '), &time, format, modifier);
402 if (end.failed()) FMTQUILL_THROW(format_error(
"failed to format time"));
405 template <
typename Char,
typename OutputIt,
406 FMTQUILL_ENABLE_IF(!std::is_same<Char, char>::value)>
407 auto write(OutputIt out,
const std::tm& time,
const std::locale& loc,
408 char format,
char modifier = 0) -> OutputIt {
409 auto&& buf = get_buffer<Char>(out);
410 do_write<Char>(buf, time, loc, format, modifier);
411 return get_iterator(buf, out);
414 template <
typename Char,
typename OutputIt,
415 FMTQUILL_ENABLE_IF(std::is_same<Char, char>::value)>
416 auto write(OutputIt out,
const std::tm& time,
const std::locale& loc,
417 char format,
char modifier = 0) -> OutputIt {
419 do_write<char>(buf, time, loc, format, modifier);
423 template <
typename T,
typename U>
424 using is_similar_arithmetic_type =
425 bool_constant<(std::is_integral<T>::value && std::is_integral<U>::value) ||
426 (std::is_floating_point<T>::value &&
427 std::is_floating_point<U>::value)>;
429 FMTQUILL_NORETURN
inline void throw_duration_error() {
430 FMTQUILL_THROW(format_error(
"cannot format duration"));
434 template <
typename To,
typename FromRep,
typename FromPeriod,
435 FMTQUILL_ENABLE_IF(std::is_integral<FromRep>::value&&
436 std::is_integral<typename To::rep>::value)>
437 auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
438 #if !FMTQUILL_SAFE_DURATION_CAST 439 return std::chrono::duration_cast<To>(from);
442 using factor = std::ratio_divide<FromPeriod, typename To::period>;
444 using common_rep =
typename std::common_type<FromRep,
typename To::rep,
445 decltype(factor::num)>::type;
448 auto count = safe_duration_cast::lossless_integral_conversion<common_rep>(
450 if (ec) throw_duration_error();
453 if (const_check(factor::num != 1)) {
454 if (count > max_value<common_rep>() / factor::num) throw_duration_error();
455 const auto min = (std::numeric_limits<common_rep>::min)() / factor::num;
456 if (const_check(!std::is_unsigned<common_rep>::value) && count < min)
457 throw_duration_error();
458 count *= factor::num;
460 if (const_check(factor::den != 1)) count /= factor::den;
462 To(safe_duration_cast::lossless_integral_conversion<typename To::rep>(
464 if (ec) throw_duration_error();
469 template <
typename To,
typename FromRep,
typename FromPeriod,
470 FMTQUILL_ENABLE_IF(std::is_floating_point<FromRep>::value&&
471 std::is_floating_point<typename To::rep>::value)>
472 auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
473 #if FMTQUILL_SAFE_DURATION_CAST 477 To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
478 if (ec) throw_duration_error();
482 return std::chrono::duration_cast<To>(from);
486 template <
typename To,
typename FromRep,
typename FromPeriod,
488 !is_similar_arithmetic_type<FromRep, typename To::rep>::value)>
489 auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
491 return std::chrono::duration_cast<To>(from);
494 template <
typename Duration>
495 auto to_time_t(sys_time<Duration> time_point) -> std::time_t {
499 return detail::duration_cast<std::chrono::duration<std::time_t>>(
500 time_point.time_since_epoch())
508 template <
typename Duration,
typename LocalTime>
509 auto to_sys(LocalTime) -> sys_time<Duration> {
513 template <
typename... T>
auto current_zone(T...) ->
time_zone* {
517 template <
typename... T>
void _tzset(T...) {}
521 inline void tzset_once() {
522 static bool init = []() {
531 FMTQUILL_BEGIN_EXPORT
538 FMTQUILL_DEPRECATED
inline auto localtime(std::time_t time) -> std::tm {
543 inline dispatcher(std::time_t t) : time_(t) {}
545 inline auto run() ->
bool {
546 using namespace fmtquill::detail;
547 return handle(localtime_r(&time_, &tm_));
550 inline auto handle(std::tm* tm) ->
bool {
return tm !=
nullptr; }
553 using namespace fmtquill::detail;
554 return fallback(localtime_s(&tm_, &time_));
557 inline auto fallback(
int res) ->
bool {
return res == 0; }
559 #if !FMTQUILL_MSC_VERSION 561 using namespace fmtquill::detail;
562 std::tm* tm = std::localtime(&time_);
564 return tm !=
nullptr;
570 if (!lt.run()) FMTQUILL_THROW(format_error(
"time_t value out of range"));
574 #if FMTQUILL_USE_LOCAL_TIME 575 template <
typename Duration>
576 FMTQUILL_DEPRECATED
auto localtime(std::chrono::local_time<Duration> time)
578 using namespace std::chrono;
580 return localtime(detail::to_time_t(current_zone()->to_sys<Duration>(time)));
589 inline auto gmtime(std::time_t time) -> std::tm {
594 inline dispatcher(std::time_t t) : time_(t) {}
596 inline auto run() ->
bool {
597 using namespace fmtquill::detail;
598 return handle(gmtime_r(&time_, &tm_));
601 inline auto handle(std::tm* tm) ->
bool {
return tm !=
nullptr; }
604 using namespace fmtquill::detail;
605 return fallback(gmtime_s(&tm_, &time_));
608 inline auto fallback(
int res) ->
bool {
return res == 0; }
610 #if !FMTQUILL_MSC_VERSION 612 std::tm* tm = std::gmtime(&time_);
614 return tm !=
nullptr;
618 auto gt = dispatcher(time);
620 if (!gt.run()) FMTQUILL_THROW(format_error(
"time_t value out of range"));
624 template <
typename Duration>
625 inline auto gmtime(sys_time<Duration> time_point) -> std::tm {
626 return gmtime(detail::to_time_t(time_point));
634 inline void write_digit2_separated(
char* buf,
unsigned a,
unsigned b,
635 unsigned c,
char sep) {
636 unsigned long long digits =
637 a | (b << 24) | (static_cast<unsigned long long>(c) << 48);
647 digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6;
649 digits = ((digits & 0x00f00000f00000f0) >> 4) |
650 ((digits & 0x000f00000f00000f) << 8);
651 auto usep =
static_cast<unsigned long long>(sep);
653 digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
655 constexpr
const size_t len = 8;
656 if (const_check(is_big_endian())) {
658 std::memcpy(tmp, &digits, len);
659 std::reverse_copy(tmp, tmp + len, buf);
661 std::memcpy(buf, &digits, len);
665 template <
typename Period>
666 FMTQUILL_CONSTEXPR
inline auto get_units() ->
const char* {
667 if (std::is_same<Period, std::atto>::value)
return "as";
668 if (std::is_same<Period, std::femto>::value)
return "fs";
669 if (std::is_same<Period, std::pico>::value)
return "ps";
670 if (std::is_same<Period, std::nano>::value)
return "ns";
671 if (std::is_same<Period, std::micro>::value)
return "us";
672 if (std::is_same<Period, std::milli>::value)
return "ms";
673 if (std::is_same<Period, std::centi>::value)
return "cs";
674 if (std::is_same<Period, std::deci>::value)
return "ds";
675 if (std::is_same<Period, std::ratio<1>>::
value)
return "s";
676 if (std::is_same<Period, std::deca>::value)
return "das";
677 if (std::is_same<Period, std::hecto>::value)
return "hs";
678 if (std::is_same<Period, std::kilo>::value)
return "ks";
679 if (std::is_same<Period, std::mega>::value)
return "Ms";
680 if (std::is_same<Period, std::giga>::value)
return "Gs";
681 if (std::is_same<Period, std::tera>::value)
return "Ts";
682 if (std::is_same<Period, std::peta>::value)
return "Ps";
683 if (std::is_same<Period, std::exa>::value)
return "Es";
684 if (std::is_same<Period, std::ratio<60>>::
value)
return "min";
685 if (std::is_same<Period, std::ratio<3600>>::
value)
return "h";
686 if (std::is_same<Period, std::ratio<86400>>::
value)
return "d";
690 enum class numeric_system {
697 enum class pad_type {
706 template <
typename OutputIt>
707 auto write_padding(OutputIt out, pad_type pad,
int width) -> OutputIt {
708 if (pad == pad_type::none)
return out;
709 return detail::fill_n(out, width, pad == pad_type::space ?
' ' :
'0');
712 template <
typename OutputIt>
713 auto write_padding(OutputIt out, pad_type pad) -> OutputIt {
714 if (pad != pad_type::none) *out++ = pad == pad_type::space ?
' ' :
'0';
719 template <
typename Char,
typename Handler>
720 FMTQUILL_CONSTEXPR
auto parse_chrono_format(
const Char* begin,
const Char* end,
721 Handler&& handler) ->
const Char* {
722 if (begin == end || *begin ==
'}')
return begin;
723 if (*begin !=
'%') FMTQUILL_THROW(format_error(
"invalid format"));
726 pad_type pad = pad_type::zero;
733 if (begin != ptr) handler.on_text(begin, ptr);
735 if (ptr == end) FMTQUILL_THROW(format_error(
"invalid format"));
739 pad = pad_type::space;
743 pad = pad_type::none;
747 if (ptr == end) FMTQUILL_THROW(format_error(
"invalid format"));
750 case '%': handler.on_text(ptr - 1, ptr);
break;
752 const Char newline[] = {
'\n'};
753 handler.on_text(newline, newline + 1);
757 const Char tab[] = {
'\t'};
758 handler.on_text(tab, tab + 1);
762 case 'Y': handler.on_year(numeric_system::standard, pad);
break;
763 case 'y': handler.on_short_year(numeric_system::standard);
break;
764 case 'C': handler.on_century(numeric_system::standard);
break;
765 case 'G': handler.on_iso_week_based_year();
break;
766 case 'g': handler.on_iso_week_based_short_year();
break;
768 case 'a': handler.on_abbr_weekday();
break;
769 case 'A': handler.on_full_weekday();
break;
770 case 'w': handler.on_dec0_weekday(numeric_system::standard);
break;
771 case 'u': handler.on_dec1_weekday(numeric_system::standard);
break;
774 case 'h': handler.on_abbr_month();
break;
775 case 'B': handler.on_full_month();
break;
776 case 'm': handler.on_dec_month(numeric_system::standard, pad);
break;
779 handler.on_dec0_week_of_year(numeric_system::standard, pad);
782 handler.on_dec1_week_of_year(numeric_system::standard, pad);
784 case 'V': handler.on_iso_week_of_year(numeric_system::standard, pad);
break;
785 case 'j': handler.on_day_of_year(pad);
break;
786 case 'd': handler.on_day_of_month(numeric_system::standard, pad);
break;
788 handler.on_day_of_month(numeric_system::standard, pad_type::space);
791 case 'H': handler.on_24_hour(numeric_system::standard, pad);
break;
792 case 'I': handler.on_12_hour(numeric_system::standard, pad);
break;
793 case 'M': handler.on_minute(numeric_system::standard, pad);
break;
794 case 'S': handler.on_second(numeric_system::standard, pad);
break;
796 case 'c': handler.on_datetime(numeric_system::standard);
break;
797 case 'x': handler.on_loc_date(numeric_system::standard);
break;
798 case 'X': handler.on_loc_time(numeric_system::standard);
break;
799 case 'D': handler.on_us_date();
break;
800 case 'F': handler.on_iso_date();
break;
801 case 'r': handler.on_12_hour_time();
break;
802 case 'R': handler.on_24_hour_time();
break;
803 case 'T': handler.on_iso_time();
break;
804 case 'p': handler.on_am_pm();
break;
805 case 'Q': handler.on_duration_value();
break;
806 case 'q': handler.on_duration_unit();
break;
807 case 'z': handler.on_utc_offset(numeric_system::standard);
break;
808 case 'Z': handler.on_tz_name();
break;
811 if (ptr == end) FMTQUILL_THROW(format_error(
"invalid format"));
814 case 'Y': handler.on_year(numeric_system::alternative, pad);
break;
815 case 'y': handler.on_offset_year();
break;
816 case 'C': handler.on_century(numeric_system::alternative);
break;
817 case 'c': handler.on_datetime(numeric_system::alternative);
break;
818 case 'x': handler.on_loc_date(numeric_system::alternative);
break;
819 case 'X': handler.on_loc_time(numeric_system::alternative);
break;
820 case 'z': handler.on_utc_offset(numeric_system::alternative);
break;
821 default: FMTQUILL_THROW(format_error(
"invalid format"));
826 if (ptr == end) FMTQUILL_THROW(format_error(
"invalid format"));
829 case 'y': handler.on_short_year(numeric_system::alternative);
break;
830 case 'm': handler.on_dec_month(numeric_system::alternative, pad);
break;
832 handler.on_dec0_week_of_year(numeric_system::alternative, pad);
835 handler.on_dec1_week_of_year(numeric_system::alternative, pad);
838 handler.on_iso_week_of_year(numeric_system::alternative, pad);
841 handler.on_day_of_month(numeric_system::alternative, pad);
844 handler.on_day_of_month(numeric_system::alternative, pad_type::space);
846 case 'w': handler.on_dec0_weekday(numeric_system::alternative);
break;
847 case 'u': handler.on_dec1_weekday(numeric_system::alternative);
break;
848 case 'H': handler.on_24_hour(numeric_system::alternative, pad);
break;
849 case 'I': handler.on_12_hour(numeric_system::alternative, pad);
break;
850 case 'M': handler.on_minute(numeric_system::alternative, pad);
break;
851 case 'S': handler.on_second(numeric_system::alternative, pad);
break;
852 case 'z': handler.on_utc_offset(numeric_system::alternative);
break;
853 default: FMTQUILL_THROW(format_error(
"invalid format"));
856 default: FMTQUILL_THROW(format_error(
"invalid format"));
860 if (begin != ptr) handler.on_text(begin, ptr);
865 FMTQUILL_CONSTEXPR
void unsupported() {
866 static_cast<Derived*
>(
this)->unsupported();
868 FMTQUILL_CONSTEXPR
void on_year(numeric_system, pad_type) { unsupported(); }
869 FMTQUILL_CONSTEXPR
void on_short_year(numeric_system) { unsupported(); }
870 FMTQUILL_CONSTEXPR
void on_offset_year() { unsupported(); }
871 FMTQUILL_CONSTEXPR
void on_century(numeric_system) { unsupported(); }
872 FMTQUILL_CONSTEXPR
void on_iso_week_based_year() { unsupported(); }
873 FMTQUILL_CONSTEXPR
void on_iso_week_based_short_year() { unsupported(); }
874 FMTQUILL_CONSTEXPR
void on_abbr_weekday() { unsupported(); }
875 FMTQUILL_CONSTEXPR
void on_full_weekday() { unsupported(); }
876 FMTQUILL_CONSTEXPR
void on_dec0_weekday(numeric_system) { unsupported(); }
877 FMTQUILL_CONSTEXPR
void on_dec1_weekday(numeric_system) { unsupported(); }
878 FMTQUILL_CONSTEXPR
void on_abbr_month() { unsupported(); }
879 FMTQUILL_CONSTEXPR
void on_full_month() { unsupported(); }
880 FMTQUILL_CONSTEXPR
void on_dec_month(numeric_system, pad_type) { unsupported(); }
881 FMTQUILL_CONSTEXPR
void on_dec0_week_of_year(numeric_system, pad_type) {
884 FMTQUILL_CONSTEXPR
void on_dec1_week_of_year(numeric_system, pad_type) {
887 FMTQUILL_CONSTEXPR
void on_iso_week_of_year(numeric_system, pad_type) {
890 FMTQUILL_CONSTEXPR
void on_day_of_year(pad_type) { unsupported(); }
891 FMTQUILL_CONSTEXPR
void on_day_of_month(numeric_system, pad_type) {
894 FMTQUILL_CONSTEXPR
void on_24_hour(numeric_system) { unsupported(); }
895 FMTQUILL_CONSTEXPR
void on_12_hour(numeric_system) { unsupported(); }
896 FMTQUILL_CONSTEXPR
void on_minute(numeric_system) { unsupported(); }
897 FMTQUILL_CONSTEXPR
void on_second(numeric_system) { unsupported(); }
898 FMTQUILL_CONSTEXPR
void on_datetime(numeric_system) { unsupported(); }
899 FMTQUILL_CONSTEXPR
void on_loc_date(numeric_system) { unsupported(); }
900 FMTQUILL_CONSTEXPR
void on_loc_time(numeric_system) { unsupported(); }
901 FMTQUILL_CONSTEXPR
void on_us_date() { unsupported(); }
902 FMTQUILL_CONSTEXPR
void on_iso_date() { unsupported(); }
903 FMTQUILL_CONSTEXPR
void on_12_hour_time() { unsupported(); }
904 FMTQUILL_CONSTEXPR
void on_24_hour_time() { unsupported(); }
905 FMTQUILL_CONSTEXPR
void on_iso_time() { unsupported(); }
906 FMTQUILL_CONSTEXPR
void on_am_pm() { unsupported(); }
907 FMTQUILL_CONSTEXPR
void on_duration_value() { unsupported(); }
908 FMTQUILL_CONSTEXPR
void on_duration_unit() { unsupported(); }
909 FMTQUILL_CONSTEXPR
void on_utc_offset(numeric_system) { unsupported(); }
910 FMTQUILL_CONSTEXPR
void on_tz_name() { unsupported(); }
915 bool has_timezone_ =
false;
919 : has_timezone_(has_timezone) {}
921 FMTQUILL_NORETURN
inline void unsupported() {
922 FMTQUILL_THROW(format_error(
"no format"));
925 template <
typename Char>
926 FMTQUILL_CONSTEXPR
void on_text(
const Char*,
const Char*) {}
927 FMTQUILL_CONSTEXPR
void on_year(numeric_system, pad_type) {}
928 FMTQUILL_CONSTEXPR
void on_short_year(numeric_system) {}
929 FMTQUILL_CONSTEXPR
void on_offset_year() {}
930 FMTQUILL_CONSTEXPR
void on_century(numeric_system) {}
931 FMTQUILL_CONSTEXPR
void on_iso_week_based_year() {}
932 FMTQUILL_CONSTEXPR
void on_iso_week_based_short_year() {}
933 FMTQUILL_CONSTEXPR
void on_abbr_weekday() {}
934 FMTQUILL_CONSTEXPR
void on_full_weekday() {}
935 FMTQUILL_CONSTEXPR
void on_dec0_weekday(numeric_system) {}
936 FMTQUILL_CONSTEXPR
void on_dec1_weekday(numeric_system) {}
937 FMTQUILL_CONSTEXPR
void on_abbr_month() {}
938 FMTQUILL_CONSTEXPR
void on_full_month() {}
939 FMTQUILL_CONSTEXPR
void on_dec_month(numeric_system, pad_type) {}
940 FMTQUILL_CONSTEXPR
void on_dec0_week_of_year(numeric_system, pad_type) {}
941 FMTQUILL_CONSTEXPR
void on_dec1_week_of_year(numeric_system, pad_type) {}
942 FMTQUILL_CONSTEXPR
void on_iso_week_of_year(numeric_system, pad_type) {}
943 FMTQUILL_CONSTEXPR
void on_day_of_year(pad_type) {}
944 FMTQUILL_CONSTEXPR
void on_day_of_month(numeric_system, pad_type) {}
945 FMTQUILL_CONSTEXPR
void on_24_hour(numeric_system, pad_type) {}
946 FMTQUILL_CONSTEXPR
void on_12_hour(numeric_system, pad_type) {}
947 FMTQUILL_CONSTEXPR
void on_minute(numeric_system, pad_type) {}
948 FMTQUILL_CONSTEXPR
void on_second(numeric_system, pad_type) {}
949 FMTQUILL_CONSTEXPR
void on_datetime(numeric_system) {}
950 FMTQUILL_CONSTEXPR
void on_loc_date(numeric_system) {}
951 FMTQUILL_CONSTEXPR
void on_loc_time(numeric_system) {}
952 FMTQUILL_CONSTEXPR
void on_us_date() {}
953 FMTQUILL_CONSTEXPR
void on_iso_date() {}
954 FMTQUILL_CONSTEXPR
void on_12_hour_time() {}
955 FMTQUILL_CONSTEXPR
void on_24_hour_time() {}
956 FMTQUILL_CONSTEXPR
void on_iso_time() {}
957 FMTQUILL_CONSTEXPR
void on_am_pm() {}
958 FMTQUILL_CONSTEXPR
void on_utc_offset(numeric_system) {
959 if (!has_timezone_) FMTQUILL_THROW(format_error(
"no timezone"));
961 FMTQUILL_CONSTEXPR
void on_tz_name() {
962 if (!has_timezone_) FMTQUILL_THROW(format_error(
"no timezone"));
966 inline auto tm_wday_full_name(
int wday) ->
const char* {
967 static constexpr
const char* full_name_list[] = {
968 "Sunday",
"Monday",
"Tuesday",
"Wednesday",
969 "Thursday",
"Friday",
"Saturday"};
970 return wday >= 0 && wday <= 6 ? full_name_list[wday] :
"?";
972 inline auto tm_wday_short_name(
int wday) ->
const char* {
973 static constexpr
const char* short_name_list[] = {
"Sun",
"Mon",
"Tue",
"Wed",
974 "Thu",
"Fri",
"Sat"};
975 return wday >= 0 && wday <= 6 ? short_name_list[wday] :
"???";
978 inline auto tm_mon_full_name(
int mon) ->
const char* {
979 static constexpr
const char* full_name_list[] = {
980 "January",
"February",
"March",
"April",
"May",
"June",
981 "July",
"August",
"September",
"October",
"November",
"December"};
982 return mon >= 0 && mon <= 11 ? full_name_list[mon] :
"?";
984 inline auto tm_mon_short_name(
int mon) ->
const char* {
985 static constexpr
const char* short_name_list[] = {
986 "Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
987 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
989 return mon >= 0 && mon <= 11 ? short_name_list[mon] :
"???";
992 template <
typename T,
typename =
void>
994 template <
typename T>
995 struct has_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>> : std::true_type {};
997 template <
typename T,
typename =
void>
struct has_tm_zone : std::false_type {};
998 template <
typename T>
999 struct has_tm_zone<T, void_t<decltype(T::tm_zone)>> : std::true_type {};
1001 template <typename T, FMTQUILL_ENABLE_IF(has_tm_zone<T>::value)>
1002 bool set_tm_zone(T& time,
char* tz) {
1006 template <typename T, FMTQUILL_ENABLE_IF(!has_tm_zone<T>::value)>
1007 bool set_tm_zone(T&,
char*) {
1011 inline char* utc() {
1012 static char tz[] =
"UTC";
1017 template <typename T, typename Int, FMTQUILL_ENABLE_IF(std::is_integral<T>::value)>
1018 inline auto to_nonnegative_int(T
value, Int upper) -> Int {
1019 if (!std::is_unsigned<Int>::value &&
1020 (value < 0 || to_unsigned(value) > to_unsigned(upper))) {
1021 FMTQUILL_THROW(format_error(
"chrono value is out of range"));
1023 return static_cast<Int
>(value);
1025 template <typename T, typename Int, FMTQUILL_ENABLE_IF(!std::is_integral<T>::value)>
1026 inline auto to_nonnegative_int(T value, Int upper) -> Int {
1027 auto int_value =
static_cast<Int
>(value);
1028 if (int_value < 0 || value > static_cast<T>(upper))
1029 FMTQUILL_THROW(format_error(
"invalid value"));
1033 constexpr
auto pow10(std::uint32_t n) ->
long long {
1034 return n == 0 ? 1 : 10 * pow10(n - 1);
1040 template <
long long Num,
long long Den,
int N = 0,
1041 bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
1043 static constexpr
int value =
1049 template <
long long Num,
long long Den,
int N>
1051 static constexpr
int value = (Num % Den == 0) ? N : 6;
1056 template <
typename Char,
typename OutputIt,
typename Duration>
1057 void write_fractional_seconds(OutputIt& out, Duration d,
int precision = -1) {
1058 constexpr
auto num_fractional_digits =
1060 Duration::period::den>
::value;
1062 using subsecond_precision = std::chrono::duration<
1063 typename std::common_type<
typename Duration::rep,
1064 std::chrono::seconds::rep>::type,
1065 std::ratio<1, pow10(num_fractional_digits)>>;
1067 const auto fractional = d - detail::duration_cast<std::chrono::seconds>(d);
1068 const auto subseconds =
1069 std::chrono::treat_as_floating_point<
1070 typename subsecond_precision::rep>::value
1071 ? fractional.count()
1072 : detail::duration_cast<subsecond_precision>(fractional).count();
1073 auto n =
static_cast<uint32_or_64_or_128_t<long long>
>(subseconds);
1074 const int num_digits = count_digits(n);
1076 int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits);
1077 if (precision < 0) {
1078 FMTQUILL_ASSERT(!std::is_floating_point<typename Duration::rep>::value,
"");
1079 if (std::ratio_less<
typename subsecond_precision::period,
1080 std::chrono::seconds::period>::value) {
1082 out = detail::fill_n(out, leading_zeroes,
'0');
1083 out = format_decimal<Char>(out, n, num_digits);
1085 }
else if (precision > 0) {
1087 leading_zeroes = min_of(leading_zeroes, precision);
1088 int remaining = precision - leading_zeroes;
1089 out = detail::fill_n(out, leading_zeroes,
'0');
1090 if (remaining < num_digits) {
1091 int num_truncated_digits = num_digits - remaining;
1092 n /= to_unsigned(pow10(to_unsigned(num_truncated_digits)));
1093 if (n != 0) out = format_decimal<Char>(out, n, remaining);
1097 out = format_decimal<Char>(out, n, num_digits);
1098 remaining -= num_digits;
1100 out = detail::fill_n(out, remaining,
'0');
1107 template <
typename Duration>
1108 void write_floating_seconds(
memory_buffer& buf, Duration duration,
1109 int num_fractional_digits = -1) {
1110 using rep =
typename Duration::rep;
1111 FMTQUILL_ASSERT(std::is_floating_point<rep>::value,
"");
1113 auto val = duration.count();
1115 if (num_fractional_digits < 0) {
1118 using namespace std;
1119 num_fractional_digits =
1121 Duration::period::den>
::value;
1122 if (num_fractional_digits < 6 && static_cast<rep>(round(val)) != val)
1123 num_fractional_digits = 6;
1126 fmtquill::format_to(std::back_inserter(buf), FMTQUILL_STRING(
"{:.{}f}"),
1127 std::fmod(val * static_cast<rep>(Duration::period::num) /
1128 static_cast<rep>(Duration::period::den),
1129 static_cast<rep>(60)),
1130 num_fractional_digits);
1133 template <
typename OutputIt,
typename Char,
1134 typename Duration = std::chrono::seconds>
1137 static constexpr
int days_per_week = 7;
1139 const std::locale& loc_;
1142 const Duration* subsecs_;
1145 auto tm_sec()
const noexcept ->
int {
1146 FMTQUILL_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61,
"");
1149 auto tm_min()
const noexcept ->
int {
1150 FMTQUILL_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59,
"");
1153 auto tm_hour()
const noexcept ->
int {
1154 FMTQUILL_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23,
"");
1157 auto tm_mday()
const noexcept ->
int {
1158 FMTQUILL_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31,
"");
1161 auto tm_mon()
const noexcept ->
int {
1162 FMTQUILL_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11,
"");
1165 auto tm_year()
const noexcept ->
long long {
return 1900ll + tm_.tm_year; }
1166 auto tm_wday()
const noexcept ->
int {
1167 FMTQUILL_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6,
"");
1170 auto tm_yday()
const noexcept ->
int {
1171 FMTQUILL_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365,
"");
1175 auto tm_hour12()
const noexcept ->
int {
1177 auto z = h < 12 ? h : h - 12;
1178 return z == 0 ? 12 : z;
1185 auto split_year_lower(
long long year)
const noexcept ->
int {
1186 auto l = year % 100;
1188 return static_cast<int>(l);
1192 auto iso_year_weeks(
long long curr_year)
const noexcept ->
int {
1193 auto prev_year = curr_year - 1;
1195 (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
1198 (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
1200 return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
1202 auto iso_week_num(
int tm_yday,
int tm_wday)
const noexcept ->
int {
1203 return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) /
1206 auto tm_iso_week_year()
const noexcept ->
long long {
1207 auto year = tm_year();
1208 auto w = iso_week_num(tm_yday(), tm_wday());
1209 if (w < 1)
return year - 1;
1210 if (w > iso_year_weeks(year))
return year + 1;
1213 auto tm_iso_week_of_year()
const noexcept ->
int {
1214 auto year = tm_year();
1215 auto w = iso_week_num(tm_yday(), tm_wday());
1216 if (w < 1)
return iso_year_weeks(year - 1);
1217 if (w > iso_year_weeks(year))
return 1;
1221 void write1(
int value) {
1222 *out_++ =
static_cast<char>(
'0' + to_unsigned(value) % 10);
1224 void write2(
int value) {
1225 const char* d = digits2(to_unsigned(value) % 100);
1229 void write2(
int value, pad_type pad) {
1230 unsigned int v = to_unsigned(value) % 100;
1232 const char* d = digits2(v);
1236 out_ = detail::write_padding(out_, pad);
1237 *out_++ =
static_cast<char>(
'0' + v);
1241 void write_year_extended(
long long year, pad_type pad) {
1244 bool negative = year < 0;
1249 uint32_or_64_or_128_t<long long> n = to_unsigned(year);
1250 const int num_digits = count_digits(n);
1251 if (negative && pad == pad_type::zero) *out_++ =
'-';
1252 if (width > num_digits)
1253 out_ = detail::write_padding(out_, pad, width - num_digits);
1254 if (negative && pad != pad_type::zero) *out_++ =
'-';
1255 out_ = format_decimal<Char>(out_, n, num_digits);
1257 void write_year(
long long year, pad_type pad) {
1258 write_year_extended(year, pad);
1261 void write_utc_offset(
long long offset, numeric_system ns) {
1269 write2(static_cast<int>(offset / 60));
1270 if (ns != numeric_system::standard) *out_++ =
':';
1271 write2(static_cast<int>(offset % 60));
1274 template <typename T, FMTQUILL_ENABLE_IF(has_tm_gmtoff<T>::value)>
1275 void format_utc_offset(
const T& tm, numeric_system ns) {
1276 write_utc_offset(tm.tm_gmtoff, ns);
1278 template <typename T, FMTQUILL_ENABLE_IF(!has_tm_gmtoff<T>::value)>
1279 void format_utc_offset(
const T&, numeric_system ns) {
1280 write_utc_offset(0, ns);
1283 template <typename T, FMTQUILL_ENABLE_IF(has_tm_zone<T>::value)>
1284 void format_tz_name(
const T& tm) {
1285 out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
1287 template <typename T, FMTQUILL_ENABLE_IF(!has_tm_zone<T>::value)>
1288 void format_tz_name(
const T&) {
1289 out_ = std::copy_n(utc(), 3, out_);
1292 void format_localized(
char format,
char modifier = 0) {
1293 out_ = write<Char>(out_, tm_, loc_, format, modifier);
1297 tm_writer(
const std::locale& loc, OutputIt out,
const std::tm& tm,
1298 const Duration* subsecs =
nullptr)
1300 is_classic_(loc_ == get_classic_locale()),
1305 auto out()
const -> OutputIt {
return out_; }
1307 FMTQUILL_CONSTEXPR
void on_text(
const Char* begin,
const Char* end) {
1308 out_ = copy<Char>(begin, end, out_);
1311 void on_abbr_weekday() {
1313 out_ = write(out_, tm_wday_short_name(tm_wday()));
1315 format_localized(
'a');
1317 void on_full_weekday() {
1319 out_ = write(out_, tm_wday_full_name(tm_wday()));
1321 format_localized(
'A');
1323 void on_dec0_weekday(numeric_system ns) {
1324 if (is_classic_ || ns == numeric_system::standard)
return write1(tm_wday());
1325 format_localized(
'w',
'O');
1327 void on_dec1_weekday(numeric_system ns) {
1328 if (is_classic_ || ns == numeric_system::standard) {
1329 auto wday = tm_wday();
1330 write1(wday == 0 ? days_per_week : wday);
1332 format_localized(
'u',
'O');
1336 void on_abbr_month() {
1338 out_ = write(out_, tm_mon_short_name(tm_mon()));
1340 format_localized(
'b');
1342 void on_full_month() {
1344 out_ = write(out_, tm_mon_full_name(tm_mon()));
1346 format_localized(
'B');
1349 void on_datetime(numeric_system ns) {
1355 on_day_of_month(numeric_system::standard, pad_type::space);
1359 on_year(numeric_system::standard, pad_type::space);
1361 format_localized(
'c', ns == numeric_system::standard ?
'\0' :
'E');
1364 void on_loc_date(numeric_system ns) {
1368 format_localized(
'x', ns == numeric_system::standard ?
'\0' :
'E');
1370 void on_loc_time(numeric_system ns) {
1374 format_localized(
'X', ns == numeric_system::standard ?
'\0' :
'E');
1378 write_digit2_separated(buf, to_unsigned(tm_mon() + 1),
1379 to_unsigned(tm_mday()),
1380 to_unsigned(split_year_lower(tm_year())),
'/');
1381 out_ = copy<Char>(std::begin(buf), std::end(buf), out_);
1383 void on_iso_date() {
1384 auto year = tm_year();
1387 if (year >= 0 && year < 10000) {
1388 write2digits(buf, static_cast<size_t>(year / 100));
1391 write_year_extended(year, pad_type::zero);
1394 write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100),
1395 to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
1397 out_ = copy<Char>(std::begin(buf) + offset, std::end(buf), out_);
1400 void on_utc_offset(numeric_system ns) { format_utc_offset(tm_, ns); }
1401 void on_tz_name() { format_tz_name(tm_); }
1403 void on_year(numeric_system ns, pad_type pad) {
1404 if (is_classic_ || ns == numeric_system::standard)
1405 return write_year(tm_year(), pad);
1406 format_localized(
'Y',
'E');
1408 void on_short_year(numeric_system ns) {
1409 if (is_classic_ || ns == numeric_system::standard)
1410 return write2(split_year_lower(tm_year()));
1411 format_localized(
'y',
'O');
1413 void on_offset_year() {
1414 if (is_classic_)
return write2(split_year_lower(tm_year()));
1415 format_localized(
'y',
'E');
1418 void on_century(numeric_system ns) {
1419 if (is_classic_ || ns == numeric_system::standard) {
1420 auto year = tm_year();
1421 auto upper = year / 100;
1422 if (year >= -99 && year < 0) {
1426 }
else if (upper >= 0 && upper < 100) {
1427 write2(static_cast<int>(upper));
1429 out_ = write<Char>(out_, upper);
1432 format_localized(
'C',
'E');
1436 void on_dec_month(numeric_system ns, pad_type pad) {
1437 if (is_classic_ || ns == numeric_system::standard)
1438 return write2(tm_mon() + 1, pad);
1439 format_localized(
'm',
'O');
1442 void on_dec0_week_of_year(numeric_system ns, pad_type pad) {
1443 if (is_classic_ || ns == numeric_system::standard)
1444 return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week,
1446 format_localized(
'U',
'O');
1448 void on_dec1_week_of_year(numeric_system ns, pad_type pad) {
1449 if (is_classic_ || ns == numeric_system::standard) {
1450 auto wday = tm_wday();
1451 write2((tm_yday() + days_per_week -
1452 (wday == 0 ? (days_per_week - 1) : (wday - 1))) /
1456 format_localized(
'W',
'O');
1459 void on_iso_week_of_year(numeric_system ns, pad_type pad) {
1460 if (is_classic_ || ns == numeric_system::standard)
1461 return write2(tm_iso_week_of_year(), pad);
1462 format_localized(
'V',
'O');
1465 void on_iso_week_based_year() {
1466 write_year(tm_iso_week_year(), pad_type::zero);
1468 void on_iso_week_based_short_year() {
1469 write2(split_year_lower(tm_iso_week_year()));
1472 void on_day_of_year(pad_type pad) {
1473 auto yday = tm_yday() + 1;
1474 auto digit1 = yday / 100;
1478 out_ = detail::write_padding(out_, pad);
1479 write2(yday % 100, pad);
1482 void on_day_of_month(numeric_system ns, pad_type pad) {
1483 if (is_classic_ || ns == numeric_system::standard)
1484 return write2(tm_mday(), pad);
1485 format_localized(
'd',
'O');
1488 void on_24_hour(numeric_system ns, pad_type pad) {
1489 if (is_classic_ || ns == numeric_system::standard)
1490 return write2(tm_hour(), pad);
1491 format_localized(
'H',
'O');
1493 void on_12_hour(numeric_system ns, pad_type pad) {
1494 if (is_classic_ || ns == numeric_system::standard)
1495 return write2(tm_hour12(), pad);
1496 format_localized(
'I',
'O');
1498 void on_minute(numeric_system ns, pad_type pad) {
1499 if (is_classic_ || ns == numeric_system::standard)
1500 return write2(tm_min(), pad);
1501 format_localized(
'M',
'O');
1504 void on_second(numeric_system ns, pad_type pad) {
1505 if (is_classic_ || ns == numeric_system::standard) {
1506 write2(tm_sec(), pad);
1508 if (std::is_floating_point<typename Duration::rep>::value) {
1510 write_floating_seconds(buf, *subsecs_);
1511 if (buf.
size() > 1) {
1513 out_ = copy<Char>(buf.begin() + 1, buf.end(), out_);
1516 write_fractional_seconds<Char>(out_, *subsecs_);
1521 format_localized(
'S',
'O');
1525 void on_12_hour_time() {
1528 write_digit2_separated(buf, to_unsigned(tm_hour12()),
1529 to_unsigned(tm_min()), to_unsigned(tm_sec()),
':');
1530 out_ = copy<Char>(std::begin(buf), std::end(buf), out_);
1534 format_localized(
'r');
1537 void on_24_hour_time() {
1542 void on_iso_time() {
1545 on_second(numeric_system::standard, pad_type::zero);
1550 *out_++ = tm_hour() < 12 ?
'A' :
'P';
1553 format_localized(
'p');
1558 void on_duration_value() {}
1559 void on_duration_unit() {}
1563 bool has_precision_integral =
false;
1565 FMTQUILL_NORETURN
inline void unsupported() { FMTQUILL_THROW(format_error(
"no date")); }
1567 template <
typename Char>
1568 FMTQUILL_CONSTEXPR
void on_text(
const Char*,
const Char*) {}
1569 FMTQUILL_CONSTEXPR
void on_day_of_year(pad_type) {}
1570 FMTQUILL_CONSTEXPR
void on_24_hour(numeric_system, pad_type) {}
1571 FMTQUILL_CONSTEXPR
void on_12_hour(numeric_system, pad_type) {}
1572 FMTQUILL_CONSTEXPR
void on_minute(numeric_system, pad_type) {}
1573 FMTQUILL_CONSTEXPR
void on_second(numeric_system, pad_type) {}
1574 FMTQUILL_CONSTEXPR
void on_12_hour_time() {}
1575 FMTQUILL_CONSTEXPR
void on_24_hour_time() {}
1576 FMTQUILL_CONSTEXPR
void on_iso_time() {}
1577 FMTQUILL_CONSTEXPR
void on_am_pm() {}
1578 FMTQUILL_CONSTEXPR
void on_duration_value()
const {
1579 if (has_precision_integral)
1580 FMTQUILL_THROW(format_error(
"precision not allowed for this argument type"));
1582 FMTQUILL_CONSTEXPR
void on_duration_unit() {}
1585 template <
typename T,
1587 inline auto isfinite(T) ->
bool {
1591 template <typename T, FMTQUILL_ENABLE_IF(std::is_integral<T>::value)>
1592 inline auto mod(T x,
int y) -> T {
1593 return x %
static_cast<T
>(y);
1595 template <typename T, FMTQUILL_ENABLE_IF(std::is_floating_point<T>::value)>
1596 inline auto mod(T x,
int y) -> T {
1597 return std::fmod(x, static_cast<T>(y));
1602 template <typename T, bool INTEGRAL = std::is_integral<T>::value>
1608 using type =
typename std::make_unsigned<T>::type;
1611 template <
typename Rep,
typename Period,
1612 FMTQUILL_ENABLE_IF(std::is_integral<Rep>::value)>
1613 inline auto get_milliseconds(std::chrono::duration<Rep, Period> d)
1614 -> std::chrono::duration<Rep, std::milli> {
1616 #if FMTQUILL_SAFE_DURATION_CAST 1617 using common_seconds_type =
1618 typename std::common_type<decltype(d), std::chrono::seconds>::type;
1619 auto d_as_common = detail::duration_cast<common_seconds_type>(d);
1620 auto d_as_whole_seconds =
1621 detail::duration_cast<std::chrono::seconds>(d_as_common);
1623 auto diff = d_as_common - d_as_whole_seconds;
1624 auto ms = detail::duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
1627 auto s = detail::duration_cast<std::chrono::seconds>(d);
1628 return detail::duration_cast<std::chrono::milliseconds>(d - s);
1632 template <
typename Char,
typename Rep,
typename OutputIt,
1633 FMTQUILL_ENABLE_IF(std::is_integral<Rep>::value)>
1634 auto format_duration_value(OutputIt out, Rep val,
int) -> OutputIt {
1635 return write<Char>(out, val);
1638 template <
typename Char,
typename Rep,
typename OutputIt,
1639 FMTQUILL_ENABLE_IF(std::is_floating_point<Rep>::value)>
1640 auto format_duration_value(OutputIt out, Rep val,
int precision) -> OutputIt {
1642 specs.precision = precision;
1643 specs.set_type(precision >= 0 ? presentation_type::fixed
1644 : presentation_type::general);
1645 return write<Char>(out, val, specs);
1648 template <
typename Char,
typename OutputIt>
1649 auto copy_unit(
string_view unit, OutputIt out, Char) -> OutputIt {
1650 return copy<Char>(unit.begin(), unit.end(), out);
1653 template <
typename OutputIt>
1654 auto copy_unit(
string_view unit, OutputIt out,
wchar_t) -> OutputIt {
1658 return copy<wchar_t>(u.c_str(), u.c_str() + u.size(), out);
1661 template <
typename Char,
typename Period,
typename OutputIt>
1662 auto format_duration_unit(OutputIt out) -> OutputIt {
1663 if (
const char* unit = get_units<Period>())
1666 out = write<Char>(out, Period::num);
1667 if (const_check(Period::den != 1)) {
1669 out = write<Char>(out, Period::den);
1679 std::locale locale_;
1681 bool has_locale_ =
false;
1686 ::new (&locale_) std::locale(loc.template get<std::locale>());
1689 if (has_locale_) locale_.~locale();
1691 inline operator const std::locale&()
const {
1692 return has_locale_ ? locale_ : get_classic_locale();
1696 template <
typename Char,
typename Rep,
typename Period>
1702 conditional_t<std::is_integral<Rep>::value &&
sizeof(Rep) <
sizeof(
int),
1703 unsigned,
typename make_unsigned_or_unchanged<Rep>::type>;
1707 bool localized =
false;
1708 using seconds = std::chrono::duration<rep>;
1710 using milliseconds = std::chrono::duration<rep, std::milli>;
1717 : out(o), val(static_cast<rep>(d.count())), locale(loc), negative(
false) {
1718 if (d.count() < 0) {
1726 s = detail::duration_cast<seconds>(std::chrono::duration<rep, Period>(val));
1730 auto handle_nan_inf() ->
bool {
1731 if (isfinite(val))
return false;
1738 std::copy_n(
"inf", 3, out);
1740 std::copy_n(
"-inf", 4, out);
1744 auto days()
const -> Rep {
return static_cast<Rep
>(s.count() / 86400); }
1745 auto hour()
const -> Rep {
1746 return static_cast<Rep
>(mod((s.count() / 3600), 24));
1749 auto hour12()
const -> Rep {
1750 Rep hour =
static_cast<Rep
>(mod((s.count() / 3600), 12));
1751 return hour <= 0 ? 12 : hour;
1754 auto minute()
const -> Rep {
1755 return static_cast<Rep
>(mod((s.count() / 60), 60));
1757 auto second()
const -> Rep {
return static_cast<Rep
>(mod(s.count(), 60)); }
1759 auto time()
const -> std::tm {
1760 auto time = std::tm();
1761 time.tm_hour = to_nonnegative_int(hour(), 24);
1762 time.tm_min = to_nonnegative_int(minute(), 60);
1763 time.tm_sec = to_nonnegative_int(second(), 60);
1768 if (!negative)
return;
1773 void write(Rep value,
int width, pad_type pad = pad_type::zero) {
1775 if (isnan(value))
return write_nan();
1776 uint32_or_64_or_128_t<int> n =
1777 to_unsigned(to_nonnegative_int(value, max_value<int>()));
1778 int num_digits = detail::count_digits(n);
1779 if (width > num_digits) {
1780 out = detail::write_padding(out, pad, width - num_digits);
1782 out = format_decimal<Char>(out, n, num_digits);
1785 void write_nan() { std::copy_n(
"nan", 3, out); }
1787 template <
typename Callback,
typename... Args>
1788 void format_tm(
const tm& time, Callback cb, Args... args) {
1789 if (isnan(val))
return write_nan();
1796 void on_text(
const Char* begin,
const Char* end) {
1797 copy<Char>(begin, end, out);
1801 void on_abbr_weekday() {}
1802 void on_full_weekday() {}
1803 void on_dec0_weekday(numeric_system) {}
1804 void on_dec1_weekday(numeric_system) {}
1805 void on_abbr_month() {}
1806 void on_full_month() {}
1807 void on_datetime(numeric_system) {}
1808 void on_loc_date(numeric_system) {}
1809 void on_loc_time(numeric_system) {}
1810 void on_us_date() {}
1811 void on_iso_date() {}
1812 void on_utc_offset(numeric_system) {}
1813 void on_tz_name() {}
1814 void on_year(numeric_system, pad_type) {}
1815 void on_short_year(numeric_system) {}
1816 void on_offset_year() {}
1817 void on_century(numeric_system) {}
1818 void on_iso_week_based_year() {}
1819 void on_iso_week_based_short_year() {}
1820 void on_dec_month(numeric_system, pad_type) {}
1821 void on_dec0_week_of_year(numeric_system, pad_type) {}
1822 void on_dec1_week_of_year(numeric_system, pad_type) {}
1823 void on_iso_week_of_year(numeric_system, pad_type) {}
1824 void on_day_of_month(numeric_system, pad_type) {}
1826 void on_day_of_year(pad_type) {
1827 if (handle_nan_inf())
return;
1831 void on_24_hour(numeric_system ns, pad_type pad) {
1832 if (handle_nan_inf())
return;
1834 if (ns == numeric_system::standard)
return write(hour(), 2, pad);
1836 time.tm_hour = to_nonnegative_int(hour(), 24);
1837 format_tm(time, &tm_writer_type::on_24_hour, ns, pad);
1840 void on_12_hour(numeric_system ns, pad_type pad) {
1841 if (handle_nan_inf())
return;
1843 if (ns == numeric_system::standard)
return write(hour12(), 2, pad);
1845 time.tm_hour = to_nonnegative_int(hour12(), 12);
1846 format_tm(time, &tm_writer_type::on_12_hour, ns, pad);
1849 void on_minute(numeric_system ns, pad_type pad) {
1850 if (handle_nan_inf())
return;
1852 if (ns == numeric_system::standard)
return write(minute(), 2, pad);
1854 time.tm_min = to_nonnegative_int(minute(), 60);
1855 format_tm(time, &tm_writer_type::on_minute, ns, pad);
1858 void on_second(numeric_system ns, pad_type pad) {
1859 if (handle_nan_inf())
return;
1861 if (ns == numeric_system::standard) {
1862 if (std::is_floating_point<rep>::value) {
1864 write_floating_seconds(buf, std::chrono::duration<rep, Period>(val),
1866 if (negative) *out++ =
'-';
1867 if (buf.
size() < 2 || buf[1] ==
'.')
1868 out = detail::write_padding(out, pad);
1869 out = copy<Char>(buf.begin(), buf.end(), out);
1871 write(second(), 2, pad);
1872 write_fractional_seconds<Char>(
1873 out, std::chrono::duration<rep, Period>(val), precision);
1878 time.tm_sec = to_nonnegative_int(second(), 60);
1879 format_tm(time, &tm_writer_type::on_second, ns, pad);
1882 void on_12_hour_time() {
1883 if (handle_nan_inf())
return;
1884 format_tm(time(), &tm_writer_type::on_12_hour_time);
1887 void on_24_hour_time() {
1888 if (handle_nan_inf()) {
1899 void on_iso_time() {
1902 if (handle_nan_inf())
return;
1903 on_second(numeric_system::standard, pad_type::zero);
1907 if (handle_nan_inf())
return;
1908 format_tm(time(), &tm_writer_type::on_am_pm);
1911 void on_duration_value() {
1912 if (handle_nan_inf())
return;
1914 out = format_duration_value<Char>(out, val, precision);
1917 void on_duration_unit() { out = format_duration_unit<Char, Period>(out); }
1922 #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 1923 using weekday = std::chrono::weekday;
1924 using day = std::chrono::day;
1925 using month = std::chrono::month;
1926 using year = std::chrono::year;
1932 unsigned char value_;
1936 constexpr
explicit weekday(
unsigned wd) noexcept
1937 : value_(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
1938 constexpr
auto c_encoding()
const noexcept ->
unsigned {
return value_; }
1943 unsigned char value_;
1947 constexpr
explicit day(
unsigned d) noexcept
1948 : value_(static_cast<unsigned char>(d)) {}
1949 constexpr
explicit operator unsigned()
const noexcept {
return value_; }
1954 unsigned char value_;
1958 constexpr
explicit month(
unsigned m) noexcept
1959 : value_(static_cast<unsigned char>(m)) {}
1960 constexpr
explicit operator unsigned()
const noexcept {
return value_; }
1969 constexpr
explicit year(
int y) noexcept : value_(y) {}
1970 constexpr
explicit operator int()
const noexcept {
return value_; }
1975 fmtquill::year year_;
1976 fmtquill::month month_;
1982 : year_(y), month_(m), day_(d) {}
1983 constexpr
auto year()
const noexcept -> fmtquill::year {
return year_; }
1984 constexpr
auto month()
const noexcept -> fmtquill::month {
return month_; }
1985 constexpr
auto day()
const noexcept -> fmtquill::day {
return day_; }
1987 #endif // __cpp_lib_chrono >= 201907 1989 template <
typename Char>
1990 struct formatter<
weekday, Char> :
private formatter<std::tm, Char> {
1992 bool use_tm_formatter_ =
false;
1996 auto it = ctx.
begin(), end = ctx.
end();
1997 if (it != end && *it ==
'L') {
1999 this->set_localized();
2001 use_tm_formatter_ = it != end && *it !=
'}';
2002 return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
2005 template <
typename FormatContext>
2006 auto format(
weekday wd, FormatContext& ctx)
const -> decltype(ctx.out()) {
2007 auto time = std::tm();
2008 time.tm_wday =
static_cast<int>(wd.c_encoding());
2009 if (use_tm_formatter_)
return formatter<std::tm, Char>::format(time, ctx);
2012 w.on_abbr_weekday();
2017 template <
typename Char>
2018 struct formatter<
day, Char> :
private formatter<std::tm, Char> {
2020 bool use_tm_formatter_ =
false;
2024 auto it = ctx.
begin(), end = ctx.
end();
2025 use_tm_formatter_ = it != end && *it !=
'}';
2026 return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
2029 template <
typename FormatContext>
2030 auto format(
day d, FormatContext& ctx)
const -> decltype(ctx.out()) {
2031 auto time = std::tm();
2032 time.tm_mday =
static_cast<int>(
static_cast<unsigned>(d));
2033 if (use_tm_formatter_)
return formatter<std::tm, Char>::format(time, ctx);
2036 w.on_day_of_month(detail::numeric_system::standard, detail::pad_type::zero);
2041 template <
typename Char>
2042 struct formatter<
month, Char> :
private formatter<std::tm, Char> {
2044 bool use_tm_formatter_ =
false;
2048 auto it = ctx.
begin(), end = ctx.
end();
2049 if (it != end && *it ==
'L') {
2051 this->set_localized();
2053 use_tm_formatter_ = it != end && *it !=
'}';
2054 return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
2057 template <
typename FormatContext>
2058 auto format(
month m, FormatContext& ctx)
const -> decltype(ctx.out()) {
2059 auto time = std::tm();
2060 time.tm_mon =
static_cast<int>(
static_cast<unsigned>(m)) - 1;
2061 if (use_tm_formatter_)
return formatter<std::tm, Char>::format(time, ctx);
2069 template <
typename Char>
2070 struct formatter<
year, Char> :
private formatter<std::tm, Char> {
2072 bool use_tm_formatter_ =
false;
2076 auto it = ctx.
begin(), end = ctx.
end();
2077 use_tm_formatter_ = it != end && *it !=
'}';
2078 return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
2081 template <
typename FormatContext>
2082 auto format(
year y, FormatContext& ctx)
const -> decltype(ctx.out()) {
2083 auto time = std::tm();
2084 time.tm_year =
static_cast<int>(y) - 1900;
2085 if (use_tm_formatter_)
return formatter<std::tm, Char>::format(time, ctx);
2088 w.on_year(detail::numeric_system::standard, detail::pad_type::zero);
2093 template <
typename Char>
2096 bool use_tm_formatter_ =
false;
2100 auto it = ctx.
begin(), end = ctx.
end();
2101 use_tm_formatter_ = it != end && *it !=
'}';
2102 return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
2105 template <
typename FormatContext>
2107 -> decltype(ctx.out()) {
2108 auto time = std::tm();
2109 time.tm_year =
static_cast<int>(val.year()) - 1900;
2110 time.tm_mon =
static_cast<int>(
static_cast<unsigned>(val.month())) - 1;
2111 time.tm_mday =
static_cast<int>(
static_cast<unsigned>(val.day()));
2112 if (use_tm_formatter_)
return formatter<std::tm, Char>::format(time, ctx);
2120 template <
typename Rep,
typename Period,
typename Char>
2121 struct formatter<
std::chrono::duration<Rep, Period>, Char> {
2130 auto it = ctx.
begin(), end = ctx.
end();
2131 if (it == end || *it ==
'}')
return it;
2133 it = detail::parse_align(it, end, specs_);
2134 if (it == end)
return it;
2137 if ((c >=
'0' && c <=
'9') || c ==
'{') {
2138 it = detail::parse_width(it, end, specs_, width_ref_, ctx);
2139 if (it == end)
return it;
2144 checker.has_precision_integral = !std::is_floating_point<Rep>::value;
2145 it = detail::parse_precision(it, end, specs_, precision_ref_, ctx);
2147 if (it != end && *it ==
'L') {
2148 specs_.set_localized();
2151 end = detail::parse_chrono_format(it, end, checker);
2152 fmt_ = {it, detail::to_unsigned(end - it)};
2156 template <
typename FormatContext>
2157 auto format(std::chrono::duration<Rep, Period> d, FormatContext& ctx)
const 2158 -> decltype(ctx.out()) {
2159 auto specs = specs_;
2160 auto precision = specs.precision;
2161 specs.precision = -1;
2162 auto begin = fmt_.begin(), end = fmt_.end();
2167 detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
2169 detail::handle_dynamic_spec(specs.dynamic_precision(), precision,
2170 precision_ref_, ctx);
2171 if (begin == end || *begin ==
'}') {
2172 out = detail::format_duration_value<Char>(out, d.count(), precision);
2173 detail::format_duration_unit<Char, Period>(out);
2177 f.precision = precision;
2178 f.localized = specs_.localized();
2179 detail::parse_chrono_format(begin, end, f);
2181 return detail::write(
2186 template <
typename Char>
struct formatter<
std::tm, Char> {
2194 auto localized()
const ->
bool {
return specs_.localized(); }
2195 FMTQUILL_CONSTEXPR
void set_localized() { specs_.set_localized(); }
2199 auto it = ctx.
begin(), end = ctx.
end();
2200 if (it == end || *it ==
'}')
return it;
2202 it = detail::parse_align(it, end, specs_);
2203 if (it == end)
return it;
2206 if ((c >=
'0' && c <=
'9') || c ==
'{') {
2207 it = detail::parse_width(it, end, specs_, width_ref_, ctx);
2208 if (it == end)
return it;
2212 specs_.set_localized();
2216 end = detail::parse_chrono_format(it, end,
2219 if (end != it) fmt_ = {it, detail::to_unsigned(end - it)};
2223 template <
typename Duration,
typename FormatContext>
2224 auto do_format(
const std::tm& tm, FormatContext& ctx,
2225 const Duration* subsecs)
const -> decltype(ctx.out()) {
2226 auto specs = specs_;
2229 detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
2235 loc, out, tm, subsecs);
2236 detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w);
2237 return detail::write(
2246 template <
typename FormatContext>
2247 auto format(
const std::tm& tm, FormatContext& ctx)
const 2248 -> decltype(ctx.out()) {
2249 return do_format<std::chrono::seconds>(tm, ctx,
nullptr);
2254 template <
typename Char,
typename Duration>
2255 struct formatter<sys_time<Duration>, Char> :
private formatter<std::tm, Char> {
2257 return this->do_parse(ctx,
true);
2260 template <
typename FormatContext>
2261 auto format(sys_time<Duration> val, FormatContext& ctx)
const 2262 -> decltype(ctx.out()) {
2263 std::tm tm = gmtime(val);
2264 using period =
typename Duration::period;
2265 if (detail::const_check(
2266 period::num == 1 && period::den == 1 &&
2267 !std::is_floating_point<typename Duration::rep>::value)) {
2268 detail::set_tm_zone(tm, detail::utc());
2269 return formatter<std::tm, Char>::format(tm, ctx);
2271 Duration epoch = val.time_since_epoch();
2272 Duration subsecs = detail::duration_cast<Duration>(
2273 epoch - detail::duration_cast<std::chrono::seconds>(epoch));
2274 if (subsecs.count() < 0) {
2275 auto second = detail::duration_cast<Duration>(std::chrono::seconds(1));
2276 if (tm.tm_sec != 0) {
2279 tm = gmtime(val - second);
2280 detail::set_tm_zone(tm, detail::utc());
2284 return formatter<std::tm, Char>::do_format(tm, ctx, &subsecs);
2288 template <
typename Duration,
typename Char>
2289 struct formatter<utc_time<Duration>, Char>
2290 : formatter<sys_time<Duration>, Char> {
2291 template <
typename FormatContext>
2292 auto format(utc_time<Duration> val, FormatContext& ctx)
const 2293 -> decltype(ctx.out()) {
2294 return formatter<sys_time<Duration>, Char>::format(
2295 detail::utc_clock::to_sys(val), ctx);
2299 template <
typename Duration,
typename Char>
2300 struct formatter<local_time<Duration>, Char>
2301 :
private formatter<std::tm, Char> {
2303 return this->do_parse(ctx,
false);
2306 template <
typename FormatContext>
2307 auto format(local_time<Duration> val, FormatContext& ctx)
const 2308 -> decltype(ctx.out()) {
2309 auto time_since_epoch = val.time_since_epoch();
2310 auto seconds_since_epoch =
2311 detail::duration_cast<std::chrono::seconds>(time_since_epoch);
2314 std::tm t = gmtime(seconds_since_epoch.count());
2315 using period =
typename Duration::period;
2316 if (period::num == 1 && period::den == 1 &&
2317 !std::is_floating_point<typename Duration::rep>::value) {
2318 return formatter<std::tm, Char>::format(t, ctx);
2321 detail::duration_cast<Duration>(time_since_epoch - seconds_since_epoch);
2322 return formatter<std::tm, Char>::do_format(t, ctx, &subsecs);
2327 FMTQUILL_END_NAMESPACE
2329 #endif // FMTQUILL_CHRONO_H_ Definition: format.h:2481
A dynamically growing memory buffer for trivially copyable/constructible types with the first SIZE el...
Definition: format.h:790
FMTQUILL_CONSTEXPR auto data() noexcept -> T *
Returns a pointer to the buffer data (not null-terminated).
Definition: base.h:1799
Definition: chrono.h:1930
Definition: chrono.h:1952
Parsing context consisting of a format string range being parsed and an argument counter for automati...
Definition: base.h:644
Definition: chrono.h:1973
FMTQUILL_CONSTEXPR20 void append(const U *begin, const U *end)
Appends data to the end of the buffer.
Definition: base.h:1833
constexpr auto data() const noexcept -> const Char *
Returns a pointer to the string data.
Definition: base.h:565
Definition: doctest.h:530
constexpr auto size() const noexcept -> size_t
Returns the string size.
Definition: base.h:568
Definition: chrono.h:1963
Setups a signal handler to handle fatal signals.
Definition: BackendManager.h:24
constexpr auto end() const noexcept -> iterator
Returns an iterator past the end of the format string range being parsed.
Definition: base.h:893
constexpr auto size() const noexcept -> size_t
Returns the size of this buffer.
Definition: base.h:1793
Definition: chrono.h:1603
Definition: chrono.h:1941
Definition: format.h:1289
Definition: chrono.h:1676
constexpr auto begin() const noexcept -> iterator
Returns an iterator to the beginning of the format string range being parsed.
Definition: base.h:890
Definition: format.h:1272
Definition: chrono.h:1042
Definition: chrono.h:1135