@@ -42,7 +42,7 @@ inline std::tm localtime(std::time_t time) {
42
42
return handle (localtime_r (&time_, &tm_));
43
43
}
44
44
45
- bool handle (std::tm* tm) { return tm != FMT_NULL ; }
45
+ bool handle (std::tm* tm) { return tm != nullptr ; }
46
46
47
47
bool handle (internal::null<>) {
48
48
using namespace fmt ::internal;
@@ -56,7 +56,7 @@ inline std::tm localtime(std::time_t time) {
56
56
using namespace fmt ::internal;
57
57
std::tm* tm = std::localtime (&time_);
58
58
if (tm) tm_ = *tm;
59
- return tm != FMT_NULL ;
59
+ return tm != nullptr ;
60
60
}
61
61
#endif
62
62
};
@@ -79,7 +79,7 @@ inline std::tm gmtime(std::time_t time) {
79
79
return handle (gmtime_r (&time_, &tm_));
80
80
}
81
81
82
- bool handle (std::tm* tm) { return tm != FMT_NULL ; }
82
+ bool handle (std::tm* tm) { return tm != nullptr ; }
83
83
84
84
bool handle (internal::null<>) {
85
85
using namespace fmt ::internal;
@@ -92,7 +92,7 @@ inline std::tm gmtime(std::time_t time) {
92
92
bool fallback (internal::null<>) {
93
93
std::tm* tm = std::gmtime (&time_);
94
94
if (tm) tm_ = *tm;
95
- return tm != FMT_NULL ;
95
+ return tm != nullptr ;
96
96
}
97
97
#endif
98
98
};
@@ -157,7 +157,7 @@ template <typename Char> struct formatter<std::tm, Char> {
157
157
158
158
namespace internal {
159
159
template <typename Period> FMT_CONSTEXPR const char * get_units () {
160
- return FMT_NULL ;
160
+ return nullptr ;
161
161
}
162
162
template <> FMT_CONSTEXPR const char * get_units<std::atto>() { return " as" ; }
163
163
template <> FMT_CONSTEXPR const char * get_units<std::femto>() { return " fs" ; }
@@ -348,59 +348,107 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
348
348
}
349
349
350
350
struct 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" )); }
352
352
353
353
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 (); }
360
360
void on_24_hour (numeric_system) {}
361
361
void on_12_hour (numeric_system) {}
362
362
void on_minute (numeric_system) {}
363
363
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 (); }
369
369
void on_12_hour_time () {}
370
370
void on_24_hour_time () {}
371
371
void on_iso_time () {}
372
372
void on_am_pm () {}
373
373
void on_duration_value () {}
374
374
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 (); }
377
377
};
378
378
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;
383
401
return static_cast <int >(value);
384
402
}
385
403
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
+
386
440
template <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);
394
445
}
395
446
396
447
template <typename Period, typename OutputIt>
397
448
static 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);
404
452
}
405
453
406
454
template <typename FormatContext, typename OutputIt, typename Rep,
@@ -409,48 +457,69 @@ struct chrono_formatter {
409
457
FormatContext& context;
410
458
OutputIt out;
411
459
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;
416
469
417
470
typedef typename FormatContext::char_type char_type;
418
471
419
472
explicit chrono_formatter (FormatContext& ctx, OutputIt o,
420
473
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
+ }
426
482
427
- int hour () const { return to_int (( s.count () / 3600 ) % 24 ); }
483
+ Rep hour () const { return static_cast <Rep>( mod (( s.count () / 3600 ), 24 ) ); }
428
484
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 ;
432
488
}
433
489
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 ) ); }
436
492
437
493
std::tm time () const {
438
494
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 );
442
498
return time;
443
499
}
444
500
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 ();
446
511
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)()));
448
514
int num_digits = internal::count_digits (n);
449
515
if (width > num_digits) out = std::fill_n (out, width - num_digits, ' 0' );
450
516
out = format_decimal<char_type>(out, n, num_digits);
451
517
}
452
518
519
+ void write_nan () { std::copy_n (" nan" , 3 , out); }
520
+
453
521
void format_localized (const tm& time, const char * format) {
522
+ if (isnan (val)) return write_nan ();
454
523
auto locale = context.locale ().template get <std::locale>();
455
524
auto & facet = std::use_facet<std::time_put<char_type>>(locale);
456
525
std::basic_ostringstream<char_type> os;
@@ -482,35 +551,36 @@ struct chrono_formatter {
482
551
void on_24_hour (numeric_system ns) {
483
552
if (ns == numeric_system::standard) return write (hour (), 2 );
484
553
auto time = tm ();
485
- time.tm_hour = hour ();
554
+ time.tm_hour = to_nonnegative_int ( hour (), 24 );
486
555
format_localized (time, " %OH" );
487
556
}
488
557
489
558
void on_12_hour (numeric_system ns) {
490
559
if (ns == numeric_system::standard) return write (hour12 (), 2 );
491
560
auto time = tm ();
492
- time.tm_hour = hour ( );
561
+ time.tm_hour = to_nonnegative_int ( hour12 (), 12 );
493
562
format_localized (time, " %OI" );
494
563
}
495
564
496
565
void on_minute (numeric_system ns) {
497
566
if (ns == numeric_system::standard) return write (minute (), 2 );
498
567
auto time = tm ();
499
- time.tm_min = minute ();
568
+ time.tm_min = to_nonnegative_int ( minute (), 60 );
500
569
format_localized (time, " %OM" );
501
570
}
502
571
503
572
void on_second (numeric_system ns) {
504
573
if (ns == numeric_system::standard) {
505
574
write (second (), 2 );
575
+ auto ms = get_milliseconds (std::chrono::duration<Rep, Period>(val));
506
576
if (ms != std::chrono::milliseconds (0 )) {
507
577
*out++ = ' .' ;
508
- write (to_int ( ms.count () ), 3 );
578
+ write (ms.count (), 3 );
509
579
}
510
580
return ;
511
581
}
512
582
auto time = tm ();
513
- time.tm_sec = second ();
583
+ time.tm_sec = to_nonnegative_int ( second (), 60 );
514
584
format_localized (time, " %OS" );
515
585
}
516
586
@@ -531,6 +601,7 @@ struct chrono_formatter {
531
601
void on_am_pm () { format_localized (time (), " %p" ); }
532
602
533
603
void on_duration_value () {
604
+ write_sign ();
534
605
out = format_chrono_duration_value (out, val, precision);
535
606
}
536
607
@@ -585,28 +656,39 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
585
656
}
586
657
};
587
658
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
+ };
590
664
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) {
593
666
auto begin = ctx.begin (), end = ctx.end ();
594
- if (begin == end) return begin;
667
+ if (begin == end || *begin == ' } ' ) return { begin, begin} ;
595
668
spec_handler handler{*this , ctx, format_str};
596
669
begin = internal::parse_align (begin, end, handler);
597
- if (begin == end) return begin;
670
+ if (begin == end) return { begin, begin} ;
598
671
begin = internal::parse_width (begin, end, handler);
599
- if (begin == end) return begin;
672
+ if (begin == end) return { begin, begin} ;
600
673
if (*begin == ' .' ) {
601
674
if (std::is_floating_point<Rep>::value)
602
675
begin = internal::parse_precision (begin, end, handler);
603
676
else
604
677
handler.on_error (" precision not allowed for this argument type" );
605
678
}
606
679
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 ;
610
692
}
611
693
612
694
template <typename FormatContext>
@@ -616,7 +698,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
616
698
// is not specified.
617
699
basic_memory_buffer<Char> buf;
618
700
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>;
620
702
basic_writer<range> w (range (ctx.out ()));
621
703
internal::handle_dynamic_spec<internal::width_checker>(
622
704
spec.width_ , width_ref, ctx, format_str.begin ());
0 commit comments