Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Date.inl
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4
5namespace Stroika::Foundation::Time {
6
7 enum class [[deprecated ("Since Stroika v3.0d1, unused and use std::chrono")]] DayOfYear : uint16_t {
8 eFirstDayOfYear = 1,
9 eLastDayOfYear = 366,
10
11 Stroika_Define_Enum_Bounds (eFirstDayOfYear, eLastDayOfYear)
12 };
13
14 /*
15 ********************************************************************************
16 ********************************** DayOfWeek ***********************************
17 ********************************************************************************
18 */
19 constexpr DayOfWeek::DayOfWeek (weekday w, DataExchange::ValidationStrategy validationStrategy)
20 : weekday{w}
21 {
22 // stdc++ allows this to contain any number 1..255 (queer)
23 if (validationStrategy == DataExchange::ValidationStrategy::eThrow) {
24 if (not ok ()) {
25 Execution::Throw (Date::FormatException::kThe);
26 }
27 }
28 Require (ok ());
29 }
30 constexpr DayOfWeek::DayOfWeek (unsigned int w, DataExchange::ValidationStrategy validationStrategy)
31 : weekday{w}
32 {
33 if (validationStrategy == DataExchange::ValidationStrategy::eThrow) {
34 if (not ok ()) {
35 Execution::Throw (Date::FormatException::kThe);
36 }
37 }
38 Require (ok ());
39 }
40
41 /*
42 ********************************************************************************
43 ********************************** MonthOfYear *********************************
44 ********************************************************************************
45 */
46 constexpr MonthOfYear::MonthOfYear (month m, DataExchange::ValidationStrategy validationStrategy)
47 : month{m}
48 {
49 // stdc++ allows this to contain any number 1..255 (queer)
50 if (validationStrategy == DataExchange::ValidationStrategy::eThrow) {
51 if (not ok ()) {
52 Execution::Throw (Date::FormatException::kThe);
53 }
54 }
55 Require (ok ());
56 }
57 constexpr MonthOfYear::MonthOfYear (unsigned int m, DataExchange::ValidationStrategy validationStrategy)
58 : month{m}
59 {
60 if (validationStrategy == DataExchange::ValidationStrategy::eThrow) {
61 if (not ok ()) {
62 Execution::Throw (Date::FormatException::kThe);
63 }
64 }
65 Require (ok ());
66 }
67
68 /*
69 ********************************************************************************
70 ********************************** DayOfMonth **********************************
71 ********************************************************************************
72 */
73 constexpr DayOfMonth::DayOfMonth (day d, DataExchange::ValidationStrategy validationStrategy)
74 : day{d}
75 {
76 // stdc++ allows this to contain any number 1..255 (queer)
77 if (validationStrategy == DataExchange::ValidationStrategy::eThrow) {
78 if (not ok ()) {
79 Execution::Throw (Date::FormatException::kThe);
80 }
81 }
82 Require (ok ());
83 }
84 constexpr DayOfMonth::DayOfMonth (unsigned int d, DataExchange::ValidationStrategy validationStrategy)
85 : day{d}
86 {
87 if (validationStrategy == DataExchange::ValidationStrategy::eThrow) {
88 if (not ok ()) {
89 Execution::Throw (Date::FormatException::kThe);
90 }
91 }
92 Require (ok ());
93 }
94
95 /*
96 ********************************************************************************
97 ************************************ Year **************************************
98 ********************************************************************************
99 */
100 constexpr Year::Year (year y, DataExchange::ValidationStrategy validationStrategy)
101 : year{y}
102 {
103 if (validationStrategy == DataExchange::ValidationStrategy::eThrow) {
104 if (not ok ()) {
105 Execution::Throw (Date::FormatException::kThe);
106 }
107 }
108 Require (ok ());
109 }
110 constexpr Year::Year (int y, DataExchange::ValidationStrategy validationStrategy)
111 : year{y}
112 {
113 if (validationStrategy == DataExchange::ValidationStrategy::eThrow) {
114 if (not ok ()) {
115 Execution::Throw (Date::FormatException::kThe);
116 }
117 }
118 Require (ok ());
119 }
120
121 /*
122 ********************************************************************************
123 ********************************* IsLeapYear ***********************************
124 ********************************************************************************
125 */
126 [[deprecated ("Since Stroika v3.0d1, use year{}.is_leap ()")]] inline bool IsLeapYear (Year y)
127 {
128 return y.is_leap ();
129 }
130 [[deprecated ("Since Stroika v3.0d1, use year{}.is_leap ()")]] inline bool IsLeapYear (int y)
131 {
132 return Year{y}.is_leap ();
133 }
134
135 /*
136 ********************************************************************************
137 *************************************** Date ***********************************
138 ********************************************************************************
139 */
140 constexpr inline Date::JulianDayNumber Date::ToJulianRep (month m, day d, year y, DataExchange::ValidationStrategy validationStrategy)
141 {
142 if (validationStrategy == DataExchange::ValidationStrategy::eThrow) {
143 if (not m.ok () or not d.ok () or not y.ok () or y / m / d < kMinDateReference.fYMD) {
144 Execution::Throw (FormatException::kThe);
145 }
146 }
147 Require (y.ok () and m.ok () and d.ok ());
148 Require (y / m / d >= kMinDateReference.fYMD);
149
150 // @todo http://stroika-bugs.sophists.com/browse/STK-976 - improve precision of map to/from JulianDateRep
151
152 JulianDayNumber result = [&] () noexcept -> JulianDayNumber {
153 if (y / m / d >= kGregorianCalendarEpoch.fYMD) {
154 return static_cast<JulianDayNumber> ((chrono::sys_days{y / m / d} - chrono::sys_days{kGregorianCalendarEpoch.fYMD}).count ()) +
155 kGregorianCalendarEpoch.fJulianRep;
156 }
157 return static_cast<JulianDayNumber> ((chrono::sys_days{y / m / d} - chrono::sys_days{kStartOfJulianCalendar.fYMD}).count ()) +
158 kStartOfJulianCalendar.fJulianRep;
159 }();
160
161 if (not is_constant_evaluated ()) {
162 [[maybe_unused]] JulianDayNumber stroikav21Algorithm = [&] () noexcept -> JulianDayNumber {
163 // Do arithmatic as integer representations, not 'datetime' reps that do funny things like wrap
164 // using m - months, etc..
165 unsigned int mm{m};
166 unsigned int dd{d};
167 int yy{y};
168 /*
169 * Convert Gregorian calendar date to the corresponding Julian day number
170 * j. Algorithm 199 from Communications of the ACM, Volume 6, No. 8,
171 * (Aug. 1963), p. 444. Gregorian calendar started on Sep. 14, 1752.
172 * This function not valid before that.
173 *
174 * (This code originally from NIHCL)
175 */
176 if (mm > 2) {
177 mm = mm - 3;
178 }
179 else {
180 mm = mm + 9;
181 --yy;
182 }
183 Date::JulianDayNumber c = yy / 100;
184 Date::JulianDayNumber ya = yy - 100 * c;
185 return ((146097 * c) >> 2) + ((1461 * ya) >> 2) + (153 * mm + 2) / 5 + dd + 1721119;
186 }();
187 Assert (result == stroikav21Algorithm or (y / m / d < kGregorianCalendarEpoch.fYMD));
188 }
189 return result;
190 }
191 constexpr auto Date::ToJulianRep (year_month_day ymd, DataExchange::ValidationStrategy validationStrategy) -> JulianDayNumber
192 {
193 return ToJulianRep (ymd.month (), ymd.day (), ymd.year (), validationStrategy);
194 }
195 constexpr year_month_day Date::FromJulianRep (JulianDayNumber jr, DataExchange::ValidationStrategy validationStrategy)
196 {
197 if (validationStrategy == DataExchange::ValidationStrategy::eThrow) {
198 if (not(kMinJulianRep <= jr and jr <= kMaxJulianRep)) {
199 Execution::Throw (FormatException::kThe);
200 }
201 }
202 Require (kMinJulianRep <= jr and jr <= kMaxJulianRep);
203
204 // @todo http://stroika-bugs.sophists.com/browse/STK-976 - improve precision of map to/from JulianDateRep
205 year_month_day result = [&] () noexcept -> year_month_day {
206 if (jr >= kGregorianCalendarEpoch.fJulianRep) {
207 return chrono::sys_days{kGregorianCalendarEpoch.fYMD} + days{(jr - kGregorianCalendarEpoch.fJulianRep)};
208 }
209 return chrono::sys_days{kStartOfJulianCalendar.fYMD} + days{(jr - kStartOfJulianCalendar.fJulianRep)};
210 }();
211
212 if (not is_constant_evaluated ()) {
213 [[maybe_unused]] year_month_day legacyValue = [&] () {
214 /*
215 * Convert a Julian day number to its corresponding Gregorian calendar
216 * date. Algorithm 199 from Communications of the ACM, Volume 6, No. 8,
217 * (Aug. 1963), p. 444. Gregorian calendar started on Sep. 14, 1752.
218 * This function not valid before that.
219 *
220 * (This code originally from NIHCL)
221 */
222 JulianDayNumber m;
223 JulianDayNumber d;
224 JulianDayNumber y;
225 // A reference for this formula (not original I used) - can be found at:
226 // http://aa.usno.navy.mil/faq/docs/JD_Formula.php
227 // at least close, and I could probably switch to that...
228 //
229 JulianDayNumber j = jr - 1721119;
230 y = (((j << 2) - 1) / 146097);
231 j = (j << 2) - 1 - 146097 * y;
232 d = (j >> 2);
233 j = ((d << 2) + 3) / 1461;
234 d = ((d << 2) + 3 - 1461 * j);
235 d = (d + 4) >> 2;
236 m = (5 * d - 3) / 153;
237 d = 5 * d - 3 - 153 * m;
238 d = (d + 5) / 5;
239 y = (100 * y + j);
240 if (m < 10) {
241 m += 3;
242 }
243 else {
244 m -= 9;
245 ++y;
246 }
247 return year_month_day{Year{static_cast<int> (y)}, MonthOfYear{m}, DayOfMonth{d}};
248 }();
249 Assert (result == legacyValue or jr < kGregorianCalendarEpoch.fJulianRep);
250 }
251 return result;
252 }
253 inline constexpr Date::JulianDayNumber Date::kMaxJulianRep = Date::ToJulianRep (December, 31d, Year::eLastYear);
254 static_assert (Date::ToJulianRep (Date::kGregorianCalendarEpoch.fYMD) == Date::kGregorianCalendarEpoch.fJulianRep); // not important, but if that ever failed, would indicate serious bug or we changed definition
255
256 inline constexpr Date::Date (JulianDayNumber julianRep, DataExchange::ValidationStrategy validationStrategy)
257 : fRep_{FromJulianRep (julianRep, validationStrategy)}
258 {
259 }
260 constexpr inline Date::Date (year_month_day ymd, DataExchange::ValidationStrategy validationStrategy)
261 : fRep_{ymd}
262 {
263 (void)ToJulianRep (ymd, validationStrategy); // this is overkill, and we do be slightly more efficient - just for side-effect of checks
264 }
265 constexpr inline Date::Date (year y, month m, day d, DataExchange::ValidationStrategy validationStrategy)
266 : Date{year_month_day{y, m, d}, validationStrategy}
267 {
268 }
269 template <>
270 constexpr year_month_day Date::As () const
271 {
272 return fRep_;
273 }
274 template <>
275 constexpr ::tm Date::As () const
276 {
277 ::tm tm{};
278 tm.tm_year = static_cast<int> (GetYear ()) - kTM_Year_RelativeToYear_;
279 tm.tm_mon = static_cast<unsigned int> (GetMonth ()) - 1;
280 tm.tm_mday = static_cast<unsigned int> (GetDayOfMonth ());
281 return tm;
282 }
283 inline constexpr Date::JulianDayNumber Date::GetJulianRep () const
284 {
285 return ToJulianRep (fRep_);
286 }
287 inline constexpr year Date::GetYear () const
288 {
289 return fRep_.year ();
290 }
291 inline constexpr month Date::GetMonth () const
292 {
293 Ensure (January <= fRep_.month () and fRep_.month () <= December);
294 return fRep_.month ();
295 }
296 inline constexpr day Date::GetDayOfMonth () const
297 {
298 Ensure (1d <= fRep_.day () and fRep_.day () <= 31d);
299 return fRep_.day ();
300 }
301 inline Date Date::Parse (const String& rep, const locale& l)
302 {
303 return Date::Parse_ (rep, l, kDefaultParseFormats, nullptr);
304 }
305 inline Date Date::Parse (const String& rep, const locale& l, const Traversal::Iterable<String>& formatPatterns)
306 {
307 return Date::Parse_ (rep, l, formatPatterns, nullptr);
308 }
309 inline Date Date::Parse (const String& rep, const locale& l, const Traversal::Iterable<String>& formatPatterns, size_t* consumedCharsInStringUpTo)
310 {
311 RequireNotNull (consumedCharsInStringUpTo);
312 return Date::Parse_ (rep, l, formatPatterns, consumedCharsInStringUpTo);
313 }
314 inline Date Date::Parse (const String& rep, const locale& l, size_t* consumedCharsInStringUpTo)
315 {
316 RequireNotNull (consumedCharsInStringUpTo);
317 return Date::Parse_ (rep, l, kDefaultParseFormats, consumedCharsInStringUpTo);
318 }
319 inline Date Date::Parse (const String& rep, const locale& l, const String& formatPattern)
320 {
321 if (rep.empty ()) {
322 Execution::Throw (FormatException::kThe);
323 }
324 wstring wRep = rep.As<wstring> ();
325 const time_get<wchar_t>& tmget = use_facet<time_get<wchar_t>> (l);
326 if (auto r = ParseQuietly_ (wRep, tmget, formatPattern, nullptr)) {
327 return *r;
328 }
329 Execution::Throw (FormatException::kThe);
330 }
331 inline Date Date::Parse (const String& rep, const locale& l, const String& formatPattern, size_t* consumedCharsInStringUpTo)
332 {
333 RequireNotNull (consumedCharsInStringUpTo);
334 if (rep.empty ()) {
335 Execution::Throw (FormatException::kThe);
336 }
337 wstring wRep = rep.As<wstring> ();
338 const time_get<wchar_t>& tmget = use_facet<time_get<wchar_t>> (l);
339 if (auto r = ParseQuietly_ (wRep, tmget, formatPattern, consumedCharsInStringUpTo)) {
340 return *r;
341 }
342 Execution::Throw (FormatException::kThe);
343 }
344 inline Date Date::Parse (const String& rep, const Traversal::Iterable<String>& formatPatterns)
345 {
346 return Date::Parse_ (rep, locale::classic (), formatPatterns, nullptr);
347 }
348 inline Date Date::Parse (const String& rep, const Traversal::Iterable<String>& formatPatterns, size_t* consumedCharsInStringUpTo)
349 {
350 RequireNotNull (consumedCharsInStringUpTo);
351 return Date::Parse_ (rep, locale::classic (), formatPatterns, consumedCharsInStringUpTo);
352 }
353 inline Date Date::Parse (const String& rep, const String& formatPattern)
354 {
355 if (rep.empty ()) {
356 Execution::Throw (FormatException::kThe);
357 }
358 wstring wRep = rep.As<wstring> ();
359 const time_get<wchar_t>& tmget = use_facet<time_get<wchar_t>> (locale::classic ());
360 if (auto r = ParseQuietly_ (wRep, tmget, formatPattern, nullptr)) {
361 return *r;
362 }
363 Execution::Throw (FormatException::kThe);
364 }
365 inline Date Date::Parse (const String& rep, const String& formatPattern, size_t* consumedCharsInStringUpTo)
366 {
367 RequireNotNull (consumedCharsInStringUpTo);
368 if (rep.empty ()) {
369 Execution::Throw (FormatException::kThe);
370 }
371 wstring wRep = rep.As<wstring> ();
372 const time_get<wchar_t>& tmget = use_facet<time_get<wchar_t>> (locale::classic ());
373 if (auto r = ParseQuietly_ (wRep, tmget, formatPattern, consumedCharsInStringUpTo)) {
374 return *r;
375 }
376 Execution::Throw (FormatException::kThe);
377 }
378 inline optional<Date> Date::ParseQuietly (const String& rep, const String& formatPattern)
379 {
380 return ParseQuietly (rep, locale::classic (), formatPattern);
381 }
382 inline optional<Date> Date::ParseQuietly (const String& rep, const locale& l, const String& formatPattern)
383 {
384 if (rep.empty ()) {
385 return nullopt;
386 }
387 return ParseQuietly_ (rep.As<wstring> (), use_facet<time_get<wchar_t>> (l), formatPattern, nullptr);
388 }
389 inline String Date::ToString () const
390 {
391 return Format ();
392 }
393 [[nodiscard]] inline Date Date::Add (int dayCount) const
394 {
395 return Add (days{dayCount});
396 }
397 inline Date Date::operator+ (int daysOffset) const
398 {
399 return this->Add (daysOffset);
400 }
401 inline Date Date::operator+ (days daysOffset) const
402 {
403 return this->Add (daysOffset);
404 }
405 inline Date Date::operator+ (const Duration& d) const
406 {
407 return this->Add (d);
408 }
409 inline days Date::Since (Date dStart, Date dEnd)
410 {
411 if (dStart < dEnd) {
412 return dEnd - dStart;
413 }
414 return days{0};
415 }
416 inline auto Date::Difference (const Date& rhs) const -> days
417 {
418 return chrono::sys_days{fRep_} - chrono::sys_days{rhs.fRep_};
419 }
420 inline auto Date::operator- (const Date& rhs) const -> days
421 {
422 return Difference (rhs);
423 }
424 inline Date Date::operator- (days daysOffset) const
425 {
426 return Add (-daysOffset);
427 }
428 inline Date Date::operator- (int daysOffset) const
429 {
430 return Add (-daysOffset);
431 }
432 inline constexpr Date Date::kMin{Date::kMinJulianRep};
433 inline constexpr Date Date::kMax{Date::kMaxJulianRep};
434
435}
436
438 template <>
439 constexpr EnumNames<Stroika::Foundation::Time::Date::NonStandardPrintFormat> DefaultNames<Stroika::Foundation::Time::Date::NonStandardPrintFormat>::k{{{
440 {Stroika::Foundation::Time::Date::NonStandardPrintFormat::eCurrentLocale_WithZerosStripped, L"Current-Locale-With-Zeros-Stripped"},
441 }}};
442}
443
445
446 inline constexpr Time::Date Default<Time::Date>::kLowerBound{Time::Date::kMin};
447 inline constexpr Time::Date Default<Time::Date>::kUpperBound{Time::Date::kMax};
448
449 /// need getNext/GetPrev here excelt if causes constexpr issues
450 inline Time::Date Default<Time::Date>::GetNext (Time::Date n)
451 {
452 return n + 1;
453 }
454 inline Time::Date Default<Time::Date>::GetPrevious (Time::Date n)
455 {
456 return n - 1;
457 }
458
459};
#define RequireNotNull(p)
Definition Assertions.h:347
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
nonvirtual days Difference(const Date &rhs) const
Returns the difference (*this - rhs) between the two Date records;.
Definition Date.inl:416
nonvirtual String Format(NonStandardPrintFormat pf=NonStandardPrintFormat::eDEFAULT) const
Definition Date.cpp:119
nonvirtual days operator-(const Date &rhs) const
Definition Date.inl:420
nonvirtual Date operator+(int daysOffset) const
Syntactic sure for Add (n);.
Definition Date.inl:397
static constexpr ReferencePoint kGregorianCalendarEpoch
Definition Date.h:376
static optional< Date > ParseQuietly(const String &rep, const String &formatPattern)
like Parse(), but returns nullopt on parse error, not throwing exception. if locale is missing,...
Definition Date.inl:378
static const JulianDayNumber kMinJulianRep
Definition Date.h:413
static const Date kMax
Definition Date.h:548
static const JulianDayNumber kMaxJulianRep
Definition Date.h:419
static constexpr ReferencePoint kMinDateReference
Definition Date.h:383
static Date Parse(const String &rep, const locale &l=locale{})
Definition Date.inl:301
nonvirtual String ToString() const
Definition Date.inl:389
static constexpr JulianDayNumber ToJulianRep(month m, day d, year y, DataExchange::ValidationStrategy validationStrategy=DataExchange::ValidationStrategy::eAssertion)
Definition Date.inl:140
static constexpr ReferencePoint kStartOfJulianCalendar
Definition Date.h:360
static constexpr year_month_day FromJulianRep(JulianDayNumber j, DataExchange::ValidationStrategy validationStrategy=DataExchange::ValidationStrategy::eAssertion)
Definition Date.inl:195
static const Date kMin
Definition Date.h:542
nonvirtual constexpr JulianDayNumber GetJulianRep() const
return the Julian Day Number (JDN) - corresponding to this date object (https://en....
Definition Date.inl:283
constexpr Date(Date &&src) noexcept=default
static const Traversal::Iterable< String > kDefaultParseFormats
Definition Date.h:494
nonvirtual Date Add(int d) const
Definition Date.inl:393
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43
Simple wrapper on std::chrono::day, with some helpful validation properties (assures constructed 'ok'...
Definition Date.h:163
constexpr DayOfMonth(day d, DataExchange::ValidationStrategy validationStrategy=DataExchange::ValidationStrategy::eAssertion)
Definition Date.inl:73
constexpr DayOfWeek(weekday w, DataExchange::ValidationStrategy validationStrategy=DataExchange::ValidationStrategy::eAssertion)
Definition Date.inl:19
Simple wrapper on std::chrono::month, with some helpful validation properties (assures constructed 'o...
Definition Date.h:131
constexpr MonthOfYear(month m, DataExchange::ValidationStrategy validationStrategy=DataExchange::ValidationStrategy::eAssertion)
Definition Date.inl:46
Simple wrapper on std::chrono::year, with some helpful validation properties (assures constructed 'ok...
Definition Date.h:215
static constexpr year eLastYear
Definition Date.h:237