mirror of
https://github.com/fmtlib/fmt.git
synced 2025-04-10 12:44:48 +00:00
Fix errors in ISO week-base-year formatter
This commit is contained in:
parent
218cecb6d1
commit
2754546080
@ -1412,11 +1412,11 @@ template <typename FormatContext, typename OutputIt> struct tm_formatter {
|
|||||||
// 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 bytes, with more only if necessary.
|
||||||
struct split_year {
|
struct split_year_result {
|
||||||
int upper;
|
int upper;
|
||||||
int lower;
|
int lower;
|
||||||
};
|
};
|
||||||
auto tm_split_year(int year) const -> split_year {
|
auto split_year(int year) const -> split_year_result {
|
||||||
auto q = year / 100;
|
auto q = year / 100;
|
||||||
auto r = year % 100;
|
auto r = year % 100;
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
@ -1428,23 +1428,28 @@ template <typename FormatContext, typename OutputIt> struct tm_formatter {
|
|||||||
|
|
||||||
// Algorithm:
|
// Algorithm:
|
||||||
// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
|
// https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
|
||||||
struct iso_weak {
|
struct iso_week_result {
|
||||||
int year;
|
int year;
|
||||||
int woy;
|
int woy;
|
||||||
};
|
};
|
||||||
auto tm_iso_year_weaks(int year) -> int {
|
auto iso_year_weeks(const int curr_year) -> int {
|
||||||
int p = (year + year / 4 - year / 100 + year / 400) % daysperweek;
|
const int prev_year = curr_year - 1;
|
||||||
int year_1 = year - 1;
|
const int curr_p =
|
||||||
int p_1 = (year_1 + year_1 / 4 - year_1 / 100 + year_1 / 400) % daysperweek;
|
(curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
|
||||||
return 52 + ((p == 4 || p_1 == 3) ? 1 : 0);
|
daysperweek;
|
||||||
|
const int prev_p =
|
||||||
|
(prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
|
||||||
|
daysperweek;
|
||||||
|
return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
|
||||||
}
|
}
|
||||||
auto tm_iso_weak() -> iso_weak {
|
auto tm_iso_week() -> iso_week_result {
|
||||||
auto year = tm_year();
|
const auto year = tm_year();
|
||||||
int w = (tm.tm_yday + 10 - (tm.tm_wday == 0 ? daysperweek : tm.tm_wday)) /
|
const int w =
|
||||||
daysperweek;
|
(tm.tm_yday + 11 - (tm.tm_wday == 0 ? daysperweek : tm.tm_wday)) /
|
||||||
|
daysperweek;
|
||||||
if (w < 1) {
|
if (w < 1) {
|
||||||
return {year - 1, tm_iso_year_weaks(year - 1)};
|
return {year - 1, iso_year_weeks(year - 1)};
|
||||||
} else if (w > tm_iso_year_weaks(year)) {
|
} else if (w > iso_year_weeks(year)) {
|
||||||
return {year + 1, 1};
|
return {year + 1, 1};
|
||||||
} else {
|
} else {
|
||||||
return {year, w};
|
return {year, w};
|
||||||
@ -1537,7 +1542,7 @@ template <typename FormatContext, typename OutputIt> struct tm_formatter {
|
|||||||
detail::write_digit2_separated(
|
detail::write_digit2_separated(
|
||||||
buf, detail::to_unsigned(tm.tm_mon + 1),
|
buf, detail::to_unsigned(tm.tm_mon + 1),
|
||||||
detail::to_unsigned(tm.tm_mday),
|
detail::to_unsigned(tm.tm_mday),
|
||||||
detail::to_unsigned(tm_split_year(tm_year()).lower), '/');
|
detail::to_unsigned(split_year(tm_year()).lower), '/');
|
||||||
out = detail::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() {
|
||||||
@ -1568,7 +1573,7 @@ template <typename FormatContext, typename OutputIt> struct tm_formatter {
|
|||||||
}
|
}
|
||||||
void on_last2_year(numeric_system ns) {
|
void on_last2_year(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard) {
|
||||||
write2(detail::to_unsigned(tm_split_year(tm_year()).lower));
|
write2(detail::to_unsigned(split_year(tm_year()).lower));
|
||||||
} else {
|
} else {
|
||||||
format_localized('y', 'O');
|
format_localized('y', 'O');
|
||||||
}
|
}
|
||||||
@ -1576,7 +1581,7 @@ template <typename FormatContext, typename OutputIt> struct tm_formatter {
|
|||||||
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_base_year(numeric_system ns) {
|
||||||
if (ns == numeric_system::standard) {
|
if (ns == numeric_system::standard) {
|
||||||
auto split = tm_split_year(tm_year());
|
auto split = split_year(tm_year());
|
||||||
if (split.upper >= 0 && split.upper < 100) {
|
if (split.upper >= 0 && split.upper < 100) {
|
||||||
write2(detail::to_unsigned(split.upper));
|
write2(detail::to_unsigned(split.upper));
|
||||||
} else {
|
} else {
|
||||||
@ -1613,14 +1618,14 @@ template <typename FormatContext, typename OutputIt> struct tm_formatter {
|
|||||||
}
|
}
|
||||||
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_weak().woy));
|
write2(detail::to_unsigned(tm_iso_week().woy));
|
||||||
} else {
|
} else {
|
||||||
format_localized('V', 'O');
|
format_localized('V', 'O');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void on_iso_week_based_year() { write_year(tm_iso_weak().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_year_last2() {
|
||||||
write2(detail::to_unsigned(tm_split_year(tm_iso_weak().year).lower));
|
write2(detail::to_unsigned(split_year(tm_iso_week().year).lower));
|
||||||
}
|
}
|
||||||
void on_day_of_year() {
|
void on_day_of_year() {
|
||||||
auto yday = tm.tm_yday + 1;
|
auto yday = tm.tm_yday + 1;
|
||||||
|
@ -85,10 +85,10 @@ TEST(chrono_test, format_tm) {
|
|||||||
"2000-01-02", // W52
|
"2000-01-02", // W52
|
||||||
"2000-01-03", // W1
|
"2000-01-03", // W1
|
||||||
};
|
};
|
||||||
std::vector<std::string> spec_list = {"%G", "%g", "%V"};
|
const std::string iso_week_spec = "%Y-%m-%d: %G %g %V";
|
||||||
|
|
||||||
for (const auto& str_tm : str_tm_list) {
|
for (const auto& str_tm : str_tm_list) {
|
||||||
tm = std::tm();
|
tm = std::tm();
|
||||||
|
|
||||||
// GCC 4 does not support std::get_time
|
// GCC 4 does not support std::get_time
|
||||||
// MSVC dows not support POSIX strptime
|
// MSVC dows not support POSIX strptime
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -102,12 +102,21 @@ TEST(chrono_test, format_tm) {
|
|||||||
std::time_t t = std::mktime(&tm);
|
std::time_t t = std::mktime(&tm);
|
||||||
tm = *std::localtime(&t);
|
tm = *std::localtime(&t);
|
||||||
|
|
||||||
for (const auto& spec : spec_list) {
|
char output[256] = {};
|
||||||
char output[256] = {};
|
std::strftime(output, sizeof(output), iso_week_spec.c_str(), &tm);
|
||||||
std::strftime(output, sizeof(output), spec.c_str(), &tm);
|
auto fmt_spec = std::string("{:").append(iso_week_spec).append("}");
|
||||||
auto fmt_spec = std::string("{:").append(spec).append("}");
|
EXPECT_EQ(output, fmt::format(fmt::runtime(fmt_spec), tm));
|
||||||
EXPECT_EQ(output, fmt::format(fmt::runtime(fmt_spec), tm));
|
}
|
||||||
}
|
|
||||||
|
// Every day from 1970-01-01
|
||||||
|
std::time_t time_now = std::time(nullptr);
|
||||||
|
for (std::time_t t = 6 * 3600; t < time_now; t += 86400) {
|
||||||
|
tm = *std::localtime(&t);
|
||||||
|
|
||||||
|
char output[256] = {};
|
||||||
|
std::strftime(output, sizeof(output), iso_week_spec.c_str(), &tm);
|
||||||
|
auto fmt_spec = std::string("{:").append(iso_week_spec).append("}");
|
||||||
|
EXPECT_EQ(output, fmt::format(fmt::runtime(fmt_spec), tm));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user