@@ -42,7 +42,7 @@ inline std::tm localtime(std::time_t time) {
4242 return handle (localtime_r (&time_, &tm_));
4343 }
4444
45- bool handle (std::tm* tm) { return tm != FMT_NULL ; }
45+ bool handle (std::tm* tm) { return tm != nullptr ; }
4646
4747 bool handle (internal::null<>) {
4848 using namespace fmt ::internal;
@@ -56,7 +56,7 @@ inline std::tm localtime(std::time_t time) {
5656 using namespace fmt ::internal;
5757 std::tm* tm = std::localtime (&time_);
5858 if (tm) tm_ = *tm;
59- return tm != FMT_NULL ;
59+ return tm != nullptr ;
6060 }
6161#endif
6262 };
@@ -79,7 +79,7 @@ inline std::tm gmtime(std::time_t time) {
7979 return handle (gmtime_r (&time_, &tm_));
8080 }
8181
82- bool handle (std::tm* tm) { return tm != FMT_NULL ; }
82+ bool handle (std::tm* tm) { return tm != nullptr ; }
8383
8484 bool handle (internal::null<>) {
8585 using namespace fmt ::internal;
@@ -92,7 +92,7 @@ inline std::tm gmtime(std::time_t time) {
9292 bool fallback (internal::null<>) {
9393 std::tm* tm = std::gmtime (&time_);
9494 if (tm) tm_ = *tm;
95- return tm != FMT_NULL ;
95+ return tm != nullptr ;
9696 }
9797#endif
9898 };
@@ -157,7 +157,7 @@ template <typename Char> struct formatter<std::tm, Char> {
157157
158158namespace internal {
159159template <typename Period> FMT_CONSTEXPR const char * get_units () {
160- return FMT_NULL ;
160+ return nullptr ;
161161}
162162template <> FMT_CONSTEXPR const char * get_units<std::atto>() { return " as" ; }
163163template <> FMT_CONSTEXPR const char * get_units<std::femto>() { return " fs" ; }
@@ -348,59 +348,107 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
348348}
349349
350350struct chrono_format_checker {
351- void report_no_date () { FMT_THROW (format_error (" no date" )); }
351+ FMT_NORETURN void report_no_date () { FMT_THROW (format_error (" no date" )); }
352352
353353 template <typename Char> void on_text (const Char*, const Char*) {}
354- void on_abbr_weekday () { report_no_date (); }
355- void on_full_weekday () { report_no_date (); }
356- void on_dec0_weekday (numeric_system) { report_no_date (); }
357- void on_dec1_weekday (numeric_system) { report_no_date (); }
358- void on_abbr_month () { report_no_date (); }
359- void on_full_month () { report_no_date (); }
354+ FMT_NORETURN void on_abbr_weekday () { report_no_date (); }
355+ FMT_NORETURN void on_full_weekday () { report_no_date (); }
356+ FMT_NORETURN void on_dec0_weekday (numeric_system) { report_no_date (); }
357+ FMT_NORETURN void on_dec1_weekday (numeric_system) { report_no_date (); }
358+ FMT_NORETURN void on_abbr_month () { report_no_date (); }
359+ FMT_NORETURN void on_full_month () { report_no_date (); }
360360 void on_24_hour (numeric_system) {}
361361 void on_12_hour (numeric_system) {}
362362 void on_minute (numeric_system) {}
363363 void on_second (numeric_system) {}
364- void on_datetime (numeric_system) { report_no_date (); }
365- void on_loc_date (numeric_system) { report_no_date (); }
366- void on_loc_time (numeric_system) { report_no_date (); }
367- void on_us_date () { report_no_date (); }
368- void on_iso_date () { report_no_date (); }
364+ FMT_NORETURN void on_datetime (numeric_system) { report_no_date (); }
365+ FMT_NORETURN void on_loc_date (numeric_system) { report_no_date (); }
366+ FMT_NORETURN void on_loc_time (numeric_system) { report_no_date (); }
367+ FMT_NORETURN void on_us_date () { report_no_date (); }
368+ FMT_NORETURN void on_iso_date () { report_no_date (); }
369369 void on_12_hour_time () {}
370370 void on_24_hour_time () {}
371371 void on_iso_time () {}
372372 void on_am_pm () {}
373373 void on_duration_value () {}
374374 void on_duration_unit () {}
375- void on_utc_offset () { report_no_date (); }
376- void on_tz_name () { report_no_date (); }
375+ FMT_NORETURN void on_utc_offset () { report_no_date (); }
376+ FMT_NORETURN void on_tz_name () { report_no_date (); }
377377};
378378
379- template <typename Int> inline int to_int (Int value) {
380- FMT_ASSERT (value >= (std::numeric_limits<int >::min)() &&
381- value <= (std::numeric_limits<int >::max)(),
382- " invalid value" );
379+ template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
380+ inline bool isnan (T) {
381+ return false ;
382+ }
383+ template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
384+ inline bool isnan (T value) {
385+ return std::isnan (value);
386+ }
387+
388+ // Convers value to int and checks that it's in the range [0, upper).
389+ template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
390+ inline int to_nonnegative_int (T value, int upper) {
391+ FMT_ASSERT (value >= 0 && value <= upper, " invalid value" );
392+ (void )upper;
393+ return static_cast <int >(value);
394+ }
395+ template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
396+ inline int to_nonnegative_int (T value, int upper) {
397+ FMT_ASSERT (
398+ std::isnan (value) || (value >= 0 && value <= static_cast <T>(upper)),
399+ " invalid value" );
400+ (void )upper;
383401 return static_cast <int >(value);
384402}
385403
404+ template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
405+ inline T mod (T x, int y) {
406+ return x % y;
407+ }
408+ template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
409+ inline T mod (T x, int y) {
410+ return std::fmod (x, y);
411+ }
412+
413+ // If T is an integral type, maps T to its unsigned counterpart, otherwise
414+ // leaves it unchanged (unlike std::make_unsigned).
415+ template <typename T, bool INTEGRAL = std::is_integral<T>::value>
416+ struct make_unsigned_or_unchanged {
417+ using type = T;
418+ };
419+
420+ template <typename T> struct make_unsigned_or_unchanged <T, true > {
421+ using type = typename std::make_unsigned<T>::type;
422+ };
423+
424+ template <typename Rep, typename Period,
425+ FMT_ENABLE_IF (std::is_integral<Rep>::value)>
426+ inline std::chrono::duration<Rep, std::milli> get_milliseconds (
427+ std::chrono::duration<Rep, Period> d) {
428+ auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
429+ return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
430+ }
431+
432+ template <typename Rep, typename Period,
433+ FMT_ENABLE_IF (std::is_floating_point<Rep>::value)>
434+ inline std::chrono::duration<Rep, std::milli> get_milliseconds (
435+ std::chrono::duration<Rep, Period> d) {
436+ auto ms = mod (d.count () * Period::num / Period::den * 1000 , 1000 );
437+ return std::chrono::duration<Rep, std::milli>(static_cast <Rep>(ms));
438+ }
439+
386440template <typename Rep, typename OutputIt>
387- OutputIt static format_chrono_duration_value (OutputIt out, Rep val,
388- int precision) {
389- if (precision < 0 ) {
390- return format_to (out, std::is_floating_point<Rep>::value ? " {:g}" : " {}" ,
391- val);
392- }
393- return format_to (out, " {:.{}f}" , val, precision);
441+ OutputIt format_chrono_duration_value (OutputIt out, Rep val, int precision) {
442+ if (precision >= 0 ) return format_to (out, " {:.{}f}" , val, precision);
443+ return format_to (out, std::is_floating_point<Rep>::value ? " {:g}" : " {}" ,
444+ val);
394445}
395446
396447template <typename Period, typename OutputIt>
397448static OutputIt format_chrono_duration_unit (OutputIt out) {
398- if (const char * unit = get_units<Period>())
399- return format_to (out, " {}" , unit);
400- else if (Period::den == 1 )
401- return format_to (out, " [{}]s" , Period::num);
402- else
403- return format_to (out, " [{}/{}]s" , Period::num, Period::den);
449+ if (const char * unit = get_units<Period>()) return format_to (out, " {}" , unit);
450+ if (Period::den == 1 ) return format_to (out, " [{}]s" , Period::num);
451+ return format_to (out, " [{}/{}]s" , Period::num, Period::den);
404452}
405453
406454template <typename FormatContext, typename OutputIt, typename Rep,
@@ -409,48 +457,69 @@ struct chrono_formatter {
409457 FormatContext& context;
410458 OutputIt out;
411459 int precision;
412- Rep val;
413- typedef std::chrono::duration<Rep, std::milli> milliseconds;
414- std::chrono::seconds s;
415- milliseconds ms;
460+ // rep is unsigned to avoid overflow.
461+ using rep =
462+ conditional_t <std::is_integral<Rep>::value && sizeof (Rep) < sizeof (int ),
463+ unsigned , typename make_unsigned_or_unchanged<Rep>::type>;
464+ rep val;
465+ typedef std::chrono::duration<rep> seconds;
466+ seconds s;
467+ typedef std::chrono::duration<rep, std::milli> milliseconds;
468+ bool negative;
416469
417470 typedef typename FormatContext::char_type char_type;
418471
419472 explicit chrono_formatter (FormatContext& ctx, OutputIt o,
420473 std::chrono::duration<Rep, Period> d)
421- : context(ctx),
422- out(o),
423- val(d.count()),
424- s(std::chrono::duration_cast<std::chrono::seconds>(d)),
425- ms(std::chrono::duration_cast<milliseconds>(d - s)) {}
474+ : context(ctx), out(o), val(d.count()), negative(false ) {
475+ if (d.count () < 0 ) {
476+ val = -val;
477+ negative = true ;
478+ }
479+ s = std::chrono::duration_cast<seconds>(
480+ std::chrono::duration<rep, Period>(val));
481+ }
426482
427- int hour () const { return to_int (( s.count () / 3600 ) % 24 ); }
483+ Rep hour () const { return static_cast <Rep>( mod (( s.count () / 3600 ), 24 ) ); }
428484
429- int hour12 () const {
430- auto hour = to_int (( s.count () / 3600 ) % 12 );
431- return hour > 0 ? hour : 12 ;
485+ Rep hour12 () const {
486+ Rep hour = static_cast <Rep>( mod (( s.count () / 3600 ), 12 ) );
487+ return hour <= 0 ? 12 : hour ;
432488 }
433489
434- int minute () const { return to_int (( s.count () / 60 ) % 60 ); }
435- int second () const { return to_int ( s.count () % 60 ); }
490+ Rep minute () const { return static_cast <Rep>( mod (( s.count () / 60 ), 60 ) ); }
491+ Rep second () const { return static_cast <Rep>( mod ( s.count (), 60 ) ); }
436492
437493 std::tm time () const {
438494 auto time = std::tm ();
439- time.tm_hour = hour ();
440- time.tm_min = minute ();
441- time.tm_sec = second ();
495+ time.tm_hour = to_nonnegative_int ( hour (), 24 );
496+ time.tm_min = to_nonnegative_int ( minute (), 60 );
497+ time.tm_sec = to_nonnegative_int ( second (), 60 );
442498 return time;
443499 }
444500
445- void write (int value, int width) {
501+ void write_sign () {
502+ if (negative) {
503+ *out++ = ' -' ;
504+ negative = false ;
505+ }
506+ }
507+
508+ void write (Rep value, int width) {
509+ write_sign ();
510+ if (isnan (value)) return write_nan ();
446511 typedef typename int_traits<int >::main_type main_type;
447- main_type n = to_unsigned (value);
512+ main_type n = to_unsigned (
513+ to_nonnegative_int (value, (std::numeric_limits<int >::max)()));
448514 int num_digits = internal::count_digits (n);
449515 if (width > num_digits) out = std::fill_n (out, width - num_digits, ' 0' );
450516 out = format_decimal<char_type>(out, n, num_digits);
451517 }
452518
519+ void write_nan () { std::copy_n (" nan" , 3 , out); }
520+
453521 void format_localized (const tm& time, const char * format) {
522+ if (isnan (val)) return write_nan ();
454523 auto locale = context.locale ().template get <std::locale>();
455524 auto & facet = std::use_facet<std::time_put<char_type>>(locale);
456525 std::basic_ostringstream<char_type> os;
@@ -482,35 +551,36 @@ struct chrono_formatter {
482551 void on_24_hour (numeric_system ns) {
483552 if (ns == numeric_system::standard) return write (hour (), 2 );
484553 auto time = tm ();
485- time.tm_hour = hour ();
554+ time.tm_hour = to_nonnegative_int ( hour (), 24 );
486555 format_localized (time, " %OH" );
487556 }
488557
489558 void on_12_hour (numeric_system ns) {
490559 if (ns == numeric_system::standard) return write (hour12 (), 2 );
491560 auto time = tm ();
492- time.tm_hour = hour ( );
561+ time.tm_hour = to_nonnegative_int ( hour12 (), 12 );
493562 format_localized (time, " %OI" );
494563 }
495564
496565 void on_minute (numeric_system ns) {
497566 if (ns == numeric_system::standard) return write (minute (), 2 );
498567 auto time = tm ();
499- time.tm_min = minute ();
568+ time.tm_min = to_nonnegative_int ( minute (), 60 );
500569 format_localized (time, " %OM" );
501570 }
502571
503572 void on_second (numeric_system ns) {
504573 if (ns == numeric_system::standard) {
505574 write (second (), 2 );
575+ auto ms = get_milliseconds (std::chrono::duration<Rep, Period>(val));
506576 if (ms != std::chrono::milliseconds (0 )) {
507577 *out++ = ' .' ;
508- write (to_int ( ms.count () ), 3 );
578+ write (ms.count (), 3 );
509579 }
510580 return ;
511581 }
512582 auto time = tm ();
513- time.tm_sec = second ();
583+ time.tm_sec = to_nonnegative_int ( second (), 60 );
514584 format_localized (time, " %OS" );
515585 }
516586
@@ -531,6 +601,7 @@ struct chrono_formatter {
531601 void on_am_pm () { format_localized (time (), " %p" ); }
532602
533603 void on_duration_value () {
604+ write_sign ();
534605 out = format_chrono_duration_value (out, val, precision);
535606 }
536607
@@ -585,28 +656,39 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
585656 }
586657 };
587658
588- public:
589- formatter () : spec(), precision(-1 ) {}
659+ typedef typename basic_parse_context<Char>::iterator iterator;
660+ struct parse_range {
661+ iterator begin;
662+ iterator end;
663+ };
590664
591- FMT_CONSTEXPR auto parse (basic_parse_context<Char>& ctx)
592- -> decltype(ctx.begin()) {
665+ FMT_CONSTEXPR parse_range do_parse (basic_parse_context<Char>& ctx) {
593666 auto begin = ctx.begin (), end = ctx.end ();
594- if (begin == end) return begin;
667+ if (begin == end || *begin == ' } ' ) return { begin, begin} ;
595668 spec_handler handler{*this , ctx, format_str};
596669 begin = internal::parse_align (begin, end, handler);
597- if (begin == end) return begin;
670+ if (begin == end) return { begin, begin} ;
598671 begin = internal::parse_width (begin, end, handler);
599- if (begin == end) return begin;
672+ if (begin == end) return { begin, begin} ;
600673 if (*begin == ' .' ) {
601674 if (std::is_floating_point<Rep>::value)
602675 begin = internal::parse_precision (begin, end, handler);
603676 else
604677 handler.on_error (" precision not allowed for this argument type" );
605678 }
606679 end = parse_chrono_format (begin, end, internal::chrono_format_checker ());
607- format_str =
608- basic_string_view<Char>(&*begin, internal::to_unsigned (end - begin));
609- return end;
680+ return {begin, end};
681+ }
682+
683+ public:
684+ formatter () : spec(), precision(-1 ) {}
685+
686+ FMT_CONSTEXPR auto parse (basic_parse_context<Char>& ctx)
687+ -> decltype(ctx.begin()) {
688+ auto range = do_parse (ctx);
689+ format_str = basic_string_view<Char>(
690+ &*range.begin , internal::to_unsigned (range.end - range.begin ));
691+ return range.end ;
610692 }
611693
612694 template <typename FormatContext>
@@ -616,7 +698,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
616698 // is not specified.
617699 basic_memory_buffer<Char> buf;
618700 auto out = std::back_inserter (buf);
619- typedef output_range<decltype (ctx.out ()), Char> range ;
701+ using range = internal:: output_range<decltype (ctx.out ()), Char>;
620702 basic_writer<range> w (range (ctx.out ()));
621703 internal::handle_dynamic_spec<internal::width_checker>(
622704 spec.width_ , width_ref, ctx, format_str.begin ());
0 commit comments