Stroika Library 3.0d18
 
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 <typename T>
270 constexpr T Date::As () const
271 requires (Common::IAnyOf<::tm, tm, year_month_day> or Common::ITimePoint<T>)
272 {
273 if constexpr (same_as<T, ::tm>) {
274 ::tm tm{};
275 tm.tm_year = static_cast<int> (GetYear ()) - kTM_Year_RelativeToYear_;
276 tm.tm_mon = static_cast<unsigned int> (GetMonth ()) - 1;
277 tm.tm_mday = static_cast<unsigned int> (GetDayOfMonth ());
278 return tm;
279 }
280 else if constexpr (same_as<T, year_month_day>) {
281 return fRep_;
282 }
283 else if constexpr (Common::ITimePoint<T>) {
284 // note: only supports UNIX epoch
285 using CLOCK_T = typename T::clock;
286 using DURATION_T = typename T::duration;
287 ::tm asTM = this->As<::tm> ();
288 time_t thisTimeT = mktime (&asTM);
289 auto t = Time::clock_cast<CLOCK_T> (chrono::system_clock::from_time_t (thisTimeT));
290 if constexpr (same_as<DURATION_T, chrono::system_clock::duration>) {
291 return t;
292 }
293 else {
294 return chrono::time_point_cast<DURATION_T> (t);
295 }
296 }
297 }
298 inline constexpr Date::JulianDayNumber Date::GetJulianRep () const
299 {
300 return ToJulianRep (fRep_);
301 }
302 inline constexpr year Date::GetYear () const
303 {
304 return fRep_.year ();
305 }
306 inline constexpr month Date::GetMonth () const
307 {
308 Ensure (January <= fRep_.month () and fRep_.month () <= December);
309 return fRep_.month ();
310 }
311 inline constexpr day Date::GetDayOfMonth () const
312 {
313 Ensure (1d <= fRep_.day () and fRep_.day () <= 31d);
314 return fRep_.day ();
315 }
316 inline Date Date::Parse (const String& rep, const locale& l)
317 {
318 return Date::Parse_ (rep, l, kDefaultParseFormats, nullptr);
319 }
320 inline Date Date::Parse (const String& rep, const locale& l, const Traversal::Iterable<String>& formatPatterns)
321 {
322 return Date::Parse_ (rep, l, formatPatterns, nullptr);
323 }
324 inline Date Date::Parse (const String& rep, const locale& l, const Traversal::Iterable<String>& formatPatterns, size_t* consumedCharsInStringUpTo)
325 {
326 RequireNotNull (consumedCharsInStringUpTo);
327 return Date::Parse_ (rep, l, formatPatterns, consumedCharsInStringUpTo);
328 }
329 inline Date Date::Parse (const String& rep, const locale& l, size_t* consumedCharsInStringUpTo)
330 {
331 RequireNotNull (consumedCharsInStringUpTo);
332 return Date::Parse_ (rep, l, kDefaultParseFormats, consumedCharsInStringUpTo);
333 }
334 inline Date Date::Parse (const String& rep, const locale& l, const String& formatPattern)
335 {
336 if (rep.empty ()) {
337 Execution::Throw (FormatException::kThe);
338 }
339 wstring wRep = rep.As<wstring> ();
340 const time_get<wchar_t>& tmget = use_facet<time_get<wchar_t>> (l);
341 if (auto r = ParseQuietly_ (wRep, tmget, formatPattern, nullptr)) {
342 return *r;
343 }
344 Execution::Throw (FormatException::kThe);
345 }
346 inline Date Date::Parse (const String& rep, const locale& l, const String& formatPattern, size_t* consumedCharsInStringUpTo)
347 {
348 RequireNotNull (consumedCharsInStringUpTo);
349 if (rep.empty ()) {
350 Execution::Throw (FormatException::kThe);
351 }
352 wstring wRep = rep.As<wstring> ();
353 const time_get<wchar_t>& tmget = use_facet<time_get<wchar_t>> (l);
354 if (auto r = ParseQuietly_ (wRep, tmget, formatPattern, consumedCharsInStringUpTo)) {
355 return *r;
356 }
357 Execution::Throw (FormatException::kThe);
358 }
359 inline Date Date::Parse (const String& rep, const Traversal::Iterable<String>& formatPatterns)
360 {
361 return Date::Parse_ (rep, locale::classic (), formatPatterns, nullptr);
362 }
363 inline Date Date::Parse (const String& rep, const Traversal::Iterable<String>& formatPatterns, size_t* consumedCharsInStringUpTo)
364 {
365 RequireNotNull (consumedCharsInStringUpTo);
366 return Date::Parse_ (rep, locale::classic (), formatPatterns, consumedCharsInStringUpTo);
367 }
368 inline Date Date::Parse (const String& rep, const String& formatPattern)
369 {
370 if (rep.empty ()) {
371 Execution::Throw (FormatException::kThe);
372 }
373 wstring wRep = rep.As<wstring> ();
374 const time_get<wchar_t>& tmget = use_facet<time_get<wchar_t>> (locale::classic ());
375 if (auto r = ParseQuietly_ (wRep, tmget, formatPattern, nullptr)) {
376 return *r;
377 }
378 Execution::Throw (FormatException::kThe);
379 }
380 inline Date Date::Parse (const String& rep, const String& formatPattern, size_t* consumedCharsInStringUpTo)
381 {
382 RequireNotNull (consumedCharsInStringUpTo);
383 if (rep.empty ()) {
384 Execution::Throw (FormatException::kThe);
385 }
386 wstring wRep = rep.As<wstring> ();
387 const time_get<wchar_t>& tmget = use_facet<time_get<wchar_t>> (locale::classic ());
388 if (auto r = ParseQuietly_ (wRep, tmget, formatPattern, consumedCharsInStringUpTo)) {
389 return *r;
390 }
391 Execution::Throw (FormatException::kThe);
392 }
393 inline optional<Date> Date::ParseQuietly (const String& rep, const String& formatPattern)
394 {
395 return ParseQuietly (rep, locale::classic (), formatPattern);
396 }
397 inline optional<Date> Date::ParseQuietly (const String& rep, const locale& l, const String& formatPattern)
398 {
399 if (rep.empty ()) {
400 return nullopt;
401 }
402 return ParseQuietly_ (rep.As<wstring> (), use_facet<time_get<wchar_t>> (l), formatPattern, nullptr);
403 }
404 inline String Date::ToString () const
405 {
406 return Format ();
407 }
408 [[nodiscard]] inline Date Date::Add (int dayCount) const
409 {
410 return Add (days{dayCount});
411 }
412 inline Date Date::operator+ (int daysOffset) const
413 {
414 return this->Add (daysOffset);
415 }
416 inline Date Date::operator+ (days daysOffset) const
417 {
418 return this->Add (daysOffset);
419 }
420 inline Date Date::operator+ (const Duration& d) const
421 {
422 return this->Add (d);
423 }
424 inline days Date::Since (Date dStart, Date dEnd)
425 {
426 if (dStart < dEnd) {
427 return dEnd - dStart;
428 }
429 return days{0};
430 }
431 inline auto Date::Difference (const Date& rhs) const -> days
432 {
433 return chrono::sys_days{fRep_} - chrono::sys_days{rhs.fRep_};
434 }
435 inline auto Date::operator- (const Date& rhs) const -> days
436 {
437 return Difference (rhs);
438 }
439 inline Date Date::operator- (days daysOffset) const
440 {
441 return Add (-daysOffset);
442 }
443 inline Date Date::operator- (int daysOffset) const
444 {
445 return Add (-daysOffset);
446 }
447 inline constexpr Date Date::kMin{Date::kMinJulianRep};
448 inline constexpr Date Date::kMax{Date::kMaxJulianRep};
449
450}
451
453 template <>
454 constexpr EnumNames<Stroika::Foundation::Time::Date::NonStandardPrintFormat> DefaultNames<Stroika::Foundation::Time::Date::NonStandardPrintFormat>::k{{{
455 {Stroika::Foundation::Time::Date::NonStandardPrintFormat::eCurrentLocale_WithZerosStripped, L"Current-Locale-With-Zeros-Stripped"},
456 }}};
457}
458
460
461 inline constexpr Time::Date Default<Time::Date>::kLowerBound{Time::Date::kMin};
462 inline constexpr Time::Date Default<Time::Date>::kUpperBound{Time::Date::kMax};
463
464 /// need getNext/GetPrev here excelt if causes constexpr issues
465 inline Time::Date Default<Time::Date>::GetNext (Time::Date n)
466 {
467 return n + 1;
468 }
469 inline Time::Date Default<Time::Date>::GetPrevious (Time::Date n)
470 {
471 return n - 1;
472 }
473
474};
#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:431
constexpr T As() const
Definition Date.inl:270
nonvirtual String Format(NonStandardPrintFormat pf=NonStandardPrintFormat::eDEFAULT) const
Definition Date.cpp:119
nonvirtual days operator-(const Date &rhs) const
Definition Date.inl:435
nonvirtual Date operator+(int daysOffset) const
Syntactic sure for Add (n);.
Definition Date.inl:412
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:393
static const JulianDayNumber kMinJulianRep
Definition Date.h:413
static const Date kMax
Definition Date.h:550
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:316
nonvirtual String ToString() const
Definition Date.inl:404
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:544
nonvirtual constexpr JulianDayNumber GetJulianRep() const
return the Julian Day Number (JDN) - corresponding to this date object (https://en....
Definition Date.inl:298
constexpr Date(Date &&src) noexcept=default
static const Traversal::Iterable< String > kDefaultParseFormats
Definition Date.h:496
nonvirtual Date Add(int d) const
Definition Date.inl:408
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