mirror of
https://github.com/fmtlib/fmt.git
synced 2026-11-09 14:50:24 +08:00
Simplify ISO week computation
Compute the ISO week-based year and week number from the day of the year of the Thursday in the current week instead of the weekday of December 31. This only needs a leap year check, which works for negative years without floored division, and is about 3 times faster. For std::tm values with tm_wday and tm_yday inconsistent with tm_year, %G, %g and %V now give the same results as strftime. Replace the negative year tests with a year_month_day test that also covers the leap year check and days_from_civil for negative years.
This commit is contained in:
parent
f87a75e152
commit
97bd2ad530
@ -1151,42 +1151,29 @@ class tm_writer {
|
||||
return static_cast<int>(l);
|
||||
}
|
||||
|
||||
// Returns the weekday of December 31 of the given year (0 = Sunday). Uses
|
||||
// floored division and a non-negative remainder so that it is also correct
|
||||
// for negative years.
|
||||
static auto dec31_weekday(long long year) noexcept -> long long {
|
||||
auto floor_div = [](long long a, long long b) {
|
||||
return a / b - (a % b < 0 ? 1 : 0);
|
||||
};
|
||||
auto p = (year + floor_div(year, 4) - floor_div(year, 100) +
|
||||
floor_div(year, 400)) %
|
||||
days_per_week;
|
||||
return p < 0 ? p + days_per_week : p;
|
||||
static auto days_in_year(long long year) noexcept -> int {
|
||||
return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? 366 : 365;
|
||||
}
|
||||
|
||||
// Algorithm: https://en.wikipedia.org/wiki/ISO_week_date.
|
||||
auto iso_year_weeks(long long curr_year) const noexcept -> int {
|
||||
auto curr_p = dec31_weekday(curr_year);
|
||||
auto prev_p = dec31_weekday(curr_year - 1);
|
||||
return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
|
||||
}
|
||||
auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int {
|
||||
return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) /
|
||||
days_per_week;
|
||||
// Returns the day of the year of the Thursday in the current week, which may
|
||||
// be outside the current year. The ISO week-based year is the year of this
|
||||
// Thursday: https://en.wikipedia.org/wiki/ISO_week_date.
|
||||
auto thursday_yday() const noexcept -> int {
|
||||
auto wday = tm_wday();
|
||||
return tm_yday() + 4 - (wday == 0 ? days_per_week : wday);
|
||||
}
|
||||
auto tm_iso_week_year() const noexcept -> long long {
|
||||
auto year = tm_year();
|
||||
auto w = iso_week_num(tm_yday(), tm_wday());
|
||||
if (w < 1) return year - 1;
|
||||
if (w > iso_year_weeks(year)) return year + 1;
|
||||
auto thursday = thursday_yday();
|
||||
if (thursday < 0) return year - 1;
|
||||
if (thursday >= days_in_year(year)) return year + 1;
|
||||
return year;
|
||||
}
|
||||
auto tm_iso_week_of_year() const noexcept -> int {
|
||||
auto year = tm_year();
|
||||
auto w = iso_week_num(tm_yday(), tm_wday());
|
||||
if (w < 1) return iso_year_weeks(year - 1);
|
||||
if (w > iso_year_weeks(year)) return 1;
|
||||
return w;
|
||||
auto thursday = thursday_yday();
|
||||
if (thursday >= days_in_year(tm_year())) return 1;
|
||||
if (thursday < 0) thursday += days_in_year(tm_year() - 1);
|
||||
return thursday / days_per_week + 1;
|
||||
}
|
||||
|
||||
void write1(int value) {
|
||||
|
||||
@ -123,21 +123,6 @@ TEST(chrono_test, format_tm) {
|
||||
tm.tm_year = -2147483648;
|
||||
EXPECT_EQ(fmt::format("{:%Y}", tm), "-2147481748");
|
||||
|
||||
// ISO week-based year and week number for negative years. The year -2 starts
|
||||
// on a Thursday, so it has 53 ISO weeks.
|
||||
tm = make_tm(-2, 12, 31, 0, 0, 0);
|
||||
tm.tm_wday = 4;
|
||||
tm.tm_yday = 364;
|
||||
EXPECT_EQ(fmt::format("{:%G %V}", tm), "-002 53");
|
||||
tm = make_tm(-1, 1, 1, 0, 0, 0);
|
||||
tm.tm_wday = 5;
|
||||
tm.tm_yday = 0;
|
||||
EXPECT_EQ(fmt::format("{:%G %V}", tm), "-002 53");
|
||||
tm = make_tm(-1, 1, 4, 0, 0, 0);
|
||||
tm.tm_wday = 1;
|
||||
tm.tm_yday = 3;
|
||||
EXPECT_EQ(fmt::format("{:%G %V}", tm), "-001 01");
|
||||
|
||||
// for week on the year
|
||||
// https://www.cl.cam.ac.uk/~mgk25/iso-time.html
|
||||
std::vector<std::tm> tm_list = {
|
||||
@ -1127,6 +1112,9 @@ TEST(chrono_test, year_month_day_weekday_and_day_of_year) {
|
||||
EXPECT_EQ(fmt::format("{:%a %j %U %W}", ymd), "Fri 001 00 00");
|
||||
EXPECT_EQ(fmt::format("{:%G-W%V-%u}", ymd), "2020-W53-5");
|
||||
|
||||
ymd = fmt::year_month_day(fmt::year(-23), fmt::month(1), fmt::day(1));
|
||||
EXPECT_EQ(fmt::format("{:%G-W%V}", ymd), "-024-W53");
|
||||
|
||||
ymd = fmt::year_month_day(fmt::year(1969), fmt::month(12), fmt::day(31));
|
||||
EXPECT_EQ(fmt::format("{:%A %j}", ymd), "Wednesday 365");
|
||||
EXPECT_EQ(fmt::format("{:%G-W%V}", ymd), "1970-W01");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user