Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 19 additions & 23 deletions stl/inc/chrono
Original file line number Diff line number Diff line change
Expand Up @@ -1394,35 +1394,31 @@ namespace chrono {
// courtesy of Howard Hinnant
// https://howardhinnant.github.io/date_algorithms.html#civil_from_days
_NODISCARD static constexpr year_month_day _Civil_from_days(int _Tp) noexcept {
static_assert(numeric_limits<unsigned int>::digits >= 18);
static_assert(numeric_limits<int>::digits >= 20);
const int _Zx = _Tp + 719468; // Shift epoch to 0000-03-01
const int _Era = (_Zx >= 0 ? _Zx : _Zx - 146096) / 146097;
const unsigned int _Day_of_era = static_cast<unsigned int>(_Zx - _Era * 146097); // [0, 146096]
const unsigned int _Year_of_era =
(_Day_of_era - _Day_of_era / 1460 + _Day_of_era / 36524 - _Day_of_era / 146096) / 365; // [0, 399]
const int _Year = static_cast<int>(_Year_of_era) + _Era * 400; // Where March is the first month
const unsigned int _Day_of_year =
_Day_of_era - (365 * _Year_of_era + _Year_of_era / 4 - _Year_of_era / 100); // [0, 365]
const unsigned int _Mp = (5 * _Day_of_year + 2) / 153; // [0, 11]
const unsigned int _Day = _Day_of_year - (153 * _Mp + 2) / 5 + 1; // [1, 31]
const unsigned int _Month = _Mp + (_Mp < 10 ? 3 : static_cast<unsigned int>(-9)); // [1, 12]
static_assert(numeric_limits<unsigned int>::digits >= 32);
static_assert(numeric_limits<int>::digits >= 26);
const int _Zx = _Tp + 719468; // Shift epoch to 0000-03-01
const int _Century = (_Zx >= 0 ? 4 * _Zx + 3 : 4 * _Zx - 146093) / 146097;
const unsigned int _Day_of_century =
static_cast<unsigned int>(_Zx - ((146097 * _Century) >> 2)); // [0, 36524]
const unsigned int _Year_of_century = (91867 * (_Day_of_century + 1)) >> 25; // [0, 99]
const int _Year = static_cast<int>(_Year_of_century) + _Century * 100; // Where March is the first month
const unsigned int _Day_of_year = _Day_of_century - ((1461 * _Year_of_century) >> 2); // [0, 365]
const unsigned int _Mp = (535 * _Day_of_year + 333) >> 14; // [0, 11]
const unsigned int _Day = _Day_of_year - ((979 * _Mp + 19) >> 5) + 1; // [1, 31]
Comment on lines +1400 to +1407

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much for these optimized algorithms! Given the thorough testing, I am fairly confident in their correctness, but confess that I am having trouble following the logic behind a few of the calculations. I don't want this to hold up merging, but have filed #1641 so we can add a short explanation here in future (applies to both _Civil_from_days and _Days_from_civil).

const unsigned int _Month = _Mp + (_Mp < 10 ? 3 : static_cast<unsigned int>(-9)); // [1, 12]
return year_month_day{_CHRONO year{_Year + (_Month <= 2)}, _CHRONO month{_Month}, _CHRONO day{_Day}};
}
// courtesy of Howard Hinnant
// https://howardhinnant.github.io/date_algorithms.html#days_from_civil
_NODISCARD constexpr days _Days_from_civil() const noexcept {
static_assert(numeric_limits<unsigned int>::digits >= 18);
static_assert(numeric_limits<int>::digits >= 20);
const int _Ye = static_cast<int>(_Year) - (_Month <= _CHRONO month{2});
const unsigned int _Mo = static_cast<unsigned int>(_Month);
const int _Era = (_Ye >= 0 ? _Ye : _Ye - 399) / 400;
const unsigned int _Year_of_era = static_cast<unsigned int>(_Ye - _Era * 400); // [0, 399]
const unsigned int _Day_of_year = (153 * (_Mo + (_Mo > 2 ? static_cast<unsigned int>(-3) : 9)) + 2) / 5
+ static_cast<unsigned int>(_Day) - 1; // [0, 365]
const unsigned int _Day_of_era =
_Year_of_era * 365 + _Year_of_era / 4 - _Year_of_era / 100 + _Day_of_year; // [0, 146096]
return days{_Era * 146097 + static_cast<int>(_Day_of_era) - 719468};
static_assert(numeric_limits<int>::digits >= 26);
const unsigned int _Mo = static_cast<unsigned int>(_Month); // [1, 12]
const int _Ye = static_cast<int>(_Year) - (_Mo <= 2);
const int _Century = (_Ye >= 0 ? _Ye : _Ye - 99) / 100;
const unsigned int _Mp = _Mo + (_Mo > 2 ? static_cast<unsigned int>(-3) : 9); // [0, 11]
const int _Day_of_year = static_cast<int>(((979 * _Mp + 19) >> 5) + static_cast<unsigned int>(_Day)) - 1;
return days{((_Ye * 1461) >> 2) - _Century + (_Century >> 2) + _Day_of_year - 719468};
}
};

