MT Core (C++)
Core library for replacing C++ standard in project usage
Loading...
Searching...
No Matches
gregorian.hpp
Go to the documentation of this file.
1/*
2
3Copyright 2025 Matthew Tolman
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16
17*/
18
19#ifndef MTCORE_CALENDARS_GREGORIAN_HPP
20#define MTCORE_CALENDARS_GREGORIAN_HPP
21
25#include <string_view>
26
28 struct Gregorian {
29 static constexpr auto EPOCH = GREGORIAN_EPOCH;
30 static constexpr std::string_view name = "Gregorian";
31
46
48 Month month = Month::January;
49 u8 day = 1;
50
52 this->year = AstronomicalYear{year};
53 this->month = month;
54 this->day = day;
55 }
56
57 [[nodiscard]] constexpr std::strong_ordering operator<=>(const Gregorian &g) const {
58 if (const auto c = year <=> g.year; c != std::strong_ordering::equivalent) {
59 return c;
60 }
61 if (const auto c = month <=> g.month; c != std::strong_ordering::equivalent) {
62 return c;
63 }
64 return day <=> g.day;
65 }
66
67 [[nodiscard]] static constexpr bool is_leap_year(const AstronomicalYear year) {
68 const auto y = static_cast<YearBase>(year);
69 const auto yearMod400 = math::mod(y, 400);
70 return math::mod(y, 4) == 0 && yearMod400 != 100 && yearMod400 != 200 && yearMod400 != 300;
71 }
72
73 [[nodiscard]] constexpr bool leap_year() const { return is_leap_year(this->year); }
74
75 [[nodiscard]] static constexpr Gregorian year_start_for(const AstronomicalYear year) {
76 return {.year = year, .month = Month::January, .day = 1};
77 }
78
79 [[nodiscard]] constexpr Gregorian year_start() const { return year_start_for(this->year); }
80
81 [[nodiscard]] static constexpr Gregorian year_end_for(const AstronomicalYear year) {
82 return {.year = year, .month = Month::December, .day = 31};
83 }
84
85 [[nodiscard]] constexpr Gregorian year_end() const { return year_start_for(this->year); }
86
87 [[nodiscard]] constexpr Fixed to_fixed() const {
88 return Fixed{.day = EPOCH - 1 + 365 * (static_cast<YearBase>(year) - 1) +
89 math::floor<double, i32>(static_cast<double>(static_cast<YearBase>(year) - 1) / 4.0) -
90 math::floor<double, i32>(static_cast<double>(static_cast<YearBase>(year) - 1) / 100.0) +
91 math::floor<double, i32>(static_cast<double>(static_cast<YearBase>(year) - 1) / 400.0) +
92 math::floor<double, i32>((367. * static_cast<double>(month) - 362.) / 12.0) +
93 (static_cast<i32>(month) <= 2 ? 0
94 : is_leap_year(year) ? -1
95 : -2) +
96 static_cast<i32>(day)};
97 }
98
99 [[nodiscard]] static constexpr Gregorian from_fixed(const Fixed &f) {
100 const auto year = year_from_fixed(f);
101 const auto yearStart = year_start_for(year);
102 const auto yearStartRdDate = yearStart.to_fixed();
103 const auto priorDays = f.day_difference(yearStartRdDate);
104 const auto marchFirst = Gregorian{year, Month::March, 1};
105 const auto marchFirstRdDate = marchFirst.to_fixed();
106 const auto correction = (f <=> marchFirstRdDate) == std::strong_ordering::less ? 0 : is_leap_year(year) ? 1 : 2;
107 const auto month = math::floor<double, Month>(static_cast<double>(12 * (priorDays + correction) + 373) / 367.0);
108 const auto firstOfMonth = Gregorian{year, month, 1};
109 const auto firstOfMonthRdDate = firstOfMonth.to_fixed();
110 return Gregorian{year, month, static_cast<u8>(f.day_difference(firstOfMonthRdDate) + 1)};
111 }
112
113 [[nodiscard]] constexpr u16 days_in_year() const { return days_in_year_for(year); }
114
115 [[nodiscard]] static constexpr u16 days_in_year_for(const AstronomicalYear year) {
116 if (is_leap_year(year)) {
117 return 366;
118 }
119 return 365;
120 }
121
122 [[nodiscard]] constexpr u8 days_in_month() const { return days_in_month_for(year, month); }
123
124 [[nodiscard]] static constexpr u8 days_in_month_for(const AstronomicalYear year, const Month month) {
125 switch (month) {
126 case Month::January:
127 case Month::March:
128 case Month::May:
129 case Month::July:
130 case Month::August:
131 case Month::October:
132 case Month::December:
133 return 31;
134 case Month::February:
135 return is_leap_year(year) ? 29 : 28;
136 case Month::April:
137 case Month::June:
138 case Month::September:
139 case Month::November:
140 return 30;
141 }
142 unreachable("BAD MONTH");
143 }
144
145 private:
146 [[nodiscard]] static constexpr AstronomicalYear year_from_fixed(const Fixed &f) {
147 const auto d0 = f.sub_days(EPOCH).day;
148 // 146097 = number of days in 400 years (adjusted for leap years)
149 const auto n400 = math::floor<double, int64_t>(static_cast<double>(d0) / 146097.0);
150 const auto d1 = math::mod(d0, 146097);
151 // 36524 = number of days in 100 years (adjusted for leap years)
152 const auto n100 = math::floor<double, int64_t>(static_cast<double>(d1) / 36524.0);
153 const auto d2 = math::mod(d1, 36524);
154 // 1461 = number of days in 4 years (adjusted for leap years)
155 const auto n4 = math::floor<double, int64_t>(static_cast<double>(d2) / 1461.0);
156 const auto d3 = math::mod(d2, 1461);
157 const auto n1 = math::floor<double, int64_t>(static_cast<double>(d3) / 365.0);
158 const auto year = 400 * n400 + 100 * n100 + 4 * n4 + n1 + 1 -
159 static_cast<int64_t>(static_cast<unsigned>(n100 == 4) | static_cast<unsigned>(n1 == 4));
160 return year;
161 }
162 };
163
164 static_assert(IsDayCalendarSystem<Gregorian>);
165} // namespace mtcore::calendars::systems
166
167#endif // MTCORE_CALENDARS_GREGORIAN_HPP
#define unreachable(...)
Marks code as unreachable.
constexpr R floor(T num) noexcept
Floors a number with support for constexpr and fast runtime compilation.
constexpr Res mod(L left, R right) noexcept
Calculates the mathematical mod of two numbers.
int32_t i32
Alias for 32-bit ints.
uint8_t u8
Alias for 8-bit unsigned ints.
uint16_t u16
Alias for 16-bit unsigned ints.
constexpr i32 GREGORIAN_EPOCH
Definition epochs.hpp:26
Base calendar system tracking the number of days since its epoch.
Definition fixed.hpp:38
constexpr Fixed sub_days(const i32 days) const
Subtracts n days from a date.
Definition fixed.hpp:90
constexpr i32 day_difference(const Fixed &other) const
Difference between two dates in days.
Definition fixed.hpp:93
constexpr Gregorian year_start() const
Definition gregorian.hpp:79
constexpr bool leap_year() const
Definition gregorian.hpp:73
void init(i32 year, Month month, u8 day)
Definition gregorian.hpp:51
static constexpr u16 days_in_year_for(const AstronomicalYear year)
constexpr Fixed to_fixed() const
Definition gregorian.hpp:87
static constexpr u8 days_in_month_for(const AstronomicalYear year, const Month month)
static constexpr Gregorian year_end_for(const AstronomicalYear year)
Definition gregorian.hpp:81
constexpr std::strong_ordering operator<=>(const Gregorian &g) const
Definition gregorian.hpp:57
constexpr Gregorian year_end() const
Definition gregorian.hpp:85
static constexpr Gregorian year_start_for(const AstronomicalYear year)
Definition gregorian.hpp:75
static constexpr std::string_view name
Definition gregorian.hpp:30
static constexpr bool is_leap_year(const AstronomicalYear year)
Definition gregorian.hpp:67
static constexpr Gregorian from_fixed(const Fixed &f)
Definition gregorian.hpp:99