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 42 template <
typename To,
typename From,
43 FMTQUILL_ENABLE_IF(!std::is_same<From, To>::value &&
44 std::numeric_limits<From>::is_signed ==
45 std::numeric_limits<To>::is_signed)>
46 FMTQUILL_CONSTEXPR
auto lossless_integral_conversion(
const From from,
int& ec)
49 using F = std::numeric_limits<From>;
50 using T = std::numeric_limits<To>;
51 static_assert(F::is_integer,
"From must be integral");
52 static_assert(T::is_integer,
"To must be integral");
55 if (detail::const_check(F::digits <= T::digits)) {
59 if (from < (T::min)() || from > (T::max)()) {
65 return static_cast<To
>(from);
70 template <
typename To,
typename From,
71 FMTQUILL_ENABLE_IF(!std::is_same<From, To>::value &&
72 std::numeric_limits<From>::is_signed !=
73 std::numeric_limits<To>::is_signed)>
74 FMTQUILL_CONSTEXPR
auto lossless_integral_conversion(
const From from,
int& ec)
77 using F = std::numeric_limits<From>;
78 using T = std::numeric_limits<To>;
79 static_assert(F::is_integer,
"From must be integral");
80 static_assert(T::is_integer,
"To must be integral");
82 if (detail::const_check(F::is_signed && !T::is_signed)) {
84 if (fmtquill::detail::is_negative(from)) {
89 if (detail::const_check(F::digits > T::digits) &&
90 from > static_cast<From>(detail::max_value<To>())) {
96 if (detail::const_check(!F::is_signed && T::is_signed &&
97 F::digits >= T::digits) &&
98 from > static_cast<From>(detail::max_value<To>())) {
102 return static_cast<To
>(from);
105 template <
typename To,
typename From,
106 FMTQUILL_ENABLE_IF(std::is_same<From, To>::value)>
107 FMTQUILL_CONSTEXPR
auto lossless_integral_conversion(
const From from,
int& ec)
127 template <
typename To,
typename From,
128 FMTQUILL_ENABLE_IF(!std::is_same<From, To>::value)>
129 FMTQUILL_CONSTEXPR
auto safe_float_conversion(
const From from,
int& ec) -> To {
131 using T = std::numeric_limits<To>;
132 static_assert(std::is_floating_point<From>::value,
"From must be floating");
133 static_assert(std::is_floating_point<To>::value,
"To must be floating");
136 if (std::isfinite(from)) {
137 if (from >= T::lowest() && from <= (T::max)()) {
138 return static_cast<To
>(from);
146 return static_cast<To
>(from);
149 template <
typename To,
typename From,
150 FMTQUILL_ENABLE_IF(std::is_same<From, To>::value)>
151 FMTQUILL_CONSTEXPR
auto safe_float_conversion(
const From from,
int& ec) -> To {
153 static_assert(std::is_floating_point<From>::value,
"From must be floating");
158 template <
typename To,
typename FromRep,
typename FromPeriod,
159 FMTQUILL_ENABLE_IF(std::is_floating_point<FromRep>::value),
160 FMTQUILL_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
163 using From = std::chrono::duration<FromRep, FromPeriod>;
169 : std::ratio_divide<typename From::period, typename To::period> {};
171 static_assert(Factor::num > 0,
"num must be positive");
172 static_assert(Factor::den > 0,
"den must be positive");
178 using IntermediateRep =
179 typename std::common_type<
typename From::rep,
typename To::rep,
180 decltype(Factor::num)>::type;
184 IntermediateRep count =
185 safe_float_conversion<IntermediateRep>(from.count(), ec);
191 if (detail::const_check(Factor::num != 1)) {
192 constexpr
auto max1 = detail::max_value<IntermediateRep>() /
193 static_cast<IntermediateRep>(Factor::num);
198 constexpr
auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
199 static_cast<IntermediateRep
>(Factor::num);
204 count *=
static_cast<IntermediateRep
>(Factor::num);
208 if (detail::const_check(Factor::den != 1)) {
209 using common_t =
typename std::common_type<IntermediateRep, intmax_t>::type;
210 count /=
static_cast<common_t
>(Factor::den);
214 using ToRep =
typename To::rep;
216 const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
228 #ifdef FMTQUILL_USE_UTC_TIME 230 #elif defined(__cpp_lib_chrono) 231 # define FMTQUILL_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) 233 # define FMTQUILL_USE_UTC_TIME 0 235 #if FMTQUILL_USE_UTC_TIME 236 using utc_clock = std::chrono::utc_clock;
239 template <
typename T>
void to_sys(T);
244 #ifdef FMTQUILL_USE_LOCAL_TIME 246 #elif defined(__cpp_lib_chrono) 247 # define FMTQUILL_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) 249 # define FMTQUILL_USE_LOCAL_TIME 0 251 #if FMTQUILL_USE_LOCAL_TIME 252 using local_t = std::chrono::local_t;
259 template <
typename Duration>
260 using sys_time = std::chrono::time_point<std::chrono::system_clock, Duration>;
262 template <
typename Duration>
263 using utc_time = std::chrono::time_point<detail::utc_clock, Duration>;
265 template <
class Duration>
266 using local_time = std::chrono::time_point<detail::local_t, Duration>;
272 #define FMTQUILL_NOMACRO 274 template <
typename T =
void>
struct null {};
275 inline auto gmtime_r(...) ->
null<> {
return null<>(); }
276 inline auto gmtime_s(...) ->
null<> {
return null<>(); }
280 template <
typename StreamBuf>
class formatbuf :
public StreamBuf {
282 using char_type =
typename StreamBuf::char_type;
283 using streamsize = decltype(std::declval<StreamBuf>().sputn(
nullptr, 0));
284 using int_type =
typename StreamBuf::int_type;
285 using traits_type =
typename StreamBuf::traits_type;
299 auto overflow(int_type ch) -> int_type
override {
300 if (!traits_type::eq_int_type(ch, traits_type::eof()))
301 buffer_.push_back(static_cast<char_type>(ch));
305 auto xsputn(
const char_type* s, streamsize count) -> streamsize
override {
306 buffer_.
append(s, s + count);
311 inline auto get_classic_locale() ->
const std::locale& {
312 static const auto& locale = std::locale::classic();
317 static constexpr
size_t max_size = 32;
318 CodeUnit buf[max_size];
322 template <
typename CodeUnit>
324 const std::locale& loc) {
325 FMTQUILL_PRAGMA_CLANG(diagnostic push)
326 FMTQUILL_PRAGMA_CLANG(diagnostic ignored
"-Wdeprecated")
327 auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
328 FMTQUILL_PRAGMA_CLANG(diagnostic pop)
329 auto mb = std::mbstate_t();
330 const char* from_next =
nullptr;
331 auto result = f.in(mb, in.begin(), in.end(), from_next, std::begin(out.buf),
332 std::end(out.buf), out.end);
333 if (result != std::codecvt_base::ok)
334 FMTQUILL_THROW(format_error(
"failed to format time"));
337 template <
typename OutputIt>
338 auto write_encoded_tm_str(OutputIt out,
string_view in,
const std::locale& loc)
340 if (const_check(detail::use_utf8) && loc != get_classic_locale()) {
343 #if FMTQUILL_MSC_VERSION != 0 || \ 344 (defined(__GLIBCXX__) && \ 345 (!defined(_GLIBCXX_USE_DUAL_ABI) || _GLIBCXX_USE_DUAL_ABI == 0)) 348 using code_unit = wchar_t;
350 using code_unit = char32_t;
355 write_codecvt(unit, in, loc);
359 if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)}))
360 FMTQUILL_THROW(format_error(
"failed to format time"));
361 return copy<char>(u.c_str(), u.c_str() + u.size(), out);
363 return copy<char>(in.
data(), in.
data() + in.
size(), out);
366 template <
typename Char,
typename OutputIt,
367 FMTQUILL_ENABLE_IF(!std::is_same<Char, char>::value)>
368 auto write_tm_str(OutputIt out,
string_view sv,
const std::locale& loc)
371 write_codecvt(unit, sv, loc);
372 return copy<Char>(unit.buf, unit.end, out);
375 template <
typename Char,
typename OutputIt,
376 FMTQUILL_ENABLE_IF(std::is_same<Char, char>::value)>
377 auto write_tm_str(OutputIt out,
string_view sv,
const std::locale& loc)
379 return write_encoded_tm_str(out, sv, loc);
382 template <
typename Char>
383 inline void do_write(
buffer<Char>& buf,
const std::tm& time,
384 const std::locale& loc,
char format,
char modifier) {
388 const auto& facet = std::use_facet<std::time_put<Char>>(loc);
389 auto end = facet.put(os, os, Char(
' '), &time, format, modifier);
390 if (end.failed()) FMTQUILL_THROW(format_error(
"failed to format time"));
393 template <
typename Char,
typename OutputIt,
394 FMTQUILL_ENABLE_IF(!std::is_same<Char, char>::value)>
395 auto write(OutputIt out,
const std::tm& time,
const std::locale& loc,
396 char format,
char modifier = 0) -> OutputIt {
397 auto&& buf = get_buffer<Char>(out);
398 do_write<Char>(buf, time, loc, format, modifier);
399 return get_iterator(buf, out);
402 template <
typename Char,
typename OutputIt,
403 FMTQUILL_ENABLE_IF(std::is_same<Char, char>::value)>
404 auto write(OutputIt out,
const std::tm& time,
const std::locale& loc,
405 char format,
char modifier = 0) -> OutputIt {
407 do_write<char>(buf, time, loc, format, modifier);
411 template <
typename T,
typename U>
412 using is_similar_arithmetic_type =
413 bool_constant<(std::is_integral<T>::value && std::is_integral<U>::value) ||
414 (std::is_floating_point<T>::value &&
415 std::is_floating_point<U>::value)>;
417 FMTQUILL_NORETURN
inline void throw_duration_error() {
418 FMTQUILL_THROW(format_error(
"cannot format duration"));
422 template <
typename To,
typename FromRep,
typename FromPeriod,
423 FMTQUILL_ENABLE_IF(std::is_integral<FromRep>::value&&
424 std::is_integral<typename To::rep>::value)>
425 auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
426 #if !FMTQUILL_SAFE_DURATION_CAST 427 return std::chrono::duration_cast<To>(from);
430 using factor = std::ratio_divide<FromPeriod, typename To::period>;
432 using common_rep =
typename std::common_type<FromRep,
typename To::rep,
433 decltype(factor::num)>::type;
434 common_rep count = from.count();
437 if (const_check(factor::num != 1)) {
438 if (count > max_value<common_rep>() / factor::num) throw_duration_error();
439 const auto min = (std::numeric_limits<common_rep>::min)() / factor::num;
440 if (const_check(!std::is_unsigned<common_rep>::value) && count < min)
441 throw_duration_error();
442 count *= factor::num;
444 if (const_check(factor::den != 1)) count /= factor::den;
447 To(safe_duration_cast::lossless_integral_conversion<typename To::rep>(
449 if (ec) throw_duration_error();
454 template <
typename To,
typename FromRep,
typename FromPeriod,
455 FMTQUILL_ENABLE_IF(std::is_floating_point<FromRep>::value&&
456 std::is_floating_point<typename To::rep>::value)>
457 auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
458 #if FMTQUILL_SAFE_DURATION_CAST 460 if (!isfinite(from.count()))
return static_cast<To
>(from.count());
464 To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
465 if (ec) throw_duration_error();
469 return std::chrono::duration_cast<To>(from);
473 template <
typename To,
typename FromRep,
typename FromPeriod,
475 !is_similar_arithmetic_type<FromRep, typename To::rep>::value)>
476 auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
478 return std::chrono::duration_cast<To>(from);
481 template <
typename Duration>
482 auto to_time_t(sys_time<Duration> time_point) -> std::time_t {
486 return detail::duration_cast<std::chrono::duration<std::time_t>>(
487 time_point.time_since_epoch())
493 FMTQUILL_BEGIN_EXPORT
500 inline auto gmtime(std::time_t time) -> std::tm {
505 inline dispatcher(std::time_t t) : time_(t) {}
507 inline auto run() ->
bool {
508 using namespace fmtquill::detail;
509 return handle(gmtime_r(&time_, &tm_));
512 inline auto handle(std::tm* tm) ->
bool {
return tm !=
nullptr; }
515 using namespace fmtquill::detail;
516 return fallback(gmtime_s(&tm_, &time_));
519 inline auto fallback(
int res) ->
bool {
return res == 0; }
521 #if !FMTQUILL_MSC_VERSION 523 std::tm* tm = std::gmtime(&time_);
525 return tm !=
nullptr;
529 auto gt = dispatcher(time);
531 if (!gt.run()) FMTQUILL_THROW(format_error(
"time_t value out of range"));
535 template <
typename Duration>
536 inline auto gmtime(sys_time<Duration> time_point) -> std::tm {
537 return gmtime(detail::to_time_t(time_point));
545 inline void write_digit2_separated(
char* buf,
unsigned a,
unsigned b,
546 unsigned c,
char sep) {
547 unsigned long long digits =
548 a | (b << 24) | (static_cast<unsigned long long>(c) << 48);
558 digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6;
560 digits = ((digits & 0x00f00000f00000f0) >> 4) |
561 ((digits & 0x000f00000f00000f) << 8);
562 auto usep =
static_cast<unsigned long long>(sep);
564 digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
566 constexpr
size_t len = 8;
567 if (const_check(is_big_endian())) {
569 std::memcpy(tmp, &digits, len);
570 std::reverse_copy(tmp, tmp + len, buf);
572 std::memcpy(buf, &digits, len);
576 template <
typename Period>
577 FMTQUILL_CONSTEXPR
inline auto get_units() ->
const char* {
578 if (std::is_same<Period, std::atto>::value)
return "as";
579 if (std::is_same<Period, std::femto>::value)
return "fs";
580 if (std::is_same<Period, std::pico>::value)
return "ps";
581 if (std::is_same<Period, std::nano>::value)
return "ns";
582 if (std::is_same<Period, std::micro>::value)
return "us";
583 if (std::is_same<Period, std::milli>::value)
return "ms";
584 if (std::is_same<Period, std::centi>::value)
return "cs";
585 if (std::is_same<Period, std::deci>::value)
return "ds";
586 if (std::is_same<Period, std::ratio<1>>::
value)
return "s";
587 if (std::is_same<Period, std::deca>::value)
return "das";
588 if (std::is_same<Period, std::hecto>::value)
return "hs";
589 if (std::is_same<Period, std::kilo>::value)
return "ks";
590 if (std::is_same<Period, std::mega>::value)
return "Ms";
591 if (std::is_same<Period, std::giga>::value)
return "Gs";
592 if (std::is_same<Period, std::tera>::value)
return "Ts";
593 if (std::is_same<Period, std::peta>::value)
return "Ps";
594 if (std::is_same<Period, std::exa>::value)
return "Es";
595 if (std::is_same<Period, std::ratio<60>>::
value)
return "min";
596 if (std::is_same<Period, std::ratio<3600>>::
value)
return "h";
597 if (std::is_same<Period, std::ratio<86400>>::
value)
return "d";
601 enum class numeric_system {
608 enum class pad_type {
617 template <
typename OutputIt>
618 auto write_padding(OutputIt out, pad_type pad,
int width) -> OutputIt {
619 if (pad == pad_type::none)
return out;
620 return detail::fill_n(out, width, pad == pad_type::space ?
' ' :
'0');
623 template <
typename OutputIt>
624 auto write_padding(OutputIt out, pad_type pad) -> OutputIt {
625 if (pad != pad_type::none) *out++ = pad == pad_type::space ?
' ' :
'0';
630 template <
typename Char,
typename Handler>
631 FMTQUILL_CONSTEXPR
auto parse_chrono_format(
const Char* begin,
const Char* end,
632 Handler&& handler) ->
const Char* {
633 if (begin == end || *begin ==
'}')
return begin;
634 if (*begin !=
'%') FMTQUILL_THROW(format_error(
"invalid format"));
637 pad_type pad = pad_type::zero;
644 if (begin != ptr) handler.on_text(begin, ptr);
646 if (ptr == end) FMTQUILL_THROW(format_error(
"invalid format"));
650 pad = pad_type::space;
654 pad = pad_type::none;
658 if (ptr == end) FMTQUILL_THROW(format_error(
"invalid format"));
661 case '%': handler.on_text(ptr - 1, ptr);
break;
663 const Char newline[] = {
'\n'};
664 handler.on_text(newline, newline + 1);
668 const Char tab[] = {
'\t'};
669 handler.on_text(tab, tab + 1);
673 case 'Y': handler.on_year(numeric_system::standard, pad);
break;
674 case 'y': handler.on_short_year(numeric_system::standard);
break;
675 case 'C': handler.on_century(numeric_system::standard);
break;
676 case 'G': handler.on_iso_week_based_year();
break;
677 case 'g': handler.on_iso_week_based_short_year();
break;
679 case 'a': handler.on_abbr_weekday();
break;
680 case 'A': handler.on_full_weekday();
break;
681 case 'w': handler.on_dec0_weekday(numeric_system::standard);
break;
682 case 'u': handler.on_dec1_weekday(numeric_system::standard);
break;
685 case 'h': handler.on_abbr_month();
break;
686 case 'B': handler.on_full_month();
break;
687 case 'm': handler.on_dec_month(numeric_system::standard, pad);
break;
690 handler.on_dec0_week_of_year(numeric_system::standard, pad);
693 handler.on_dec1_week_of_year(numeric_system::standard, pad);
695 case 'V': handler.on_iso_week_of_year(numeric_system::standard, pad);
break;
696 case 'j': handler.on_day_of_year(pad);
break;
697 case 'd': handler.on_day_of_month(numeric_system::standard, pad);
break;
699 handler.on_day_of_month(numeric_system::standard, pad_type::space);
702 case 'H': handler.on_24_hour(numeric_system::standard, pad);
break;
703 case 'I': handler.on_12_hour(numeric_system::standard, pad);
break;
704 case 'M': handler.on_minute(numeric_system::standard, pad);
break;
705 case 'S': handler.on_second(numeric_system::standard, pad);
break;
707 case 'c': handler.on_datetime(numeric_system::standard);
break;
708 case 'x': handler.on_loc_date(numeric_system::standard);
break;
709 case 'X': handler.on_loc_time(numeric_system::standard);
break;
710 case 'D': handler.on_us_date();
break;
711 case 'F': handler.on_iso_date();
break;
712 case 'r': handler.on_12_hour_time();
break;
713 case 'R': handler.on_24_hour_time();
break;
714 case 'T': handler.on_iso_time();
break;
715 case 'p': handler.on_am_pm();
break;
716 case 'Q': handler.on_duration_value();
break;
717 case 'q': handler.on_duration_unit();
break;
718 case 'z': handler.on_utc_offset(numeric_system::standard);
break;
719 case 'Z': handler.on_tz_name();
break;
722 if (ptr == end) FMTQUILL_THROW(format_error(
"invalid format"));
725 case 'Y': handler.on_year(numeric_system::alternative, pad);
break;
726 case 'y': handler.on_offset_year();
break;
727 case 'C': handler.on_century(numeric_system::alternative);
break;
728 case 'c': handler.on_datetime(numeric_system::alternative);
break;
729 case 'x': handler.on_loc_date(numeric_system::alternative);
break;
730 case 'X': handler.on_loc_time(numeric_system::alternative);
break;
731 case 'z': handler.on_utc_offset(numeric_system::alternative);
break;
732 default: FMTQUILL_THROW(format_error(
"invalid format"));
737 if (ptr == end) FMTQUILL_THROW(format_error(
"invalid format"));
740 case 'y': handler.on_short_year(numeric_system::alternative);
break;
741 case 'm': handler.on_dec_month(numeric_system::alternative, pad);
break;
743 handler.on_dec0_week_of_year(numeric_system::alternative, pad);
746 handler.on_dec1_week_of_year(numeric_system::alternative, pad);
749 handler.on_iso_week_of_year(numeric_system::alternative, pad);
752 handler.on_day_of_month(numeric_system::alternative, pad);
755 handler.on_day_of_month(numeric_system::alternative, pad_type::space);
757 case 'w': handler.on_dec0_weekday(numeric_system::alternative);
break;
758 case 'u': handler.on_dec1_weekday(numeric_system::alternative);
break;
759 case 'H': handler.on_24_hour(numeric_system::alternative, pad);
break;
760 case 'I': handler.on_12_hour(numeric_system::alternative, pad);
break;
761 case 'M': handler.on_minute(numeric_system::alternative, pad);
break;
762 case 'S': handler.on_second(numeric_system::alternative, pad);
break;
763 case 'z': handler.on_utc_offset(numeric_system::alternative);
break;
764 default: FMTQUILL_THROW(format_error(
"invalid format"));
767 default: FMTQUILL_THROW(format_error(
"invalid format"));
771 if (begin != ptr) handler.on_text(begin, ptr);
776 FMTQUILL_CONSTEXPR
void unsupported() {
777 static_cast<Derived*
>(
this)->unsupported();
779 FMTQUILL_CONSTEXPR
void on_year(numeric_system, pad_type) { unsupported(); }
780 FMTQUILL_CONSTEXPR
void on_short_year(numeric_system) { unsupported(); }
781 FMTQUILL_CONSTEXPR
void on_offset_year() { unsupported(); }
782 FMTQUILL_CONSTEXPR
void on_century(numeric_system) { unsupported(); }
783 FMTQUILL_CONSTEXPR
void on_iso_week_based_year() { unsupported(); }
784 FMTQUILL_CONSTEXPR
void on_iso_week_based_short_year() { unsupported(); }
785 FMTQUILL_CONSTEXPR
void on_abbr_weekday() { unsupported(); }
786 FMTQUILL_CONSTEXPR
void on_full_weekday() { unsupported(); }
787 FMTQUILL_CONSTEXPR
void on_dec0_weekday(numeric_system) { unsupported(); }
788 FMTQUILL_CONSTEXPR
void on_dec1_weekday(numeric_system) { unsupported(); }
789 FMTQUILL_CONSTEXPR
void on_abbr_month() { unsupported(); }
790 FMTQUILL_CONSTEXPR
void on_full_month() { unsupported(); }
791 FMTQUILL_CONSTEXPR
void on_dec_month(numeric_system, pad_type) { unsupported(); }
792 FMTQUILL_CONSTEXPR
void on_dec0_week_of_year(numeric_system, pad_type) {
795 FMTQUILL_CONSTEXPR
void on_dec1_week_of_year(numeric_system, pad_type) {
798 FMTQUILL_CONSTEXPR
void on_iso_week_of_year(numeric_system, pad_type) {
801 FMTQUILL_CONSTEXPR
void on_day_of_year(pad_type) { unsupported(); }
802 FMTQUILL_CONSTEXPR
void on_day_of_month(numeric_system, pad_type) {
805 FMTQUILL_CONSTEXPR
void on_24_hour(numeric_system) { unsupported(); }
806 FMTQUILL_CONSTEXPR
void on_12_hour(numeric_system) { unsupported(); }
807 FMTQUILL_CONSTEXPR
void on_minute(numeric_system) { unsupported(); }
808 FMTQUILL_CONSTEXPR
void on_second(numeric_system) { unsupported(); }
809 FMTQUILL_CONSTEXPR
void on_datetime(numeric_system) { unsupported(); }
810 FMTQUILL_CONSTEXPR
void on_loc_date(numeric_system) { unsupported(); }
811 FMTQUILL_CONSTEXPR
void on_loc_time(numeric_system) { unsupported(); }
812 FMTQUILL_CONSTEXPR
void on_us_date() { unsupported(); }
813 FMTQUILL_CONSTEXPR
void on_iso_date() { unsupported(); }
814 FMTQUILL_CONSTEXPR
void on_12_hour_time() { unsupported(); }
815 FMTQUILL_CONSTEXPR
void on_24_hour_time() { unsupported(); }
816 FMTQUILL_CONSTEXPR
void on_iso_time() { unsupported(); }
817 FMTQUILL_CONSTEXPR
void on_am_pm() { unsupported(); }
818 FMTQUILL_CONSTEXPR
void on_duration_value() { unsupported(); }
819 FMTQUILL_CONSTEXPR
void on_duration_unit() { unsupported(); }
820 FMTQUILL_CONSTEXPR
void on_utc_offset(numeric_system) { unsupported(); }
821 FMTQUILL_CONSTEXPR
void on_tz_name() { unsupported(); }
826 bool has_timezone_ =
false;
830 : has_timezone_(has_timezone) {}
832 FMTQUILL_NORETURN
inline void unsupported() {
833 FMTQUILL_THROW(format_error(
"no format"));
836 template <
typename Char>
837 FMTQUILL_CONSTEXPR
void on_text(
const Char*,
const Char*) {}
838 FMTQUILL_CONSTEXPR
void on_year(numeric_system, pad_type) {}
839 FMTQUILL_CONSTEXPR
void on_short_year(numeric_system) {}
840 FMTQUILL_CONSTEXPR
void on_offset_year() {}
841 FMTQUILL_CONSTEXPR
void on_century(numeric_system) {}
842 FMTQUILL_CONSTEXPR
void on_iso_week_based_year() {}
843 FMTQUILL_CONSTEXPR
void on_iso_week_based_short_year() {}
844 FMTQUILL_CONSTEXPR
void on_abbr_weekday() {}
845 FMTQUILL_CONSTEXPR
void on_full_weekday() {}
846 FMTQUILL_CONSTEXPR
void on_dec0_weekday(numeric_system) {}
847 FMTQUILL_CONSTEXPR
void on_dec1_weekday(numeric_system) {}
848 FMTQUILL_CONSTEXPR
void on_abbr_month() {}
849 FMTQUILL_CONSTEXPR
void on_full_month() {}
850 FMTQUILL_CONSTEXPR
void on_dec_month(numeric_system, pad_type) {}
851 FMTQUILL_CONSTEXPR
void on_dec0_week_of_year(numeric_system, pad_type) {}
852 FMTQUILL_CONSTEXPR
void on_dec1_week_of_year(numeric_system, pad_type) {}
853 FMTQUILL_CONSTEXPR
void on_iso_week_of_year(numeric_system, pad_type) {}
854 FMTQUILL_CONSTEXPR
void on_day_of_year(pad_type) {}
855 FMTQUILL_CONSTEXPR
void on_day_of_month(numeric_system, pad_type) {}
856 FMTQUILL_CONSTEXPR
void on_24_hour(numeric_system, pad_type) {}
857 FMTQUILL_CONSTEXPR
void on_12_hour(numeric_system, pad_type) {}
858 FMTQUILL_CONSTEXPR
void on_minute(numeric_system, pad_type) {}
859 FMTQUILL_CONSTEXPR
void on_second(numeric_system, pad_type) {}
860 FMTQUILL_CONSTEXPR
void on_datetime(numeric_system) {}
861 FMTQUILL_CONSTEXPR
void on_loc_date(numeric_system) {}
862 FMTQUILL_CONSTEXPR
void on_loc_time(numeric_system) {}
863 FMTQUILL_CONSTEXPR
void on_us_date() {}
864 FMTQUILL_CONSTEXPR
void on_iso_date() {}
865 FMTQUILL_CONSTEXPR
void on_12_hour_time() {}
866 FMTQUILL_CONSTEXPR
void on_24_hour_time() {}
867 FMTQUILL_CONSTEXPR
void on_iso_time() {}
868 FMTQUILL_CONSTEXPR
void on_am_pm() {}
869 FMTQUILL_CONSTEXPR
void on_utc_offset(numeric_system) {
870 if (!has_timezone_) FMTQUILL_THROW(format_error(
"no timezone"));
872 FMTQUILL_CONSTEXPR
void on_tz_name() {
873 if (!has_timezone_) FMTQUILL_THROW(format_error(
"no timezone"));
877 inline auto tm_wday_full_name(
int wday) ->
const char* {
878 static constexpr
const char* full_name_list[] = {
879 "Sunday",
"Monday",
"Tuesday",
"Wednesday",
880 "Thursday",
"Friday",
"Saturday"};
881 return wday >= 0 && wday <= 6 ? full_name_list[wday] :
"?";
883 inline auto tm_wday_short_name(
int wday) ->
const char* {
884 static constexpr
const char* short_name_list[] = {
"Sun",
"Mon",
"Tue",
"Wed",
885 "Thu",
"Fri",
"Sat"};
886 return wday >= 0 && wday <= 6 ? short_name_list[wday] :
"???";
889 inline auto tm_mon_full_name(
int mon) ->
const char* {
890 static constexpr
const char* full_name_list[] = {
891 "January",
"February",
"March",
"April",
"May",
"June",
892 "July",
"August",
"September",
"October",
"November",
"December"};
893 return mon >= 0 && mon <= 11 ? full_name_list[mon] :
"?";
895 inline auto tm_mon_short_name(
int mon) ->
const char* {
896 static constexpr
const char* short_name_list[] = {
897 "Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
898 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
900 return mon >= 0 && mon <= 11 ? short_name_list[mon] :
"???";
903 template <
typename T,
typename =
void>
905 template <
typename T>
906 struct has_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>> : std::true_type {};
908 template <
typename T,
typename =
void>
struct has_tm_zone : std::false_type {};
909 template <
typename T>
910 struct has_tm_zone<T, void_t<decltype(T::tm_zone)>> : std::true_type {};
912 template <typename T, FMTQUILL_ENABLE_IF(has_tm_zone<T>::value)>
913 auto set_tm_zone(T& time,
char* tz) ->
bool {
917 template <typename T, FMTQUILL_ENABLE_IF(!has_tm_zone<T>::value)>
918 auto set_tm_zone(T&,
char*) ->
bool {
922 inline auto utc() ->
char* {
923 static char tz[] =
"UTC";
928 template <typename T, typename Int, FMTQUILL_ENABLE_IF(std::is_integral<T>::value)>
929 inline auto to_nonnegative_int(T
value, Int upper) -> Int {
930 if (!std::is_unsigned<Int>::value &&
931 (value < 0 || to_unsigned(value) > to_unsigned(upper))) {
932 FMTQUILL_THROW(format_error(
"chrono value is out of range"));
934 return static_cast<Int
>(value);
936 template <typename T, typename Int, FMTQUILL_ENABLE_IF(!std::is_integral<T>::value)>
937 inline auto to_nonnegative_int(T value, Int upper) -> Int {
938 auto int_value =
static_cast<Int
>(value);
939 if (int_value < 0 || value > static_cast<T>(upper))
940 FMTQUILL_THROW(format_error(
"invalid value"));
944 constexpr
auto pow10(std::uint32_t n) ->
long long {
945 return n == 0 ? 1 : 10 * pow10(n - 1);
951 template <
long long Num,
long long Den,
int N = 0,
952 bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
954 static constexpr
int value =
960 template <
long long Num,
long long Den,
int N>
962 static constexpr
int value = (Num % Den == 0) ? N : 6;
967 template <
typename Char,
typename OutputIt,
typename Duration>
968 void write_fractional_seconds(OutputIt& out, Duration d,
int precision = -1) {
969 constexpr
auto num_fractional_digits =
973 using subsecond_precision = std::chrono::duration<
974 typename std::common_type<
typename Duration::rep,
975 std::chrono::seconds::rep>::type,
976 std::ratio<1, pow10(num_fractional_digits)>>;
978 const auto fractional = d - detail::duration_cast<std::chrono::seconds>(d);
979 const auto subseconds =
980 std::chrono::treat_as_floating_point<
981 typename subsecond_precision::rep>::value
983 : detail::duration_cast<subsecond_precision>(fractional).count();
984 auto n =
static_cast<uint32_or_64_or_128_t<long long>
>(subseconds);
985 const int num_digits = count_digits(n);
987 int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits);
989 FMTQUILL_ASSERT(!std::is_floating_point<typename Duration::rep>::value,
"");
990 if (std::ratio_less<
typename subsecond_precision::period,
991 std::chrono::seconds::period>::value) {
993 out = detail::fill_n(out, leading_zeroes,
'0');
994 out = format_decimal<Char>(out, n, num_digits);
996 }
else if (precision > 0) {
998 leading_zeroes = min_of(leading_zeroes, precision);
999 int remaining = precision - leading_zeroes;
1000 out = detail::fill_n(out, leading_zeroes,
'0');
1001 if (remaining < num_digits) {
1002 int num_truncated_digits = num_digits - remaining;
1003 n /= to_unsigned(pow10(to_unsigned(num_truncated_digits)));
1004 if (n != 0) out = format_decimal<Char>(out, n, remaining);
1008 out = format_decimal<Char>(out, n, num_digits);
1009 remaining -= num_digits;
1011 out = detail::fill_n(out, remaining,
'0');
1018 template <
typename Duration>
1019 void write_floating_seconds(
memory_buffer& buf, Duration duration,
1020 int num_fractional_digits = -1) {
1021 using rep =
typename Duration::rep;
1022 FMTQUILL_ASSERT(std::is_floating_point<rep>::value,
"");
1024 auto val = duration.count();
1026 if (num_fractional_digits < 0) {
1029 using namespace std;
1030 num_fractional_digits =
1032 Duration::period::den>
::value;
1033 if (num_fractional_digits < 6 && static_cast<rep>(round(val)) != val)
1034 num_fractional_digits = 6;
1037 fmtquill::format_to(std::back_inserter(buf), FMTQUILL_STRING(
"{:.{}f}"),
1038 std::fmod(val * static_cast<rep>(Duration::period::num) /
1039 static_cast<rep>(Duration::period::den),
1040 static_cast<rep>(60)),
1041 num_fractional_digits);
1044 template <
typename OutputIt,
typename Char,
1045 typename Duration = std::chrono::seconds>
1048 static constexpr
int days_per_week = 7;
1050 const std::locale& loc_;
1053 const Duration* subsecs_;
1056 auto tm_sec()
const noexcept ->
int {
1057 FMTQUILL_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61,
"");
1060 auto tm_min()
const noexcept ->
int {
1061 FMTQUILL_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59,
"");
1064 auto tm_hour()
const noexcept ->
int {
1065 FMTQUILL_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23,
"");
1068 auto tm_mday()
const noexcept ->
int {
1069 FMTQUILL_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31,
"");
1072 auto tm_mon()
const noexcept ->
int {
1073 FMTQUILL_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11,
"");
1076 auto tm_year()
const noexcept ->
long long {
return 1900ll + tm_.tm_year; }
1077 auto tm_wday()
const noexcept ->
int {
1078 FMTQUILL_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6,
"");
1081 auto tm_yday()
const noexcept ->
int {
1082 FMTQUILL_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365,
"");
1086 auto tm_hour12()
const noexcept ->
int {
1088 auto z = h < 12 ? h : h - 12;
1089 return z == 0 ? 12 : z;
1096 auto split_year_lower(
long long year)
const noexcept ->
int {
1097 auto l = year % 100;
1099 return static_cast<int>(l);
1103 auto iso_year_weeks(
long long curr_year)
const noexcept ->
int {
1104 auto prev_year = curr_year - 1;
1106 (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
1109 (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
1111 return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
1113 auto iso_week_num(
int tm_yday,
int tm_wday)
const noexcept ->
int {
1114 return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) /
1117 auto tm_iso_week_year()
const noexcept ->
long long {
1118 auto year = tm_year();
1119 auto w = iso_week_num(tm_yday(), tm_wday());
1120 if (w < 1)
return year - 1;
1121 if (w > iso_year_weeks(year))
return year + 1;
1124 auto tm_iso_week_of_year()
const noexcept ->
int {
1125 auto year = tm_year();
1126 auto w = iso_week_num(tm_yday(), tm_wday());
1127 if (w < 1)
return iso_year_weeks(year - 1);
1128 if (w > iso_year_weeks(year))
return 1;
1132 void write1(
int value) {
1133 *out_++ =
static_cast<char>(
'0' + to_unsigned(value) % 10);
1135 void write2(
int value) {
1136 const char* d = digits2(to_unsigned(value) % 100);
1140 void write2(
int value, pad_type pad) {
1141 unsigned int v = to_unsigned(value) % 100;
1143 const char* d = digits2(v);
1147 out_ = detail::write_padding(out_, pad);
1148 *out_++ =
static_cast<char>(
'0' + v);
1152 void write_year_extended(
long long year, pad_type pad) {
1155 bool negative = year < 0;
1160 uint32_or_64_or_128_t<long long> n = to_unsigned(year);
1161 const int num_digits = count_digits(n);
1162 if (negative && pad == pad_type::zero) *out_++ =
'-';
1163 if (width > num_digits)
1164 out_ = detail::write_padding(out_, pad, width - num_digits);
1165 if (negative && pad != pad_type::zero) *out_++ =
'-';
1166 out_ = format_decimal<Char>(out_, n, num_digits);
1168 void write_year(
long long year, pad_type pad) {
1169 write_year_extended(year, pad);
1172 void write_utc_offset(
long long offset, numeric_system ns) {
1180 write2(static_cast<int>(offset / 60));
1181 if (ns != numeric_system::standard) *out_++ =
':';
1182 write2(static_cast<int>(offset % 60));
1185 template <typename T, FMTQUILL_ENABLE_IF(has_tm_gmtoff<T>::value)>
1186 void format_utc_offset(
const T& tm, numeric_system ns) {
1187 write_utc_offset(tm.tm_gmtoff, ns);
1189 template <typename T, FMTQUILL_ENABLE_IF(!has_tm_gmtoff<T>::value)>
1190 void format_utc_offset(
const T&, numeric_system ns) {
1191 write_utc_offset(0, ns);
1194 template <typename T, FMTQUILL_ENABLE_IF(has_tm_zone<T>::value)>
1195 void format_tz_name(
const T& tm) {
1196 out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
1198 template <typename T, FMTQUILL_ENABLE_IF(!has_tm_zone<T>::value)>
1199 void format_tz_name(
const T&) {
1200 out_ = std::copy_n(utc(), 3, out_);
1203 void format_localized(
char format,
char modifier = 0) {
1204 out_ = write<Char>(out_, tm_, loc_, format, modifier);
1208 tm_writer(
const std::locale& loc, OutputIt out,
const std::tm& tm,
1209 const Duration* subsecs =
nullptr)
1211 is_classic_(loc_ == get_classic_locale()),
1216 auto out()
const -> OutputIt {
return out_; }
1218 FMTQUILL_CONSTEXPR
void on_text(
const Char* begin,
const Char* end) {
1219 out_ = copy<Char>(begin, end, out_);
1222 void on_abbr_weekday() {
1224 out_ = write(out_, tm_wday_short_name(tm_wday()));
1226 format_localized(
'a');
1228 void on_full_weekday() {
1230 out_ = write(out_, tm_wday_full_name(tm_wday()));
1232 format_localized(
'A');
1234 void on_dec0_weekday(numeric_system ns) {
1235 if (is_classic_ || ns == numeric_system::standard)
return write1(tm_wday());
1236 format_localized(
'w',
'O');
1238 void on_dec1_weekday(numeric_system ns) {
1239 if (is_classic_ || ns == numeric_system::standard) {
1240 auto wday = tm_wday();
1241 write1(wday == 0 ? days_per_week : wday);
1243 format_localized(
'u',
'O');
1247 void on_abbr_month() {
1249 out_ = write(out_, tm_mon_short_name(tm_mon()));
1251 format_localized(
'b');
1253 void on_full_month() {
1255 out_ = write(out_, tm_mon_full_name(tm_mon()));
1257 format_localized(
'B');
1260 void on_datetime(numeric_system ns) {
1266 on_day_of_month(numeric_system::standard, pad_type::space);
1270 on_year(numeric_system::standard, pad_type::space);
1272 format_localized(
'c', ns == numeric_system::standard ?
'\0' :
'E');
1275 void on_loc_date(numeric_system ns) {
1279 format_localized(
'x', ns == numeric_system::standard ?
'\0' :
'E');
1281 void on_loc_time(numeric_system ns) {
1285 format_localized(
'X', ns == numeric_system::standard ?
'\0' :
'E');
1289 write_digit2_separated(buf, to_unsigned(tm_mon() + 1),
1290 to_unsigned(tm_mday()),
1291 to_unsigned(split_year_lower(tm_year())),
'/');
1292 out_ = copy<Char>(std::begin(buf), std::end(buf), out_);
1294 void on_iso_date() {
1295 auto year = tm_year();
1298 if (year >= 0 && year < 10000) {
1299 write2digits(buf, static_cast<size_t>(year / 100));
1302 write_year_extended(year, pad_type::zero);
1305 write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100),
1306 to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
1308 out_ = copy<Char>(std::begin(buf) + offset, std::end(buf), out_);
1311 void on_utc_offset(numeric_system ns) { format_utc_offset(tm_, ns); }
1312 void on_tz_name() { format_tz_name(tm_); }
1314 void on_year(numeric_system ns, pad_type pad) {
1315 if (is_classic_ || ns == numeric_system::standard)
1316 return write_year(tm_year(), pad);
1317 format_localized(
'Y',
'E');
1319 void on_short_year(numeric_system ns) {
1320 if (is_classic_ || ns == numeric_system::standard)
1321 return write2(split_year_lower(tm_year()));
1322 format_localized(
'y',
'O');
1324 void on_offset_year() {
1325 if (is_classic_)
return write2(split_year_lower(tm_year()));
1326 format_localized(
'y',
'E');
1329 void on_century(numeric_system ns) {
1330 if (is_classic_ || ns == numeric_system::standard) {
1331 auto year = tm_year();
1332 auto upper = year / 100;
1333 if (year >= -99 && year < 0) {
1337 }
else if (upper >= 0 && upper < 100) {
1338 write2(static_cast<int>(upper));
1340 out_ = write<Char>(out_, upper);
1343 format_localized(
'C',
'E');
1347 void on_dec_month(numeric_system ns, pad_type pad) {
1348 if (is_classic_ || ns == numeric_system::standard)
1349 return write2(tm_mon() + 1, pad);
1350 format_localized(
'm',
'O');
1353 void on_dec0_week_of_year(numeric_system ns, pad_type pad) {
1354 if (is_classic_ || ns == numeric_system::standard)
1355 return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week,
1357 format_localized(
'U',
'O');
1359 void on_dec1_week_of_year(numeric_system ns, pad_type pad) {
1360 if (is_classic_ || ns == numeric_system::standard) {
1361 auto wday = tm_wday();
1362 write2((tm_yday() + days_per_week -
1363 (wday == 0 ? (days_per_week - 1) : (wday - 1))) /
1367 format_localized(
'W',
'O');
1370 void on_iso_week_of_year(numeric_system ns, pad_type pad) {
1371 if (is_classic_ || ns == numeric_system::standard)
1372 return write2(tm_iso_week_of_year(), pad);
1373 format_localized(
'V',
'O');
1376 void on_iso_week_based_year() {
1377 write_year(tm_iso_week_year(), pad_type::zero);
1379 void on_iso_week_based_short_year() {
1380 write2(split_year_lower(tm_iso_week_year()));
1383 void on_day_of_year(pad_type pad) {
1384 auto yday = tm_yday() + 1;
1385 auto digit1 = yday / 100;
1389 out_ = detail::write_padding(out_, pad);
1390 write2(yday % 100, pad);
1393 void on_day_of_month(numeric_system ns, pad_type pad) {
1394 if (is_classic_ || ns == numeric_system::standard)
1395 return write2(tm_mday(), pad);
1396 format_localized(
'd',
'O');
1399 void on_24_hour(numeric_system ns, pad_type pad) {
1400 if (is_classic_ || ns == numeric_system::standard)
1401 return write2(tm_hour(), pad);
1402 format_localized(
'H',
'O');
1404 void on_12_hour(numeric_system ns, pad_type pad) {
1405 if (is_classic_ || ns == numeric_system::standard)
1406 return write2(tm_hour12(), pad);
1407 format_localized(
'I',
'O');
1409 void on_minute(numeric_system ns, pad_type pad) {
1410 if (is_classic_ || ns == numeric_system::standard)
1411 return write2(tm_min(), pad);
1412 format_localized(
'M',
'O');
1415 void on_second(numeric_system ns, pad_type pad) {
1416 if (is_classic_ || ns == numeric_system::standard) {
1417 write2(tm_sec(), pad);
1419 if (std::is_floating_point<typename Duration::rep>::value) {
1421 write_floating_seconds(buf, *subsecs_);
1422 if (buf.
size() > 1) {
1424 out_ = copy<Char>(buf.begin() + 1, buf.end(), out_);
1427 write_fractional_seconds<Char>(out_, *subsecs_);
1432 format_localized(
'S',
'O');
1436 void on_12_hour_time() {
1439 write_digit2_separated(buf, to_unsigned(tm_hour12()),
1440 to_unsigned(tm_min()), to_unsigned(tm_sec()),
':');
1441 out_ = copy<Char>(std::begin(buf), std::end(buf), out_);
1445 format_localized(
'r');
1448 void on_24_hour_time() {
1453 void on_iso_time() {
1456 on_second(numeric_system::standard, pad_type::zero);
1461 *out_++ = tm_hour() < 12 ?
'A' :
'P';
1464 format_localized(
'p');
1469 void on_duration_value() {}
1470 void on_duration_unit() {}
1474 bool has_precision_integral =
false;
1476 FMTQUILL_NORETURN
inline void unsupported() { FMTQUILL_THROW(format_error(
"no date")); }
1478 template <
typename Char>
1479 FMTQUILL_CONSTEXPR
void on_text(
const Char*,
const Char*) {}
1480 FMTQUILL_CONSTEXPR
void on_day_of_year(pad_type) {}
1481 FMTQUILL_CONSTEXPR
void on_24_hour(numeric_system, pad_type) {}
1482 FMTQUILL_CONSTEXPR
void on_12_hour(numeric_system, pad_type) {}
1483 FMTQUILL_CONSTEXPR
void on_minute(numeric_system, pad_type) {}
1484 FMTQUILL_CONSTEXPR
void on_second(numeric_system, pad_type) {}
1485 FMTQUILL_CONSTEXPR
void on_12_hour_time() {}
1486 FMTQUILL_CONSTEXPR
void on_24_hour_time() {}
1487 FMTQUILL_CONSTEXPR
void on_iso_time() {}
1488 FMTQUILL_CONSTEXPR
void on_am_pm() {}
1489 FMTQUILL_CONSTEXPR
void on_duration_value()
const {
1490 if (has_precision_integral)
1491 FMTQUILL_THROW(format_error(
"precision not allowed for this argument type"));
1493 FMTQUILL_CONSTEXPR
void on_duration_unit() {}
1496 template <
typename T,
1498 inline auto isfinite(T) ->
bool {
1502 template <typename T, FMTQUILL_ENABLE_IF(std::is_integral<T>::value)>
1503 inline auto mod(T x,
int y) -> T {
1504 return x %
static_cast<T
>(y);
1506 template <typename T, FMTQUILL_ENABLE_IF(std::is_floating_point<T>::value)>
1507 inline auto mod(T x,
int y) -> T {
1508 return std::fmod(x, static_cast<T>(y));
1513 template <typename T, bool INTEGRAL = std::is_integral<T>::value>
1519 using type =
typename std::make_unsigned<T>::type;
1522 template <
typename Rep,
typename Period,
1523 FMTQUILL_ENABLE_IF(std::is_integral<Rep>::value)>
1524 inline auto get_milliseconds(std::chrono::duration<Rep, Period> d)
1525 -> std::chrono::duration<Rep, std::milli> {
1527 #if FMTQUILL_SAFE_DURATION_CAST 1528 using common_seconds_type =
1529 typename std::common_type<decltype(d), std::chrono::seconds>::type;
1530 auto d_as_common = detail::duration_cast<common_seconds_type>(d);
1531 auto d_as_whole_seconds =
1532 detail::duration_cast<std::chrono::seconds>(d_as_common);
1534 auto diff = d_as_common - d_as_whole_seconds;
1535 auto ms = detail::duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
1538 auto s = detail::duration_cast<std::chrono::seconds>(d);
1539 return detail::duration_cast<std::chrono::milliseconds>(d - s);
1543 template <
typename Char,
typename Rep,
typename OutputIt,
1544 FMTQUILL_ENABLE_IF(std::is_integral<Rep>::value)>
1545 auto format_duration_value(OutputIt out, Rep val,
int) -> OutputIt {
1546 return write<Char>(out, val);
1549 template <
typename Char,
typename Rep,
typename OutputIt,
1550 FMTQUILL_ENABLE_IF(std::is_floating_point<Rep>::value)>
1551 auto format_duration_value(OutputIt out, Rep val,
int precision) -> OutputIt {
1553 specs.precision = precision;
1554 specs.set_type(precision >= 0 ? presentation_type::fixed
1555 : presentation_type::general);
1556 return write<Char>(out, val, specs);
1559 template <
typename Char,
typename OutputIt>
1560 auto copy_unit(
string_view unit, OutputIt out, Char) -> OutputIt {
1561 return copy<Char>(unit.begin(), unit.end(), out);
1564 template <
typename OutputIt>
1565 auto copy_unit(
string_view unit, OutputIt out,
wchar_t) -> OutputIt {
1569 return copy<wchar_t>(u.c_str(), u.c_str() + u.size(), out);
1572 template <
typename Char,
typename Period,
typename OutputIt>
1573 auto format_duration_unit(OutputIt out) -> OutputIt {
1574 if (
const char* unit = get_units<Period>())
1577 out = write<Char>(out, Period::num);
1578 if (const_check(Period::den != 1)) {
1580 out = write<Char>(out, Period::den);
1590 std::locale locale_;
1592 bool has_locale_ =
false;
1596 if (!localized)
return;
1598 ::new (&locale_) std::locale(
1599 #
if FMTQUILL_USE_LOCALE
1600 loc.template get<std::locale>()
1605 if (has_locale_) locale_.~locale();
1607 inline operator const std::locale&()
const {
1608 return has_locale_ ? locale_ : get_classic_locale();
1612 template <
typename Char,
typename Rep,
typename Period>
1618 conditional_t<std::is_integral<Rep>::value &&
sizeof(Rep) <
sizeof(
int),
1619 unsigned,
typename make_unsigned_or_unchanged<Rep>::type>;
1623 bool localized =
false;
1624 using seconds = std::chrono::duration<rep>;
1626 using milliseconds = std::chrono::duration<rep, std::milli>;
1633 : out(o), val(static_cast<rep>(d.count())), locale(loc), negative(
false) {
1634 if (d.count() < 0) {
1642 s = detail::duration_cast<seconds>(std::chrono::duration<rep, Period>(val));
1646 auto handle_nan_inf() ->
bool {
1647 if (isfinite(val))
return false;
1654 std::copy_n(
"inf", 3, out);
1656 std::copy_n(
"-inf", 4, out);
1660 auto days()
const -> Rep {
return static_cast<Rep
>(s.count() / 86400); }
1661 auto hour()
const -> Rep {
1662 return static_cast<Rep
>(mod((s.count() / 3600), 24));
1665 auto hour12()
const -> Rep {
1666 Rep hour =
static_cast<Rep
>(mod((s.count() / 3600), 12));
1667 return hour <= 0 ? 12 : hour;
1670 auto minute()
const -> Rep {
1671 return static_cast<Rep
>(mod((s.count() / 60), 60));
1673 auto second()
const -> Rep {
return static_cast<Rep
>(mod(s.count(), 60)); }
1675 auto time()
const -> std::tm {
1676 auto time = std::tm();
1677 time.tm_hour = to_nonnegative_int(hour(), 24);
1678 time.tm_min = to_nonnegative_int(minute(), 60);
1679 time.tm_sec = to_nonnegative_int(second(), 60);
1684 if (!negative)
return;
1689 void write(Rep value,
int width, pad_type pad = pad_type::zero) {
1691 if (isnan(value))
return write_nan();
1692 uint32_or_64_or_128_t<int> n =
1693 to_unsigned(to_nonnegative_int(value, max_value<int>()));
1694 int num_digits = detail::count_digits(n);
1695 if (width > num_digits) {
1696 out = detail::write_padding(out, pad, width - num_digits);
1698 out = format_decimal<Char>(out, n, num_digits);
1701 void write_nan() { std::copy_n(
"nan", 3, out); }
1703 template <
typename Callback,
typename... Args>
1704 void format_tm(
const tm& time, Callback cb, Args... args) {
1705 if (isnan(val))
return write_nan();
1712 void on_text(
const Char* begin,
const Char* end) {
1713 copy<Char>(begin, end, out);
1717 void on_abbr_weekday() {}
1718 void on_full_weekday() {}
1719 void on_dec0_weekday(numeric_system) {}
1720 void on_dec1_weekday(numeric_system) {}
1721 void on_abbr_month() {}
1722 void on_full_month() {}
1723 void on_datetime(numeric_system) {}
1724 void on_loc_date(numeric_system) {}
1725 void on_loc_time(numeric_system) {}
1726 void on_us_date() {}
1727 void on_iso_date() {}
1728 void on_utc_offset(numeric_system) {}
1729 void on_tz_name() {}
1730 void on_year(numeric_system, pad_type) {}
1731 void on_short_year(numeric_system) {}
1732 void on_offset_year() {}
1733 void on_century(numeric_system) {}
1734 void on_iso_week_based_year() {}
1735 void on_iso_week_based_short_year() {}
1736 void on_dec_month(numeric_system, pad_type) {}
1737 void on_dec0_week_of_year(numeric_system, pad_type) {}
1738 void on_dec1_week_of_year(numeric_system, pad_type) {}
1739 void on_iso_week_of_year(numeric_system, pad_type) {}
1740 void on_day_of_month(numeric_system, pad_type) {}
1742 void on_day_of_year(pad_type) {
1743 if (handle_nan_inf())
return;
1747 void on_24_hour(numeric_system ns, pad_type pad) {
1748 if (handle_nan_inf())
return;
1750 if (ns == numeric_system::standard)
return write(hour(), 2, pad);
1752 time.tm_hour = to_nonnegative_int(hour(), 24);
1753 format_tm(time, &tm_writer_type::on_24_hour, ns, pad);
1756 void on_12_hour(numeric_system ns, pad_type pad) {
1757 if (handle_nan_inf())
return;
1759 if (ns == numeric_system::standard)
return write(hour12(), 2, pad);
1761 time.tm_hour = to_nonnegative_int(hour12(), 12);
1762 format_tm(time, &tm_writer_type::on_12_hour, ns, pad);
1765 void on_minute(numeric_system ns, pad_type pad) {
1766 if (handle_nan_inf())
return;
1768 if (ns == numeric_system::standard)
return write(minute(), 2, pad);
1770 time.tm_min = to_nonnegative_int(minute(), 60);
1771 format_tm(time, &tm_writer_type::on_minute, ns, pad);
1774 void on_second(numeric_system ns, pad_type pad) {
1775 if (handle_nan_inf())
return;
1777 if (ns == numeric_system::standard) {
1778 if (std::is_floating_point<rep>::value) {
1780 write_floating_seconds(buf, std::chrono::duration<rep, Period>(val),
1782 if (negative) *out++ =
'-';
1783 if (buf.
size() < 2 || buf[1] ==
'.')
1784 out = detail::write_padding(out, pad);
1785 out = copy<Char>(buf.begin(), buf.end(), out);
1787 write(second(), 2, pad);
1788 write_fractional_seconds<Char>(
1789 out, std::chrono::duration<rep, Period>(val), precision);
1794 time.tm_sec = to_nonnegative_int(second(), 60);
1795 format_tm(time, &tm_writer_type::on_second, ns, pad);
1798 void on_12_hour_time() {
1799 if (handle_nan_inf())
return;
1800 format_tm(time(), &tm_writer_type::on_12_hour_time);
1803 void on_24_hour_time() {
1804 if (handle_nan_inf()) {
1815 void on_iso_time() {
1818 if (handle_nan_inf())
return;
1819 on_second(numeric_system::standard, pad_type::zero);
1823 if (handle_nan_inf())
return;
1824 format_tm(time(), &tm_writer_type::on_am_pm);
1827 void on_duration_value() {
1828 if (handle_nan_inf())
return;
1830 out = format_duration_value<Char>(out, val, precision);
1833 void on_duration_unit() { out = format_duration_unit<Char, Period>(out); }
1838 #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 1839 using weekday = std::chrono::weekday;
1840 using day = std::chrono::day;
1841 using month = std::chrono::month;
1842 using year = std::chrono::year;
1848 unsigned char value_;
1852 constexpr
explicit weekday(
unsigned wd) noexcept
1853 : value_(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
1854 constexpr
auto c_encoding()
const noexcept ->
unsigned {
return value_; }
1859 unsigned char value_;
1863 constexpr
explicit day(
unsigned d) noexcept
1864 : value_(static_cast<unsigned char>(d)) {}
1865 constexpr
explicit operator unsigned()
const noexcept {
return value_; }
1870 unsigned char value_;
1874 constexpr
explicit month(
unsigned m) noexcept
1875 : value_(static_cast<unsigned char>(m)) {}
1876 constexpr
explicit operator unsigned()
const noexcept {
return value_; }
1885 constexpr
explicit year(
int y) noexcept : value_(y) {}
1886 constexpr
explicit operator int()
const noexcept {
return value_; }
1891 fmtquill::year year_;
1892 fmtquill::month month_;
1898 : year_(y), month_(m), day_(d) {}
1899 constexpr
auto year()
const noexcept -> fmtquill::year {
return year_; }
1900 constexpr
auto month()
const noexcept -> fmtquill::month {
return month_; }
1901 constexpr
auto day()
const noexcept -> fmtquill::day {
return day_; }
1903 #endif // __cpp_lib_chrono >= 201907 1905 template <
typename Char>
1906 struct formatter<
weekday, Char> :
private formatter<std::tm, Char> {
1908 bool use_tm_formatter_ =
false;
1912 auto it = ctx.
begin(), end = ctx.
end();
1913 if (it != end && *it ==
'L') {
1915 this->set_localized();
1917 use_tm_formatter_ = it != end && *it !=
'}';
1918 return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
1921 template <
typename FormatContext>
1922 auto format(
weekday wd, FormatContext& ctx)
const -> decltype(ctx.out()) {
1923 auto time = std::tm();
1924 time.tm_wday =
static_cast<int>(wd.c_encoding());
1925 if (use_tm_formatter_)
return formatter<std::tm, Char>::format(time, ctx);
1928 w.on_abbr_weekday();
1933 template <
typename Char>
1934 struct formatter<
day, Char> :
private formatter<std::tm, Char> {
1936 bool use_tm_formatter_ =
false;
1940 auto it = ctx.
begin(), end = ctx.
end();
1941 use_tm_formatter_ = it != end && *it !=
'}';
1942 return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
1945 template <
typename FormatContext>
1946 auto format(
day d, FormatContext& ctx)
const -> decltype(ctx.out()) {
1947 auto time = std::tm();
1948 time.tm_mday =
static_cast<int>(
static_cast<unsigned>(d));
1949 if (use_tm_formatter_)
return formatter<std::tm, Char>::format(time, ctx);
1952 w.on_day_of_month(detail::numeric_system::standard, detail::pad_type::zero);
1957 template <
typename Char>
1958 struct formatter<
month, Char> :
private formatter<std::tm, Char> {
1960 bool use_tm_formatter_ =
false;
1964 auto it = ctx.
begin(), end = ctx.
end();
1965 if (it != end && *it ==
'L') {
1967 this->set_localized();
1969 use_tm_formatter_ = it != end && *it !=
'}';
1970 return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
1973 template <
typename FormatContext>
1974 auto format(
month m, FormatContext& ctx)
const -> decltype(ctx.out()) {
1975 auto time = std::tm();
1976 time.tm_mon =
static_cast<int>(
static_cast<unsigned>(m)) - 1;
1977 if (use_tm_formatter_)
return formatter<std::tm, Char>::format(time, ctx);
1985 template <
typename Char>
1986 struct formatter<
year, Char> :
private formatter<std::tm, Char> {
1988 bool use_tm_formatter_ =
false;
1992 auto it = ctx.
begin(), end = ctx.
end();
1993 use_tm_formatter_ = it != end && *it !=
'}';
1994 return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
1997 template <
typename FormatContext>
1998 auto format(
year y, FormatContext& ctx)
const -> decltype(ctx.out()) {
1999 auto time = std::tm();
2000 time.tm_year =
static_cast<int>(y) - 1900;
2001 if (use_tm_formatter_)
return formatter<std::tm, Char>::format(time, ctx);
2004 w.on_year(detail::numeric_system::standard, detail::pad_type::zero);
2009 template <
typename Char>
2012 bool use_tm_formatter_ =
false;
2016 auto it = ctx.
begin(), end = ctx.
end();
2017 use_tm_formatter_ = it != end && *it !=
'}';
2018 return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
2021 template <
typename FormatContext>
2023 -> decltype(ctx.out()) {
2024 auto time = std::tm();
2025 time.tm_year =
static_cast<int>(val.year()) - 1900;
2026 time.tm_mon =
static_cast<int>(
static_cast<unsigned>(val.month())) - 1;
2027 time.tm_mday =
static_cast<int>(
static_cast<unsigned>(val.day()));
2028 if (use_tm_formatter_)
return formatter<std::tm, Char>::format(time, ctx);
2036 template <
typename Rep,
typename Period,
typename Char>
2037 struct formatter<
std::chrono::duration<Rep, Period>, Char> {
2046 auto it = ctx.
begin(), end = ctx.
end();
2047 if (it == end || *it ==
'}')
return it;
2049 it = detail::parse_align(it, end, specs_);
2050 if (it == end)
return it;
2053 if ((c >=
'0' && c <=
'9') || c ==
'{') {
2054 it = detail::parse_width(it, end, specs_, width_ref_, ctx);
2055 if (it == end)
return it;
2060 checker.has_precision_integral = !std::is_floating_point<Rep>::value;
2061 it = detail::parse_precision(it, end, specs_, precision_ref_, ctx);
2063 if (it != end && *it ==
'L') {
2064 specs_.set_localized();
2067 end = detail::parse_chrono_format(it, end, checker);
2068 fmt_ = {it, detail::to_unsigned(end - it)};
2072 template <
typename FormatContext>
2073 auto format(std::chrono::duration<Rep, Period> d, FormatContext& ctx)
const 2074 -> decltype(ctx.out()) {
2075 auto specs = specs_;
2076 auto precision = specs.precision;
2077 specs.precision = -1;
2078 auto begin = fmt_.begin(), end = fmt_.end();
2083 detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
2085 detail::handle_dynamic_spec(specs.dynamic_precision(), precision,
2086 precision_ref_, ctx);
2087 if (begin == end || *begin ==
'}') {
2088 out = detail::format_duration_value<Char>(out, d.count(), precision);
2089 detail::format_duration_unit<Char, Period>(out);
2093 f.precision = precision;
2094 f.localized = specs_.localized();
2095 detail::parse_chrono_format(begin, end, f);
2097 return detail::write(
2102 template <
typename Char>
struct formatter<
std::tm, Char> {
2110 auto localized()
const ->
bool {
return specs_.localized(); }
2111 FMTQUILL_CONSTEXPR
void set_localized() { specs_.set_localized(); }
2115 auto it = ctx.
begin(), end = ctx.
end();
2116 if (it == end || *it ==
'}')
return it;
2118 it = detail::parse_align(it, end, specs_);
2119 if (it == end)
return it;
2122 if ((c >=
'0' && c <=
'9') || c ==
'{') {
2123 it = detail::parse_width(it, end, specs_, width_ref_, ctx);
2124 if (it == end)
return it;
2128 specs_.set_localized();
2132 end = detail::parse_chrono_format(it, end,
2135 if (end != it) fmt_ = {it, detail::to_unsigned(end - it)};
2139 template <
typename Duration,
typename FormatContext>
2140 auto do_format(
const std::tm& tm, FormatContext& ctx,
2141 const Duration* subsecs)
const -> decltype(ctx.out()) {
2142 auto specs = specs_;
2145 detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
2148 auto loc_ref = specs.localized() ? ctx.locale() :
locale_ref();
2151 loc, out, tm, subsecs);
2152 detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w);
2153 return detail::write(
2162 template <
typename FormatContext>
2163 auto format(
const std::tm& tm, FormatContext& ctx)
const 2164 -> decltype(ctx.out()) {
2165 return do_format<std::chrono::seconds>(tm, ctx,
nullptr);
2170 template <
typename Char,
typename Duration>
2171 struct formatter<sys_time<Duration>, Char> :
private formatter<std::tm, Char> {
2173 return this->do_parse(ctx,
true);
2176 template <
typename FormatContext>
2177 auto format(sys_time<Duration> val, FormatContext& ctx)
const 2178 -> decltype(ctx.out()) {
2179 std::tm tm = gmtime(val);
2180 using period =
typename Duration::period;
2181 if (detail::const_check(
2182 period::num == 1 && period::den == 1 &&
2183 !std::is_floating_point<typename Duration::rep>::value)) {
2184 detail::set_tm_zone(tm, detail::utc());
2185 return formatter<std::tm, Char>::format(tm, ctx);
2187 Duration epoch = val.time_since_epoch();
2188 Duration subsecs = detail::duration_cast<Duration>(
2189 epoch - detail::duration_cast<std::chrono::seconds>(epoch));
2190 if (subsecs.count() < 0) {
2191 auto second = detail::duration_cast<Duration>(std::chrono::seconds(1));
2192 if (tm.tm_sec != 0) {
2195 tm = gmtime(val - second);
2196 detail::set_tm_zone(tm, detail::utc());
2200 return formatter<std::tm, Char>::do_format(tm, ctx, &subsecs);
2204 template <
typename Duration,
typename Char>
2205 struct formatter<utc_time<Duration>, Char>
2206 : formatter<sys_time<Duration>, Char> {
2207 template <
typename FormatContext>
2208 auto format(utc_time<Duration> val, FormatContext& ctx)
const 2209 -> decltype(ctx.out()) {
2210 return formatter<sys_time<Duration>, Char>::format(
2211 detail::utc_clock::to_sys(val), ctx);
2215 template <
typename Duration,
typename Char>
2216 struct formatter<local_time<Duration>, Char>
2217 :
private formatter<std::tm, Char> {
2219 return this->do_parse(ctx,
false);
2222 template <
typename FormatContext>
2223 auto format(local_time<Duration> val, FormatContext& ctx)
const 2224 -> decltype(ctx.out()) {
2225 auto time_since_epoch = val.time_since_epoch();
2226 auto seconds_since_epoch =
2227 detail::duration_cast<std::chrono::seconds>(time_since_epoch);
2230 std::tm t = gmtime(seconds_since_epoch.count());
2231 using period =
typename Duration::period;
2232 if (period::num == 1 && period::den == 1 &&
2233 !std::is_floating_point<typename Duration::rep>::value) {
2234 return formatter<std::tm, Char>::format(t, ctx);
2237 detail::duration_cast<Duration>(time_since_epoch - seconds_since_epoch);
2238 return formatter<std::tm, Char>::do_format(t, ctx, &subsecs);
2243 FMTQUILL_END_NAMESPACE
2245 #endif // FMTQUILL_CHRONO_H_ Definition: format.h:2633
A dynamically growing memory buffer for trivially copyable/constructible types with the first SIZE el...
Definition: format.h:809
FMTQUILL_CONSTEXPR auto data() noexcept -> T *
Returns a pointer to the buffer data (not null-terminated).
Definition: base.h:1825
Definition: chrono.h:1846
Definition: chrono.h:1868
Parsing context consisting of a format string range being parsed and an argument counter for automati...
Definition: base.h:634
Definition: UserDefinedDirectFormatFuzzer.cpp:81
Definition: chrono.h:1889
FMTQUILL_CONSTEXPR20 void append(const U *begin, const U *end)
Appends data to the end of the buffer.
Definition: base.h:1859
constexpr auto data() const noexcept -> const Char *
Returns a pointer to the string data.
Definition: base.h:568
Definition: doctest.h:530
constexpr auto size() const noexcept -> size_t
Returns the string size.
Definition: base.h:571
Definition: chrono.h:1879
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:883
constexpr auto size() const noexcept -> size_t
Returns the size of this buffer.
Definition: base.h:1819
Definition: chrono.h:1514
Definition: chrono.h:1857
Definition: format.h:1329
Definition: chrono.h:1587
constexpr auto begin() const noexcept -> iterator
Returns an iterator to the beginning of the format string range being parsed.
Definition: base.h:880
Definition: format.h:1312
Definition: chrono.h:1046