Fix bug on '%Y' and '%C' formats with negative years
Requested changes
This commit is contained in:
parent
f88c020fc0
commit
aeb54b0dd9
@ -569,21 +569,21 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
|
|||||||
handler.on_text(tab, tab + 1);
|
handler.on_text(tab, tab + 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Year
|
// Year:
|
||||||
case 'Y':
|
case 'Y':
|
||||||
handler.on_year(numeric_system::standard);
|
handler.on_year(numeric_system::standard);
|
||||||
break;
|
break;
|
||||||
case 'y':
|
case 'y':
|
||||||
handler.on_last2_year(numeric_system::standard);
|
handler.on_short_year(numeric_system::standard);
|
||||||
break;
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
handler.on_base_year(numeric_system::standard);
|
handler.on_century(numeric_system::standard);
|
||||||
break;
|
break;
|
||||||
case 'G':
|
case 'G':
|
||||||
handler.on_iso_week_based_year();
|
handler.on_iso_week_based_year();
|
||||||
break;
|
break;
|
||||||
case 'g':
|
case 'g':
|
||||||
handler.on_iso_week_based_year_last2();
|
handler.on_iso_week_based_short_year();
|
||||||
break;
|
break;
|
||||||
// Day of the week:
|
// Day of the week:
|
||||||
case 'a':
|
case 'a':
|
||||||
@ -609,7 +609,7 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
|
|||||||
case 'm':
|
case 'm':
|
||||||
handler.on_dec_month(numeric_system::standard);
|
handler.on_dec_month(numeric_system::standard);
|
||||||
break;
|
break;
|
||||||
// Day of the year/month
|
// Day of the year/month:
|
||||||
case 'U':
|
case 'U':
|
||||||
handler.on_dec0_week_of_year(numeric_system::standard);
|
handler.on_dec0_week_of_year(numeric_system::standard);
|
||||||
break;
|
break;
|
||||||
@ -623,7 +623,7 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
|
|||||||
handler.on_day_of_year();
|
handler.on_day_of_year();
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
handler.on_day_of_month_zero(numeric_system::standard);
|
handler.on_day_of_month(numeric_system::standard);
|
||||||
break;
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
handler.on_day_of_month_space(numeric_system::standard);
|
handler.on_day_of_month_space(numeric_system::standard);
|
||||||
@ -693,7 +693,7 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
|
|||||||
handler.on_offset_year();
|
handler.on_offset_year();
|
||||||
break;
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
handler.on_base_year(numeric_system::alternative);
|
handler.on_century(numeric_system::alternative);
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
handler.on_datetime(numeric_system::alternative);
|
handler.on_datetime(numeric_system::alternative);
|
||||||
@ -714,7 +714,7 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
|
|||||||
c = *ptr++;
|
c = *ptr++;
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'y':
|
case 'y':
|
||||||
handler.on_last2_year(numeric_system::alternative);
|
handler.on_short_year(numeric_system::alternative);
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
handler.on_dec_month(numeric_system::alternative);
|
handler.on_dec_month(numeric_system::alternative);
|
||||||
@ -729,7 +729,7 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
|
|||||||
handler.on_iso_week_of_year(numeric_system::alternative);
|
handler.on_iso_week_of_year(numeric_system::alternative);
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
handler.on_day_of_month_zero(numeric_system::alternative);
|
handler.on_day_of_month(numeric_system::alternative);
|
||||||
break;
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
handler.on_day_of_month_space(numeric_system::alternative);
|
handler.on_day_of_month_space(numeric_system::alternative);
|
||||||
@ -770,11 +770,11 @@ template <typename Derived> struct null_chrono_spec_handler {
|
|||||||
static_cast<Derived*>(this)->unsupported();
|
static_cast<Derived*>(this)->unsupported();
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); }
|
FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); }
|
||||||
FMT_CONSTEXPR void on_last2_year(numeric_system) { unsupported(); }
|
FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); }
|
||||||
FMT_CONSTEXPR void on_offset_year() { unsupported(); }
|
FMT_CONSTEXPR void on_offset_year() { unsupported(); }
|
||||||
FMT_CONSTEXPR void on_base_year(numeric_system) { unsupported(); }
|
FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); }
|
||||||
FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); }
|
FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); }
|
||||||
FMT_CONSTEXPR void on_iso_week_based_year_last2() { unsupported(); }
|
FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); }
|
||||||
FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
|
FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
|
||||||
FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
|
FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
|
||||||
FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
|
FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
|
||||||
@ -786,7 +786,7 @@ template <typename Derived> struct null_chrono_spec_handler {
|
|||||||
FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); }
|
FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); }
|
||||||
FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); }
|
FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); }
|
||||||
FMT_CONSTEXPR void on_day_of_year() { unsupported(); }
|
FMT_CONSTEXPR void on_day_of_year() { unsupported(); }
|
||||||
FMT_CONSTEXPR void on_day_of_month_zero(numeric_system) { unsupported(); }
|
FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); }
|
||||||
FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); }
|
FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); }
|
||||||
FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
|
FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
|
||||||
FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
|
FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
|
||||||
@ -1089,17 +1089,17 @@ struct chrono_formatter {
|
|||||||
void on_utc_offset() {}
|
void on_utc_offset() {}
|
||||||
void on_tz_name() {}
|
void on_tz_name() {}
|
||||||
void on_year(numeric_system) {}
|
void on_year(numeric_system) {}
|
||||||
void on_last2_year(numeric_system) {}
|
void on_short_year(numeric_system) {}
|
||||||
void on_offset_year() {}
|
void on_offset_year() {}
|
||||||
void on_base_year(numeric_system) {}
|
void on_century(numeric_system) {}
|
||||||
void on_iso_week_based_year() {}
|
void on_iso_week_based_year() {}
|
||||||
void on_iso_week_based_year_last2() {}
|
void on_iso_week_based_short_year() {}
|
||||||
void on_dec_month(numeric_system) {}
|
void on_dec_month(numeric_system) {}
|
||||||
void on_dec0_week_of_year(numeric_system) {}
|
void on_dec0_week_of_year(numeric_system) {}
|
||||||
void on_dec1_week_of_year(numeric_system) {}
|
void on_dec1_week_of_year(numeric_system) {}
|
||||||
void on_iso_week_of_year(numeric_system) {}
|
void on_iso_week_of_year(numeric_system) {}
|
||||||
void on_day_of_year() {}
|
void on_day_of_year() {}
|
||||||
void on_day_of_month_zero(numeric_system) {}
|
void on_day_of_month(numeric_system) {}
|
||||||
void on_day_of_month_space(numeric_system) {}
|
void on_day_of_month_space(numeric_system) {}
|
||||||
|
|
||||||
void on_24_hour(numeric_system ns) {
|
void on_24_hour(numeric_system ns) {
|
||||||
@ -1364,11 +1364,11 @@ struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
|
|||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
|
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
|
||||||
FMT_CONSTEXPR void on_year(numeric_system) {}
|
FMT_CONSTEXPR void on_year(numeric_system) {}
|
||||||
FMT_CONSTEXPR void on_last2_year(numeric_system) {}
|
FMT_CONSTEXPR void on_short_year(numeric_system) {}
|
||||||
FMT_CONSTEXPR void on_offset_year() {}
|
FMT_CONSTEXPR void on_offset_year() {}
|
||||||
FMT_CONSTEXPR void on_base_year(numeric_system) {}
|
FMT_CONSTEXPR void on_century(numeric_system) {}
|
||||||
FMT_CONSTEXPR void on_iso_week_based_year() {}
|
FMT_CONSTEXPR void on_iso_week_based_year() {}
|
||||||
FMT_CONSTEXPR void on_iso_week_based_year_last2() {}
|
FMT_CONSTEXPR void on_iso_week_based_short_year() {}
|
||||||
FMT_CONSTEXPR void on_abbr_weekday() {}
|
FMT_CONSTEXPR void on_abbr_weekday() {}
|
||||||
FMT_CONSTEXPR void on_full_weekday() {}
|
FMT_CONSTEXPR void on_full_weekday() {}
|
||||||
FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {}
|
FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {}
|
||||||
@ -1380,7 +1380,7 @@ struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
|
|||||||
FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {}
|
FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {}
|
||||||
FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {}
|
FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {}
|
||||||
FMT_CONSTEXPR void on_day_of_year() {}
|
FMT_CONSTEXPR void on_day_of_year() {}
|
||||||
FMT_CONSTEXPR void on_day_of_month_zero(numeric_system) {}
|
FMT_CONSTEXPR void on_day_of_month(numeric_system) {}
|
||||||
FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {}
|
FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {}
|
||||||
FMT_CONSTEXPR void on_24_hour(numeric_system) {}
|
FMT_CONSTEXPR void on_24_hour(numeric_system) {}
|
||||||
FMT_CONSTEXPR void on_12_hour(numeric_system) {}
|
FMT_CONSTEXPR void on_12_hour(numeric_system) {}
|
||||||
@ -1399,20 +1399,20 @@ struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
|
|||||||
FMT_CONSTEXPR void on_tz_name() {}
|
FMT_CONSTEXPR void on_tz_name() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename FormatContext, typename OutputIt> class tm_formatter {
|
template <typename FormatContext, typename OutputIt> class tm_writer {
|
||||||
using char_type = typename FormatContext::char_type;
|
using char_type = typename FormatContext::char_type;
|
||||||
static constexpr int daysperweek = 7;
|
static constexpr int days_per_week = 7;
|
||||||
|
|
||||||
FormatContext& ctx_;
|
FormatContext& ctx_;
|
||||||
OutputIt out_;
|
OutputIt out_;
|
||||||
const std::tm& tm_;
|
const std::tm& tm_;
|
||||||
|
|
||||||
auto tm_year() const noexcept -> int { return 1900 + tm_.tm_year; }
|
auto tm_year() const noexcept -> int { return 1900 + tm_.tm_year; }
|
||||||
|
|
||||||
// POSIX and the C Standard are unclear or inconsistent about what %C and %y
|
// POSIX and the C Standard are unclear or inconsistent about what %C and %y
|
||||||
// do if the year is negative or exceeds 9999. Use the convention that %C
|
// do if the year is negative or exceeds 9999. Use the convention that %C
|
||||||
// concatenated with %y yields the same output as %Y, and that %Y contains at
|
// concatenated with %y yields the same output as %Y, and that %Y contains at
|
||||||
// least 4 bytes, with more only if necessary.
|
// least 4 characters, with more only if necessary.
|
||||||
auto split_year_upper(int year) const noexcept -> int { return year / 100; }
|
|
||||||
auto split_year_lower(int year) const noexcept -> int {
|
auto split_year_lower(int year) const noexcept -> int {
|
||||||
auto l = year % 100;
|
auto l = year % 100;
|
||||||
if (l < 0) {
|
if (l < 0) {
|
||||||
@ -1428,47 +1428,39 @@ template <typename FormatContext, typename OutputIt> class tm_formatter {
|
|||||||
const int prev_year = curr_year - 1;
|
const int prev_year = curr_year - 1;
|
||||||
const int curr_p =
|
const int curr_p =
|
||||||
(curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
|
(curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
|
||||||
daysperweek;
|
days_per_week;
|
||||||
const int prev_p =
|
const int prev_p =
|
||||||
(prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
|
(prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
|
||||||
daysperweek;
|
days_per_week;
|
||||||
return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
|
return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
|
||||||
}
|
}
|
||||||
auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int {
|
auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int {
|
||||||
return (tm_yday + 11 - (tm_wday == 0 ? daysperweek : tm_wday)) /
|
return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) /
|
||||||
daysperweek;
|
days_per_week;
|
||||||
}
|
}
|
||||||
auto tm_iso_week_year() const noexcept -> int {
|
auto tm_iso_week_year() const noexcept -> int {
|
||||||
const auto year = tm_year();
|
const auto year = tm_year();
|
||||||
const int w = iso_week_num(tm_.tm_yday, tm_.tm_wday);
|
const int w = iso_week_num(tm_.tm_yday, tm_.tm_wday);
|
||||||
if (w < 1) {
|
if (w < 1) return year - 1;
|
||||||
return year - 1;
|
if (w > iso_year_weeks(year)) return year + 1;
|
||||||
} else if (w > iso_year_weeks(year)) {
|
|
||||||
return year + 1;
|
|
||||||
} else {
|
|
||||||
return year;
|
return year;
|
||||||
}
|
}
|
||||||
}
|
auto tm_iso_week_of_year() const noexcept -> int {
|
||||||
auto tm_iso_week_woy() const noexcept -> int {
|
|
||||||
const auto year = tm_year();
|
const auto year = tm_year();
|
||||||
const int w = iso_week_num(tm_.tm_yday, tm_.tm_wday);
|
const int w = iso_week_num(tm_.tm_yday, tm_.tm_wday);
|
||||||
if (w < 1) {
|
if (w < 1) return iso_year_weeks(year - 1);
|
||||||
return iso_year_weeks(year - 1);
|
if (w > iso_year_weeks(year)) return 1;
|
||||||
} else if (w > iso_year_weeks(year)) {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto tm_hour12() const noexcept -> decltype(tm_.tm_hour) {
|
auto tm_hour12() const noexcept -> int {
|
||||||
auto hour = tm_.tm_hour % 12;
|
auto hour = tm_.tm_hour % 12;
|
||||||
return hour == 0 ? 12 : hour;
|
return hour == 0 ? 12 : hour;
|
||||||
}
|
}
|
||||||
|
|
||||||
void write1(size_t value) { *out_++ = detail::digits2(value)[1]; }
|
void write1(size_t value) { *out_++ = static_cast<char>('0' + value % 10); }
|
||||||
void write2(size_t value) {
|
void write2(size_t value) {
|
||||||
const char* d = detail::digits2(value);
|
const char* d = digits2(value);
|
||||||
*out_++ = *d++;
|
*out_++ = *d++;
|
||||||
*out_++ = *d;
|
*out_++ = *d;
|
||||||
}
|
}
|
||||||
@ -1477,7 +1469,18 @@ template <typename FormatContext, typename OutputIt> class tm_formatter {
|
|||||||
write2(to_unsigned(year / 100));
|
write2(to_unsigned(year / 100));
|
||||||
write2(to_unsigned(year % 100));
|
write2(to_unsigned(year % 100));
|
||||||
} else {
|
} else {
|
||||||
out_ = detail::write<char_type>(out_, year);
|
// at least 4 characters
|
||||||
|
int width = 4;
|
||||||
|
if (year < 0) {
|
||||||
|
*out_++ = '-';
|
||||||
|
year = 0 - year;
|
||||||
|
--width;
|
||||||
|
}
|
||||||
|
uint32_or_64_or_128_t<int> n =
|
||||||
|
to_unsigned(to_nonnegative_int(year, max_value<int>()));
|
||||||
|
const int num_digits = count_digits(n);
|
||||||
|
if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0');
|
||||||
|
out_ = format_decimal<char_type>(out_, n, num_digits).end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1505,34 +1508,32 @@ template <typename FormatContext, typename OutputIt> class tm_formatter {
|
|||||||
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
||||||
}
|
}
|
||||||
// Remove the extra space.
|
// Remove the extra space.
|
||||||
out_ = detail::copy_str<char_type>(buf.begin() + 1, buf.end(), out_);
|
out_ = copy_str<char_type>(buf.begin() + 1, buf.end(), out_);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit tm_formatter(FormatContext& ctx, OutputIt out, const std::tm& tm)
|
explicit tm_writer(FormatContext& ctx, OutputIt out, const std::tm& tm)
|
||||||
: ctx_(ctx), out_(out), tm_(tm) {}
|
: ctx_(ctx), out_(out), tm_(tm) {}
|
||||||
|
|
||||||
OutputIt out() const { return out_; }
|
OutputIt out() const { return out_; }
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_text(const char_type* begin, const char_type* end) {
|
FMT_CONSTEXPR void on_text(const char_type* begin, const char_type* end) {
|
||||||
out_ = detail::copy_str<char_type>(begin, end, out_);
|
out_ = copy_str<char_type>(begin, end, out_);
|
||||||
}
|
}
|
||||||
void on_abbr_weekday() { format_localized('a'); }
|
void on_abbr_weekday() { format_localized('a'); }
|
||||||
void on_full_weekday() { format_localized('A'); }
|
void on_full_weekday() { format_localized('A'); }
|
||||||
void on_dec0_weekday(numeric_system ns) {
|
void on_dec0_weekday(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard)
|
||||||
write1(detail::to_unsigned(tm_.tm_wday));
|
write1(to_unsigned(tm_.tm_wday));
|
||||||
} else {
|
else
|
||||||
format_localized('w', 'O');
|
format_localized('w', 'O');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void on_dec1_weekday(numeric_system ns) {
|
void on_dec1_weekday(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard)
|
||||||
write1(detail::to_unsigned(tm_.tm_wday == 0 ? daysperweek : tm_.tm_wday));
|
write1(to_unsigned(tm_.tm_wday == 0 ? days_per_week : tm_.tm_wday));
|
||||||
} else {
|
else
|
||||||
format_localized('u', 'O');
|
format_localized('u', 'O');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void on_abbr_month() { format_localized('b'); }
|
void on_abbr_month() { format_localized('b'); }
|
||||||
void on_full_month() { format_localized('B'); }
|
void on_full_month() { format_localized('B'); }
|
||||||
|
|
||||||
@ -1547,159 +1548,147 @@ template <typename FormatContext, typename OutputIt> class tm_formatter {
|
|||||||
}
|
}
|
||||||
void on_us_date() {
|
void on_us_date() {
|
||||||
char buf[8];
|
char buf[8];
|
||||||
detail::write_digit2_separated(
|
write_digit2_separated(buf, to_unsigned(tm_.tm_mon + 1),
|
||||||
buf, detail::to_unsigned(tm_.tm_mon + 1),
|
to_unsigned(tm_.tm_mday),
|
||||||
detail::to_unsigned(tm_.tm_mday),
|
to_unsigned(split_year_lower(tm_year())), '/');
|
||||||
detail::to_unsigned(split_year_lower(tm_year())), '/');
|
out_ = copy_str<char_type>(std::begin(buf), std::end(buf), out_);
|
||||||
out_ = detail::copy_str<char_type>(std::begin(buf), std::end(buf), out_);
|
|
||||||
}
|
}
|
||||||
void on_iso_date() {
|
void on_iso_date() {
|
||||||
auto year = tm_year();
|
auto year = tm_year();
|
||||||
char buf[10];
|
char buf[10];
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
if (year >= 0 && year < 10000) {
|
if (year >= 0 && year < 10000) {
|
||||||
detail::copy2(buf, detail::digits2(detail::to_unsigned(year / 100)));
|
copy2(buf, digits2(to_unsigned(year / 100)));
|
||||||
} else {
|
} else {
|
||||||
offset = 4;
|
offset = 4;
|
||||||
out_ = detail::write<char_type>(out_, year);
|
write_year(year);
|
||||||
year = 0;
|
year = 0;
|
||||||
}
|
}
|
||||||
detail::write_digit2_separated(buf + 2, year % 100,
|
write_digit2_separated(buf + 2, year % 100, to_unsigned(tm_.tm_mon + 1),
|
||||||
detail::to_unsigned(tm_.tm_mon + 1),
|
to_unsigned(tm_.tm_mday), '-');
|
||||||
detail::to_unsigned(tm_.tm_mday), '-');
|
out_ = copy_str<char_type>(std::begin(buf) + offset, std::end(buf), out_);
|
||||||
out_ = detail::copy_str<char_type>(std::begin(buf) + offset, std::end(buf),
|
|
||||||
out_);
|
|
||||||
}
|
}
|
||||||
void on_utc_offset() { format_localized('z'); }
|
void on_utc_offset() { format_localized('z'); }
|
||||||
void on_tz_name() { format_localized('Z'); }
|
void on_tz_name() { format_localized('Z'); }
|
||||||
void on_year(numeric_system ns) {
|
void on_year(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard)
|
||||||
write_year(tm_year());
|
write_year(tm_year());
|
||||||
} else {
|
else
|
||||||
format_localized('Y', 'E');
|
format_localized('Y', 'E');
|
||||||
}
|
}
|
||||||
}
|
void on_short_year(numeric_system ns) {
|
||||||
void on_last2_year(numeric_system ns) {
|
if (ns == numeric_system::standard)
|
||||||
if (ns == numeric_system::standard) {
|
write2(to_unsigned(split_year_lower(tm_year())));
|
||||||
write2(detail::to_unsigned(split_year_lower(tm_year())));
|
else
|
||||||
} else {
|
|
||||||
format_localized('y', 'O');
|
format_localized('y', 'O');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void on_offset_year() { format_localized('y', 'E'); }
|
void on_offset_year() { format_localized('y', 'E'); }
|
||||||
void on_base_year(numeric_system ns) {
|
void on_century(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard) {
|
||||||
auto upper = split_year_upper(tm_year());
|
auto year = tm_year();
|
||||||
if (upper >= 0 && upper < 100) {
|
auto upper = year / 100;
|
||||||
write2(detail::to_unsigned(upper));
|
if (year >= -99 && year < 0) {
|
||||||
} else {
|
// zero upper on negative year
|
||||||
out_ = detail::write<char_type>(out_, upper);
|
*out_++ = '-';
|
||||||
}
|
*out_++ = '0';
|
||||||
} else {
|
} else if (upper >= 0 && upper < 100)
|
||||||
|
write2(to_unsigned(upper));
|
||||||
|
else
|
||||||
|
out_ = write<char_type>(out_, upper);
|
||||||
|
} else
|
||||||
format_localized('C', 'E');
|
format_localized('C', 'E');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void on_dec_month(numeric_system ns) {
|
void on_dec_month(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard)
|
||||||
write2(detail::to_unsigned(tm_.tm_mon + 1));
|
write2(to_unsigned(tm_.tm_mon + 1));
|
||||||
} else {
|
else
|
||||||
format_localized('m', 'O');
|
format_localized('m', 'O');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void on_dec0_week_of_year(numeric_system ns) {
|
void on_dec0_week_of_year(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard)
|
||||||
write2(detail::to_unsigned((tm_.tm_yday + daysperweek - tm_.tm_wday) /
|
write2(to_unsigned((tm_.tm_yday + days_per_week - tm_.tm_wday) /
|
||||||
daysperweek));
|
days_per_week));
|
||||||
} else {
|
else
|
||||||
format_localized('U', 'O');
|
format_localized('U', 'O');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void on_dec1_week_of_year(numeric_system ns) {
|
void on_dec1_week_of_year(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard)
|
||||||
write2(detail::to_unsigned(
|
write2(to_unsigned(
|
||||||
(tm_.tm_yday + daysperweek -
|
(tm_.tm_yday + days_per_week -
|
||||||
(tm_.tm_wday == 0 ? (daysperweek - 1) : (tm_.tm_wday - 1))) /
|
(tm_.tm_wday == 0 ? (days_per_week - 1) : (tm_.tm_wday - 1))) /
|
||||||
daysperweek));
|
days_per_week));
|
||||||
} else {
|
else
|
||||||
format_localized('W', 'O');
|
format_localized('W', 'O');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void on_iso_week_of_year(numeric_system ns) {
|
void on_iso_week_of_year(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard)
|
||||||
write2(detail::to_unsigned(tm_iso_week_woy()));
|
write2(to_unsigned(tm_iso_week_of_year()));
|
||||||
} else {
|
else
|
||||||
format_localized('V', 'O');
|
format_localized('V', 'O');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
|
void on_iso_week_based_year() { write_year(tm_iso_week_year()); }
|
||||||
void on_iso_week_based_year_last2() {
|
void on_iso_week_based_short_year() {
|
||||||
write2(detail::to_unsigned(split_year_lower(tm_iso_week_year())));
|
write2(to_unsigned(split_year_lower(tm_iso_week_year())));
|
||||||
}
|
}
|
||||||
void on_day_of_year() {
|
void on_day_of_year() {
|
||||||
auto yday = tm_.tm_yday + 1;
|
auto yday = tm_.tm_yday + 1;
|
||||||
write1(detail::to_unsigned(yday / 100));
|
write1(to_unsigned(yday / 100));
|
||||||
write2(detail::to_unsigned(yday % 100));
|
write2(to_unsigned(yday % 100));
|
||||||
}
|
}
|
||||||
void on_day_of_month_zero(numeric_system ns) {
|
void on_day_of_month(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard)
|
||||||
write2(detail::to_unsigned(tm_.tm_mday));
|
write2(to_unsigned(tm_.tm_mday));
|
||||||
} else {
|
else
|
||||||
format_localized('d', 'O');
|
format_localized('d', 'O');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void on_day_of_month_space(numeric_system ns) {
|
void on_day_of_month_space(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard) {
|
||||||
const char* d2 = detail::digits2(detail::to_unsigned(tm_.tm_mday));
|
const char* d2 = digits2(to_unsigned(tm_.tm_mday));
|
||||||
*out_++ = tm_.tm_mday < 10 ? ' ' : d2[0];
|
*out_++ = tm_.tm_mday < 10 ? ' ' : d2[0];
|
||||||
*out_++ = d2[1];
|
*out_++ = d2[1];
|
||||||
} else {
|
} else
|
||||||
format_localized('e', 'O');
|
format_localized('e', 'O');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void on_24_hour(numeric_system ns) {
|
void on_24_hour(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard)
|
||||||
write2(detail::to_unsigned(tm_.tm_hour));
|
write2(to_unsigned(tm_.tm_hour));
|
||||||
} else {
|
else
|
||||||
format_localized('H', 'O');
|
format_localized('H', 'O');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void on_12_hour(numeric_system ns) {
|
void on_12_hour(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard)
|
||||||
write2(detail::to_unsigned(tm_hour12()));
|
write2(to_unsigned(tm_hour12()));
|
||||||
} else {
|
else
|
||||||
format_localized('I', 'O');
|
format_localized('I', 'O');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void on_minute(numeric_system ns) {
|
void on_minute(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard)
|
||||||
write2(detail::to_unsigned(tm_.tm_min));
|
write2(to_unsigned(tm_.tm_min));
|
||||||
} else {
|
else
|
||||||
format_localized('M', 'O');
|
format_localized('M', 'O');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void on_second(numeric_system ns) {
|
void on_second(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard)
|
||||||
write2(detail::to_unsigned(tm_.tm_sec));
|
write2(to_unsigned(tm_.tm_sec));
|
||||||
} else {
|
else
|
||||||
format_localized('S', 'O');
|
format_localized('S', 'O');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
void on_12_hour_time() { format_localized('r'); }
|
void on_12_hour_time() { format_localized('r'); }
|
||||||
void on_24_hour_time() {
|
void on_24_hour_time() {
|
||||||
write2(detail::to_unsigned(tm_.tm_hour));
|
write2(to_unsigned(tm_.tm_hour));
|
||||||
*out_++ = ':';
|
*out_++ = ':';
|
||||||
write2(detail::to_unsigned(tm_.tm_min));
|
write2(to_unsigned(tm_.tm_min));
|
||||||
}
|
}
|
||||||
void on_iso_time() {
|
void on_iso_time() {
|
||||||
char buf[8];
|
char buf[8];
|
||||||
detail::write_digit2_separated(buf, detail::to_unsigned(tm_.tm_hour),
|
write_digit2_separated(buf, to_unsigned(tm_.tm_hour),
|
||||||
detail::to_unsigned(tm_.tm_min),
|
to_unsigned(tm_.tm_min), to_unsigned(tm_.tm_sec),
|
||||||
detail::to_unsigned(tm_.tm_sec), ':');
|
':');
|
||||||
out_ = detail::copy_str<char_type>(std::begin(buf), std::end(buf), out_);
|
out_ = copy_str<char_type>(std::begin(buf), std::end(buf), out_);
|
||||||
}
|
}
|
||||||
void on_am_pm() { format_localized('p'); }
|
void on_am_pm() { format_localized('p'); }
|
||||||
|
|
||||||
// These are not implemented because durations don't have date information.
|
// These apply to chrono durations but not tm.
|
||||||
void on_duration_value() {}
|
void on_duration_value() {}
|
||||||
void on_duration_unit() {}
|
void on_duration_unit() {}
|
||||||
};
|
};
|
||||||
@ -1773,15 +1762,12 @@ template <typename Char> struct formatter<std::tm, Char> {
|
|||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const std::tm& tm, FormatContext& ctx) const
|
auto format(const std::tm& tm, FormatContext& ctx) const
|
||||||
-> decltype(ctx.out()) {
|
-> decltype(ctx.out()) {
|
||||||
detail::tm_formatter<FormatContext, decltype(ctx.out())> f(ctx, ctx.out(),
|
detail::tm_writer<FormatContext, decltype(ctx.out())> f(ctx, ctx.out(), tm);
|
||||||
tm);
|
if (spec_ == spec::year_month_day)
|
||||||
if (spec_ == spec::year_month_day) {
|
|
||||||
f.on_iso_date();
|
f.on_iso_date();
|
||||||
return f.out();
|
else if (spec_ == spec::hh_mm_ss)
|
||||||
} else if (spec_ == spec::hh_mm_ss) {
|
|
||||||
f.on_iso_time();
|
f.on_iso_time();
|
||||||
return f.out();
|
else
|
||||||
}
|
|
||||||
detail::parse_chrono_format(specs.begin(), specs.end(), f);
|
detail::parse_chrono_format(specs.begin(), specs.end(), f);
|
||||||
return f.out();
|
return f.out();
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,7 @@
|
|||||||
|
|
||||||
#include "fmt/chrono.h"
|
#include "fmt/chrono.h"
|
||||||
|
|
||||||
#include <time.h>
|
#include <ctime>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "gtest-extra.h" // EXPECT_THROW_MSG
|
#include "gtest-extra.h" // EXPECT_THROW_MSG
|
||||||
@ -50,6 +49,18 @@ std::string system_strftime(const std::string& format, const std::tm* timeptr,
|
|||||||
return std::string(output.data(), size);
|
return std::string(output.data(), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR std::tm make_tm(int year, int mon, int mday, int hour, int min,
|
||||||
|
int sec) {
|
||||||
|
auto tm = std::tm();
|
||||||
|
tm.tm_sec = sec;
|
||||||
|
tm.tm_min = min;
|
||||||
|
tm.tm_hour = hour;
|
||||||
|
tm.tm_mday = mday;
|
||||||
|
tm.tm_mon = mon - 1;
|
||||||
|
tm.tm_year = year - 1900;
|
||||||
|
return tm;
|
||||||
|
}
|
||||||
|
|
||||||
TEST(chrono_test, format_tm) {
|
TEST(chrono_test, format_tm) {
|
||||||
auto tm = std::tm();
|
auto tm = std::tm();
|
||||||
tm.tm_year = 116;
|
tm.tm_year = 116;
|
||||||
@ -84,33 +95,22 @@ TEST(chrono_test, format_tm) {
|
|||||||
|
|
||||||
// for week on the year
|
// for week on the year
|
||||||
// https://www.cl.cam.ac.uk/~mgk25/iso-time.html
|
// https://www.cl.cam.ac.uk/~mgk25/iso-time.html
|
||||||
std::vector<std::string> str_tm_list = {
|
std::vector<std::tm> tm_list = {
|
||||||
"1975-12-29", // W01
|
make_tm(1975, 12, 29, 12, 14, 16), // W01
|
||||||
"1977-01-02", // W53
|
make_tm(1977, 1, 2, 12, 14, 16), // W53
|
||||||
"1999-12-27", // W52
|
make_tm(1999, 12, 27, 12, 14, 16), // W52
|
||||||
"1999-12-31", // W52
|
make_tm(1999, 12, 31, 12, 14, 16), // W52
|
||||||
"2000-01-01", // W52
|
make_tm(2000, 1, 1, 12, 14, 16), // W52
|
||||||
"2000-01-02", // W52
|
make_tm(2000, 1, 2, 12, 14, 16), // W52
|
||||||
"2000-01-03", // W1
|
make_tm(2000, 1, 3, 12, 14, 16) // W1
|
||||||
};
|
};
|
||||||
const std::string iso_week_spec = "%Y-%m-%d: %G %g %V";
|
const std::string iso_week_spec = "%Y-%m-%d: %G %g %V";
|
||||||
|
for (auto ctm : tm_list) {
|
||||||
for (const auto& str_tm : str_tm_list) {
|
// Calculate tm_yday, tm_wday, etc.
|
||||||
tm = std::tm();
|
std::time_t t = std::mktime(&ctm);
|
||||||
// GCC 4 does not support std::get_time
|
|
||||||
// MSVC dows not support POSIX strptime
|
|
||||||
#ifdef _WIN32
|
|
||||||
std::istringstream ss(str_tm);
|
|
||||||
ss >> std::get_time(&tm, "%Y-%m-%d");
|
|
||||||
#else
|
|
||||||
strptime(str_tm.c_str(), "%Y-%m-%d", &tm);
|
|
||||||
#endif
|
|
||||||
// Because std::get_time doesn't calculate tm_yday, tm_wday, etc.
|
|
||||||
tm.tm_isdst = 0;
|
|
||||||
std::time_t t = std::mktime(&tm);
|
|
||||||
tm = *std::localtime(&t);
|
tm = *std::localtime(&t);
|
||||||
|
|
||||||
auto fmt_spec = std::string("{:").append(iso_week_spec).append("}");
|
auto fmt_spec = fmt::format("{{:{}}}", iso_week_spec);
|
||||||
EXPECT_EQ(system_strftime(iso_week_spec, &tm),
|
EXPECT_EQ(system_strftime(iso_week_spec, &tm),
|
||||||
fmt::format(fmt::runtime(fmt_spec), tm));
|
fmt::format(fmt::runtime(fmt_spec), tm));
|
||||||
}
|
}
|
||||||
@ -120,7 +120,7 @@ TEST(chrono_test, format_tm) {
|
|||||||
for (std::time_t t = 6 * 3600; t < time_now; t += 86400) {
|
for (std::time_t t = 6 * 3600; t < time_now; t += 86400) {
|
||||||
tm = *std::localtime(&t);
|
tm = *std::localtime(&t);
|
||||||
|
|
||||||
auto fmt_spec = std::string("{:").append(iso_week_spec).append("}");
|
auto fmt_spec = fmt::format("{{:{}}}", iso_week_spec);
|
||||||
EXPECT_EQ(system_strftime(iso_week_spec, &tm),
|
EXPECT_EQ(system_strftime(iso_week_spec, &tm),
|
||||||
fmt::format(fmt::runtime(fmt_spec), tm));
|
fmt::format(fmt::runtime(fmt_spec), tm));
|
||||||
}
|
}
|
||||||
@ -173,6 +173,14 @@ TEST(chrono_test, format_tm_past) {
|
|||||||
|
|
||||||
EXPECT_EQ(fmt::format("{:%F}", tm), "-101-04-25");
|
EXPECT_EQ(fmt::format("{:%F}", tm), "-101-04-25");
|
||||||
EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33");
|
EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33");
|
||||||
|
|
||||||
|
tm.tm_year = -1901; // -1
|
||||||
|
EXPECT_EQ(fmt::format("{:%Y}", tm), "-001");
|
||||||
|
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
|
||||||
|
|
||||||
|
tm.tm_year = -1911; // -11
|
||||||
|
EXPECT_EQ(fmt::format("{:%Y}", tm), "-011");
|
||||||
|
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -243,7 +251,7 @@ TEST(chrono_test, time_point) {
|
|||||||
|
|
||||||
auto sys_output = system_strftime(spec, &tm);
|
auto sys_output = system_strftime(spec, &tm);
|
||||||
|
|
||||||
auto fmt_spec = std::string("{:").append(spec).append("}");
|
auto fmt_spec = fmt::format("{{:{}}}", spec);
|
||||||
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1));
|
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1));
|
||||||
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm));
|
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm));
|
||||||
}
|
}
|
||||||
|
@ -293,7 +293,7 @@ TEST(chrono_test, time_point) {
|
|||||||
|
|
||||||
auto sys_output = system_wcsftime(spec, &tm);
|
auto sys_output = system_wcsftime(spec, &tm);
|
||||||
|
|
||||||
auto fmt_spec = std::wstring(L"{:").append(spec).append(L"}");
|
auto fmt_spec = fmt::format(L"{{:{}}}", spec);
|
||||||
EXPECT_EQ(sys_output, fmt::format(fmt_spec, t1));
|
EXPECT_EQ(sys_output, fmt::format(fmt_spec, t1));
|
||||||
EXPECT_EQ(sys_output, fmt::format(fmt_spec, tm));
|
EXPECT_EQ(sys_output, fmt::format(fmt_spec, tm));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user