Expand Down
184 changes: 184 additions & 0 deletions tests/std/tests/P0355R7_calendars_and_time_zones_dates/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,190 @@ constexpr void year_month_day_test() {
}
}
}

if (is_constant_evaluated()) {
// clang-format off
static_assert(sys_days{ 2000y/ 1/ 1} == sys_days{days{ 10957}});
static_assert(sys_days{ 2000y/ 1/31} == sys_days{days{ 10987}});
static_assert(sys_days{ 2000y/ 2/ 1} == sys_days{days{ 10988}});
static_assert(sys_days{ 2000y/ 2/29} == sys_days{days{ 11016}});
static_assert(sys_days{ 2000y/ 3/ 1} == sys_days{days{ 11017}});
static_assert(sys_days{ 2000y/ 3/31} == sys_days{days{ 11047}});
static_assert(sys_days{ 2000y/ 4/ 1} == sys_days{days{ 11048}});
static_assert(sys_days{ 2000y/ 4/30} == sys_days{days{ 11077}});
static_assert(sys_days{ 2000y/ 5/ 1} == sys_days{days{ 11078}});
static_assert(sys_days{ 2000y/ 5/31} == sys_days{days{ 11108}});
static_assert(sys_days{ 2000y/ 6/ 1} == sys_days{days{ 11109}});
static_assert(sys_days{ 2000y/ 6/30} == sys_days{days{ 11138}});
static_assert(sys_days{ 2000y/ 7/ 1} == sys_days{days{ 11139}});
static_assert(sys_days{ 2000y/ 7/31} == sys_days{days{ 11169}});
static_assert(sys_days{ 2000y/ 8/ 1} == sys_days{days{ 11170}});
static_assert(sys_days{ 2000y/ 8/31} == sys_days{days{ 11200}});
static_assert(sys_days{ 2000y/ 9/ 1} == sys_days{days{ 11201}});
static_assert(sys_days{ 2000y/ 9/30} == sys_days{days{ 11230}});
static_assert(sys_days{ 2000y/10/ 1} == sys_days{days{ 11231}});
static_assert(sys_days{ 2000y/10/31} == sys_days{days{ 11261}});
static_assert(sys_days{ 2000y/11/ 1} == sys_days{days{ 11262}});
static_assert(sys_days{ 2000y/11/30} == sys_days{days{ 11291}});
static_assert(sys_days{ 2000y/12/ 1} == sys_days{days{ 11292}});
static_assert(sys_days{ 2000y/12/31} == sys_days{days{ 11322}});
static_assert(sys_days{ -400y/ 2/29} == sys_days{days{ -865566}});
static_assert(sys_days{ -400y/ 3/ 1} == sys_days{days{ -865565}});
static_assert(sys_days{ -1y/ 2/28} == sys_days{days{ -719835}});
static_assert(sys_days{ -1y/ 3/ 1} == sys_days{days{ -719834}});
static_assert(sys_days{ -1y/12/31} == sys_days{days{ -719529}});
static_assert(sys_days{ 0y/ 1/ 1} == sys_days{days{ -719528}});
static_assert(sys_days{ 0y/ 2/29} == sys_days{days{ -719469}});
static_assert(sys_days{ 0y/ 3/ 1} == sys_days{days{ -719468}});
static_assert(sys_days{ 1900y/ 3/ 1} == sys_days{days{ -25508}});
static_assert(sys_days{ 1901y/ 2/28} == sys_days{days{ -25144}});
static_assert(sys_days{ 1903y/ 3/ 1} == sys_days{days{ -24413}});
static_assert(sys_days{ 1904y/ 2/29} == sys_days{days{ -24048}});
static_assert(sys_days{ 1996y/ 3/ 1} == sys_days{days{ 9556}});
static_assert(sys_days{ 1997y/ 2/28} == sys_days{days{ 9920}});
static_assert(sys_days{ 1999y/ 3/ 1} == sys_days{days{ 10651}});
static_assert(sys_days{ 2000y/ 2/29} == sys_days{days{ 11016}});
static_assert(sys_days{ 2000y/ 3/ 1} == sys_days{days{ 11017}});
static_assert(sys_days{ 2001y/ 2/28} == sys_days{days{ 11381}});
static_assert(sys_days{ 2003y/ 3/ 1} == sys_days{days{ 12112}});
static_assert(sys_days{ 2004y/ 2/29} == sys_days{days{ 12477}});
static_assert(sys_days{ 2096y/ 3/ 1} == sys_days{days{ 46081}});
static_assert(sys_days{ 2097y/ 2/28} == sys_days{days{ 46445}});
static_assert(sys_days{ 2099y/ 3/ 1} == sys_days{days{ 47176}});
static_assert(sys_days{ 2100y/ 2/28} == sys_days{days{ 47540}});
static_assert(sys_days{-32767y/ 1/ 1} == sys_days{days{-12687428}});
static_assert(sys_days{ 32767y/12/31} == sys_days{days{ 11248737}});

static_assert( 2000y/ 1/ 1 == year_month_day{sys_days{days{ 10957}}});
static_assert( 2000y/ 1/31 == year_month_day{sys_days{days{ 10987}}});
static_assert( 2000y/ 2/ 1 == year_month_day{sys_days{days{ 10988}}});
static_assert( 2000y/ 2/29 == year_month_day{sys_days{days{ 11016}}});
static_assert( 2000y/ 3/ 1 == year_month_day{sys_days{days{ 11017}}});
static_assert( 2000y/ 3/31 == year_month_day{sys_days{days{ 11047}}});
static_assert( 2000y/ 4/ 1 == year_month_day{sys_days{days{ 11048}}});
static_assert( 2000y/ 4/30 == year_month_day{sys_days{days{ 11077}}});
static_assert( 2000y/ 5/ 1 == year_month_day{sys_days{days{ 11078}}});
static_assert( 2000y/ 5/31 == year_month_day{sys_days{days{ 11108}}});
static_assert( 2000y/ 6/ 1 == year_month_day{sys_days{days{ 11109}}});
static_assert( 2000y/ 6/30 == year_month_day{sys_days{days{ 11138}}});
static_assert( 2000y/ 7/ 1 == year_month_day{sys_days{days{ 11139}}});
static_assert( 2000y/ 7/31 == year_month_day{sys_days{days{ 11169}}});
static_assert( 2000y/ 8/ 1 == year_month_day{sys_days{days{ 11170}}});
static_assert( 2000y/ 8/31 == year_month_day{sys_days{days{ 11200}}});
static_assert( 2000y/ 9/ 1 == year_month_day{sys_days{days{ 11201}}});
static_assert( 2000y/ 9/30 == year_month_day{sys_days{days{ 11230}}});
static_assert( 2000y/10/ 1 == year_month_day{sys_days{days{ 11231}}});
static_assert( 2000y/10/31 == year_month_day{sys_days{days{ 11261}}});
static_assert( 2000y/11/ 1 == year_month_day{sys_days{days{ 11262}}});
static_assert( 2000y/11/30 == year_month_day{sys_days{days{ 11291}}});
static_assert( 2000y/12/ 1 == year_month_day{sys_days{days{ 11292}}});
static_assert( 2000y/12/31 == year_month_day{sys_days{days{ 11322}}});
static_assert( -400y/ 2/29 == year_month_day{sys_days{days{ -865566}}});
static_assert( -400y/ 3/ 1 == year_month_day{sys_days{days{ -865565}}});
static_assert( -1y/ 2/28 == year_month_day{sys_days{days{ -719835}}});
static_assert( -1y/ 3/ 1 == year_month_day{sys_days{days{ -719834}}});
static_assert( -1y/12/31 == year_month_day{sys_days{days{ -719529}}});
static_assert( 0y/ 1/ 1 == year_month_day{sys_days{days{ -719528}}});
static_assert( 0y/ 2/29 == year_month_day{sys_days{days{ -719469}}});
static_assert( 0y/ 3/ 1 == year_month_day{sys_days{days{ -719468}}});
static_assert( 1900y/ 3/ 1 == year_month_day{sys_days{days{ -25508}}});
static_assert( 1901y/ 2/28 == year_month_day{sys_days{days{ -25144}}});
static_assert( 1903y/ 3/ 1 == year_month_day{sys_days{days{ -24413}}});
static_assert( 1904y/ 2/29 == year_month_day{sys_days{days{ -24048}}});
static_assert( 1996y/ 3/ 1 == year_month_day{sys_days{days{ 9556}}});
static_assert( 1997y/ 2/28 == year_month_day{sys_days{days{ 9920}}});
static_assert( 1999y/ 3/ 1 == year_month_day{sys_days{days{ 10651}}});
static_assert( 2000y/ 2/29 == year_month_day{sys_days{days{ 11016}}});
static_assert( 2000y/ 3/ 1 == year_month_day{sys_days{days{ 11017}}});
static_assert( 2001y/ 2/28 == year_month_day{sys_days{days{ 11381}}});
static_assert( 2003y/ 3/ 1 == year_month_day{sys_days{days{ 12112}}});
static_assert( 2004y/ 2/29 == year_month_day{sys_days{days{ 12477}}});
static_assert( 2096y/ 3/ 1 == year_month_day{sys_days{days{ 46081}}});
static_assert( 2097y/ 2/28 == year_month_day{sys_days{days{ 46445}}});
static_assert( 2099y/ 3/ 1 == year_month_day{sys_days{days{ 47176}}});
static_assert( 2100y/ 2/28 == year_month_day{sys_days{days{ 47540}}});
static_assert(-32767y/ 1/ 1 == year_month_day{sys_days{days{-12687428}}});
static_assert( 32767y/12/31 == year_month_day{sys_days{days{ 11248737}}});

static_assert(sys_days{ 2000y/ 1/ 0} == sys_days{days{ 10956}});
static_assert(sys_days{ 2000y/ 1/255} == sys_days{days{ 11211}});
static_assert(sys_days{ 2000y/ 2/ 0} == sys_days{days{ 10987}});
static_assert(sys_days{ 2000y/ 2/255} == sys_days{days{ 11242}});
static_assert(sys_days{ 2000y/ 3/ 0} == sys_days{days{ 11016}});
static_assert(sys_days{ 2000y/ 3/255} == sys_days{days{ 11271}});
static_assert(sys_days{ 2000y/ 4/ 0} == sys_days{days{ 11047}});
static_assert(sys_days{ 2000y/ 4/255} == sys_days{days{ 11302}});
static_assert(sys_days{ 2000y/ 5/ 0} == sys_days{days{ 11077}});
static_assert(sys_days{ 2000y/ 5/255} == sys_days{days{ 11332}});
static_assert(sys_days{ 2000y/ 6/ 0} == sys_days{days{ 11108}});
static_assert(sys_days{ 2000y/ 6/255} == sys_days{days{ 11363}});
static_assert(sys_days{ 2000y/ 7/ 0} == sys_days{days{ 11138}});
static_assert(sys_days{ 2000y/ 7/255} == sys_days{days{ 11393}});
static_assert(sys_days{ 2000y/ 8/ 0} == sys_days{days{ 11169}});
static_assert(sys_days{ 2000y/ 8/255} == sys_days{days{ 11424}});
static_assert(sys_days{ 2000y/ 9/ 0} == sys_days{days{ 11200}});
static_assert(sys_days{ 2000y/ 9/255} == sys_days{days{ 11455}});
static_assert(sys_days{ 2000y/10/ 0} == sys_days{days{ 11230}});
static_assert(sys_days{ 2000y/10/255} == sys_days{days{ 11485}});
static_assert(sys_days{ 2000y/11/ 0} == sys_days{days{ 11261}});
static_assert(sys_days{ 2000y/11/255} == sys_days{days{ 11516}});
static_assert(sys_days{ 2000y/12/ 0} == sys_days{days{ 11291}});
static_assert(sys_days{ 2000y/12/255} == sys_days{days{ 11546}});
static_assert(sys_days{ -400y/ 2/255} == sys_days{days{ -865340}});
static_assert(sys_days{ -400y/ 3/ 0} == sys_days{days{ -865566}});
static_assert(sys_days{ -1y/ 2/255} == sys_days{days{ -719608}});
static_assert(sys_days{ -1y/ 3/ 0} == sys_days{days{ -719835}});
static_assert(sys_days{ -1y/12/255} == sys_days{days{ -719305}});
static_assert(sys_days{ 0y/ 1/ 0} == sys_days{days{ -719529}});
static_assert(sys_days{ 0y/ 2/255} == sys_days{days{ -719243}});
static_assert(sys_days{ 0y/ 3/ 0} == sys_days{days{ -719469}});
static_assert(sys_days{ 1900y/ 3/ 0} == sys_days{days{ -25509}});
static_assert(sys_days{ 1901y/ 2/255} == sys_days{days{ -24917}});
static_assert(sys_days{ 1903y/ 3/ 0} == sys_days{days{ -24414}});
static_assert(sys_days{ 1904y/ 2/255} == sys_days{days{ -23822}});
static_assert(sys_days{ 1996y/ 3/ 0} == sys_days{days{ 9555}});
static_assert(sys_days{ 1997y/ 2/255} == sys_days{days{ 10147}});
static_assert(sys_days{ 1999y/ 3/ 0} == sys_days{days{ 10650}});
static_assert(sys_days{ 2000y/ 2/255} == sys_days{days{ 11242}});
static_assert(sys_days{ 2000y/ 3/ 0} == sys_days{days{ 11016}});
static_assert(sys_days{ 2001y/ 2/255} == sys_days{days{ 11608}});
static_assert(sys_days{ 2003y/ 3/ 0} == sys_days{days{ 12111}});
static_assert(sys_days{ 2004y/ 2/255} == sys_days{days{ 12703}});
static_assert(sys_days{ 2096y/ 3/ 0} == sys_days{days{ 46080}});
static_assert(sys_days{ 2097y/ 2/255} == sys_days{days{ 46672}});
static_assert(sys_days{ 2099y/ 3/ 0} == sys_days{days{ 47175}});
static_assert(sys_days{ 2100y/ 2/255} == sys_days{days{ 47767}});
static_assert(sys_days{-32767y/ 1/ 0} == sys_days{days{-12687429}});
static_assert(sys_days{ 32767y/12/255} == sys_days{days{ 11248961}});
// clang-format on
} else {
sys_days sys2{year{y_min} / 1 / 1};

for (int iy = y_min; iy <= y_max; ++iy) {
const year y{iy};

for (auto um = 1u; um <= 12u; ++um) {
const month m{um};

const year_month_day ymd_first = y / m / 1;
assert(sys_days{ymd_first} == sys2);
assert(year_month_day{sys2} == ymd_first);

const year_month_day ymd_min = y / m / 0;
assert(sys_days{ymd_min} == sys2 - days{1});

const year_month_day ymd_max = y / m / 255;
assert(sys_days{ymd_max} == sys2 + days{254});

const year_month_day ymd_last = y / m / last;
sys2 += (ymd_last.day() - 1d);
assert(sys_days{ymd_last} == sys2);
assert(year_month_day{sys2} == ymd_last);

sys2 += days{1};
}
}
}
}

constexpr void year_month_day_last_test() {
Expand Down