Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Date.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Time_Date_h_
5#define _Stroika_Foundation_Time_Date_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <chrono>
10#include <climits>
11#include <compare>
12#include <string>
13
15#include "Stroika/Foundation/Common/Common.h"
17#include "Stroika/Foundation/DataExchange/ValidationStrategy.h"
18#include "Stroika/Foundation/Execution/Exceptions.h"
21
22/**
23 * \file
24 *
25 * \note Code-Status: <a href="Code-Status.md#Release">Release</a>
26 *
27 * TODO:
28 * @todo Could optimize the Format/Parse calls for case without locale to just hardwire implementation
29 * using sprintf/scanf (as we had before 2.1b10); only performance optimization and unclear it would help
30 *
31 * @todo I'm not sure eCurrentLocale_WithZerosStripped is a good idea. Not sure if better
32 * to use separate format print arg or???
33 *
34 * @todo It would be highly desirable to allow this date code to represent larger/smaller dates
35 * (without the Julian calendar restriction).
36 * That maybe a longer term issue.
37 *
38 * Consider representing as big struct
39 * o Like with STRUCT DATETIME or struct tm
40 * o Int year
41 * o Int month
42 * o And maybe store cached string reps for common cases as optimization and
43 * store cached second-offset (mutable) for quick compares
44 * o Note in docs for future versions the min/max date COULD be expanded
45 */
46
47namespace Stroika::Foundation::Time {
48
49 class Duration;
50
51 using Characters::String;
52
53 using chrono::month;
54 using chrono::months;
55
56 // clang-format off
57 using chrono::January;
58 using chrono::February;
59 using chrono::March;
60 using chrono::April;
61 using chrono::May;
62 using chrono::June;
63 using chrono::July;
64 using chrono::August;
65 using chrono::September;
66 using chrono::October;
67 using chrono::November;
68 using chrono::December;
69 // clang-format on
70
71 using chrono::weekday;
72
73 // clang-format off
74 using chrono::Sunday;
75 using chrono::Monday;
76 using chrono::Tuesday;
77 using chrono::Wednesday;
78 using chrono::Thursday;
79 using chrono::Friday;
80 using chrono::Saturday;
81 // clang-format on
82
83 using chrono::day;
84 using chrono::days;
85
86 using chrono::year;
87
88 using chrono::year_month_day;
89
90 DISABLE_COMPILER_MSC_WARNING_START (4455)
91 using std::literals::chrono_literals::operator"" d; // day
92 using std::literals::chrono_literals::operator"" y; // year
93 DISABLE_COMPILER_MSC_WARNING_END (4455)
94 using std::chrono::operator/; // year/month/day -> year_month_day
95
96 /**
97 * \brief Simple wrapper on std::chrono::weekday, with some helpful validation properties (assures constructed 'ok'). But not necessary to use - use just 'weekday' in most places
98 *
99 * \note - DayOfWeek was an enum in Stroika v2.1, so this is a significant change.
100 *
101 * \note DayOfWeek can be converted from/to unsigned int.
102 *
103 * \note not [[nodiscard]] cuz can use CTOR just for quiet validation
104 */
105 struct DayOfWeek : weekday {
106 /**
107 * For the purpose of integer constructors, 0==Sunday, 1==Monday, and so on
108 */
110 constexpr DayOfWeek (unsigned int w, DataExchange::ValidationStrategy validationStrategy = DataExchange::ValidationStrategy::eAssertion);
111
112 public:
113 [[deprecated ("Since Stroika v3.0d1, use chrono::Sunday")]] static constexpr weekday eSunday{Sunday};
114 [[deprecated ("Since Stroika v3.0d1, use chrono::Monday")]] static constexpr weekday eMonday{Monday};
115 [[deprecated ("Since Stroika v3.0d1, use chrono::Tuesday")]] static constexpr weekday eTuesday{Tuesday};
116 [[deprecated ("Since Stroika v3.0d1, use chrono::Wednesday")]] static constexpr weekday eWednesday{Wednesday};
117 [[deprecated ("Since Stroika v3.0d1, use chrono::Thursday")]] static constexpr weekday eThursday{Thursday};
118 [[deprecated ("Since Stroika v3.0d1, use chrono::Friday")]] static constexpr weekday eFriday{Friday};
119 [[deprecated ("Since Stroika v3.0d1, use chrono::Saturday")]] static constexpr weekday eSaturday{Saturday};
120 };
121
122 /**
123 * \brief Simple wrapper on std::chrono::month, with some helpful validation properties (assures constructed 'ok'). But not necessary to use - use just 'month' in most places
124 *
125 * \note - MonthOfYear was an enum in Stroika v2.1, so this is a significant change.
126 *
127 * \note MonthOfYear can be converted from/to unsigned int.
128 *
129 * \note not [[nodiscard]] cuz can use CTOR just for quiet validation
130 */
131 struct MonthOfYear : month {
132 /**
133 * For the purpose of integer constructors, 1==January, 2==February and so on (no zero).
134 */
137
138 public:
139 [[deprecated ("Since Stroika v3.0d1, use chrono::January")]] static constexpr chrono::month eJanuary{January};
140 [[deprecated ("Since Stroika v3.0d1, use chrono::February")]] static constexpr chrono::month eFebruary{February};
141 [[deprecated ("Since Stroika v3.0d1, use chrono::March")]] static constexpr chrono::month eMarch{March};
142 [[deprecated ("Since Stroika v3.0d1, use chrono::April")]] static constexpr chrono::month eApril{April};
143 [[deprecated ("Since Stroika v3.0d1, use chrono::May")]] static constexpr chrono::month eMay{May};
144 [[deprecated ("Since Stroika v3.0d1, use chrono::June")]] static constexpr chrono::month eJune{June};
145 [[deprecated ("Since Stroika v3.0d1, use chrono::July")]] static constexpr chrono::month eJuly{July};
146 [[deprecated ("Since Stroika v3.0d1, use chrono::August")]] static constexpr chrono::month eAugust{August};
147 [[deprecated ("Since Stroika v3.0d1, use chrono::September")]] static constexpr chrono::month eSeptember{September};
148 [[deprecated ("Since Stroika v3.0d1, use chrono::October")]] static constexpr chrono::month eOctober{October};
149 [[deprecated ("Since Stroika v3.0d1, use chrono::November")]] static constexpr chrono::month eNovember{November};
150 [[deprecated ("Since Stroika v3.0d1, use chrono::December")]] static constexpr chrono::month eDecember{December};
151 };
152
153 /**
154 * \brief Simple wrapper on std::chrono::day, with some helpful validation properties (assures constructed 'ok'). But not necessary to use - use just 'day' in most places
155 *
156 * \note - DayOfMonth was an enum in Stroika v2.1, so this is a significant change.
157 *
158 * \note DayOfMonth can be converted from/to unsigned int.
159 * \note You can use the suffix 'd' instead of DayOfMonth{n}
160 *
161 * \note not [[nodiscard]] cuz can use CTOR just for quiet validation
162 */
163 struct DayOfMonth : day {
164 /**
165 * For the purpose of integer constructors, 1==1st, 2==2nd, and so on (no zero)
166 */
169
170 public:
171 [[deprecated ("Since Stroika v3.0d1, use day{1}")]] static constexpr day e1{1};
172 [[deprecated ("Since Stroika v3.0d1, use day{2}")]] static constexpr day e2{2};
173 [[deprecated ("Since Stroika v3.0d1, use day{3}")]] static constexpr day e3{3};
174 [[deprecated ("Since Stroika v3.0d1, use day{4}")]] static constexpr day e4{4};
175 [[deprecated ("Since Stroika v3.0d1, use day{5}")]] static constexpr day e5{5};
176 [[deprecated ("Since Stroika v3.0d1, use day{6}")]] static constexpr day e6{6};
177 [[deprecated ("Since Stroika v3.0d1, use day{7}")]] static constexpr day e7{7};
178 [[deprecated ("Since Stroika v3.0d1, use day{8}")]] static constexpr day e8{8};
179 [[deprecated ("Since Stroika v3.0d1, use day{9}")]] static constexpr day e9{9};
180 [[deprecated ("Since Stroika v3.0d1, use day{10}")]] static constexpr day e10{10};
181 [[deprecated ("Since Stroika v3.0d1, use day{11}")]] static constexpr day e11{11};
182 [[deprecated ("Since Stroika v3.0d1, use day{12}")]] static constexpr day e12{12};
183 [[deprecated ("Since Stroika v3.0d1, use day{13}")]] static constexpr day e13{13};
184 [[deprecated ("Since Stroika v3.0d1, use day{14}")]] static constexpr day e14{14};
185 [[deprecated ("Since Stroika v3.0d1, use day{15}")]] static constexpr day e15{15};
186 [[deprecated ("Since Stroika v3.0d1, use day{16}")]] static constexpr day e16{16};
187 [[deprecated ("Since Stroika v3.0d1, use day{17}")]] static constexpr day e17{17};
188 [[deprecated ("Since Stroika v3.0d1, use day{18}")]] static constexpr day e18{18};
189 [[deprecated ("Since Stroika v3.0d1, use day{19}")]] static constexpr day e19{19};
190 [[deprecated ("Since Stroika v3.0d1, use day{20}")]] static constexpr day e20{20};
191 [[deprecated ("Since Stroika v3.0d1, use day{21}")]] static constexpr day e21{21};
192 [[deprecated ("Since Stroika v3.0d1, use day{22}")]] static constexpr day e22{22};
193 [[deprecated ("Since Stroika v3.0d1, use day{23}")]] static constexpr day e23{23};
194 [[deprecated ("Since Stroika v3.0d1, use day{24}")]] static constexpr day e24{24};
195 [[deprecated ("Since Stroika v3.0d1, use day{25}")]] static constexpr day e25{25};
196 [[deprecated ("Since Stroika v3.0d1, use day{26}")]] static constexpr day e26{26};
197 [[deprecated ("Since Stroika v3.0d1, use day{27}")]] static constexpr day e27{27};
198 [[deprecated ("Since Stroika v3.0d1, use day{28}")]] static constexpr day e28{28};
199 [[deprecated ("Since Stroika v3.0d1, use day{29}")]] static constexpr day e29{29};
200 [[deprecated ("Since Stroika v3.0d1, use day{30}")]] static constexpr day e30{30};
201 [[deprecated ("Since Stroika v3.0d1, use day{31}")]] static constexpr day e31{31};
202 };
203
204 /**
205 * \brief Simple wrapper on std::chrono::year, with some helpful validation properties (assures constructed 'ok'). But not necessary to use - use just 'year' in most places
206 *
207 * \note - Year was an enum in Stroika v2.1, so this is a significant change.
208 *
209 * \note Year can be converted from/to signed int.
210 *
211 * \note you can use the suffix y instead of Year{N} (assuming using namespace Foundation::Time).
212 *
213 * \note not [[nodiscard]] cuz can use CTOR just for quiet validation
214 */
215 struct Year : year {
216 /**
217 */
220
221 public:
222 /**
223 * \brief 4713 BC (no year zero)
224 *
225 * Was 1752 in Stroika v2.1
226 */
227 static constexpr year eFirstYear{-4712};
228
229 public:
230 /**
231 * Reason for current max-date:
232 * C:\Program Files (x86)\Windows Kits\10\Source\10.0.22000.0\ucrt\time\wcsftime.cpp
233 * _VALIDATE_RETURN(timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099, EINVAL, false);
234 * -- LGP 2022-11-09
235 * Was SHRT_MAX - 1 in Stroika v2.1
236 */
237 static constexpr year eLastYear{8099};
238 };
239
240 /**
241 * \brief this defines undefined but important properties of the %x (read/write date) format string in stdc++ time_get/time_put
242 *
243 * This (rather important detail) appears to be left out of the std c++ specification for date/time formatting, and as a result
244 * each implementation varies. Attempt to capture the actual choices - so at least I can write regression tests that properly
245 * reflect expected Stroika behavior. And - perhaps - avoid the %x format string!
246 *
247 * @todo see if other format strings similarly unreliable. MAYBE the biggest lesson (obvious) is to not count on reading/writing dates
248 * in any locale-defined format (but for UIs this is a tricky constraint)?
249 *
250 * \note - These values all derived empirically (and checked in Stroika regression tests)
251 */
253 static constexpr bool kLocaleClassic_Write4DigitYear = false;
254 static constexpr bool kLocaleENUS_Write4DigitYear =
255#if defined(_MSC_VER)
256 true
257#elif defined(_GLIBCXX_RELEASE)
258 false
259#elif defined(_LIBCPP_VERSION)
260 true
261#else
262 true
263#endif
264 ;
265 };
266
267 /**
268 * Description:
269 * The Date class is (originally) based on SmallTalk-80, The Language & Its Implementation,
270 * page 108 (apx) - but changed to use Gregorian instead of Julian calendar.
271 *
272 * \note This class integrates neatly with the C++20 chrono date support. You can easily
273 * go back and forth (e.g. Date{std::chrono::year_month_day...}} or d.As<year_month_day> ())
274 *
275 * The main features of the Stroika Data class (compared to the std c++ date support):
276 *
277 * o Simpler to use/understand (Stroika 'Date' does about the same thing as a bevy of
278 * different chrono classes)
279 * o Stroika Date immutable (OK - difference not clearly advantage)
280 * o Validation (constructor DataExchange::ValidationStrategy)
281 * (no non-ok () Date objects in Stroika). Nice clear semantics about exceptions
282 * and assertions.
283 * o Easier to use formatting
284 * o ISO8601 formatting
285 * o Wraps needlessly complicated locale/facet API for formatting dates as Strings, or
286 * parsing them from strings.
287 * o Builtin support for Julian calendar (again - maybe this is a difference not advantage?)
288 *
289 * \note
290 * o Date stores date's internally as Julian days, and so is valid for any date > January 1, −4713;
291 * Also note sizeof (year_month_day) == sizeof (Date) == 4
292 *
293 * \par Miscellaneous references
294 * o According to https://en.wikipedia.org/wiki/Gregorian_calendar
295 * Britain and the British Empire (including the eastern part of what is
296 * now the United States) adopted the Gregorian calendar in 1752
297 * o https://aa.usno.navy.mil/data/JulianDate
298 * Best Julian date calculator I found (bad but best)
299 * o Proleptic Gregorian Calendar
300 * https://en.wikipedia.org/wiki/Gregorian_calendar#Proleptic_Gregorian_calendar
301 * o Julian Day Numer
302 * https://en.wikipedia.org/wiki/Julian_day
303 *
304 * Class Date knows about some obvious information:
305 * -> there are seven days in a week, each day having a symbolic name and
306 * an index 1..7
307 * -> there are twelve months in a year, each having a symbolic name and
308 * an index 1..12.
309 * -> months have 28..31 days and
310 * -> a particular year might be a leap year."
311 *
312 * NB: Date implies NO NOTION of timezone.
313 *
314 * \note The entire Date API is immutable - meaning that all methods are const (except constructor and assignment operator)
315 *
316 * \note Date constructors REQUIRE valid inputs, and any operations which might overflow throw range_error
317 * instead of creating invalid values.
318 *
319 * \note Would like to make Date inherit from Debug::AssertExternallySynchronizedMutex to assure its not accidentially modified, but
320 * that's difficult because its sometimes uses as a constexpr
321 *
322 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
323 *
324 * \note <a href="Design-Overview.md#Comparisons">Comparisons</a>:
325 * static_assert (totally_ordered<Date>);
326 */
327 class [[nodiscard]] Date {
328 public:
329 /*
330 * This refers to Julian Day Number - JDN - https://en.wikipedia.org/wiki/Julian_day
331 *
332 * \note This was called JulianRepType in Stroika v2.1
333 */
334 using JulianDayNumber = uint32_t;
335
336 public:
337 /**
338 * Sometimes want a signed type to compute differences.
339 */
340 using SignedJulianDayNumber = make_signed_t<JulianDayNumber>;
341
342 public:
343 /**
344 * Define a few 'reference' points, which define the correspondence between year/month/day in the Gregorian(ish)
345 * calendar with.
346 *
347 * Generally, users of the Date class can ignore this detail. Its used to 'calibrate' the mapping between Julian dates and
348 * year month day dates.
349 */
351 year_month_day fYMD;
352 JulianDayNumber fJulianRep;
353 };
354
355 public:
356 /**
357 * Start of Julian Calendar (see https://docs.kde.org/trunk5/en/kstars/kstars/ai-julianday.html)
358 * JD=0, is January 1, 4713 BC (or -4712 January 1, since there was no year '0').
359 */
360 static constexpr ReferencePoint kStartOfJulianCalendar{-4712y / January / 1, 0};
361
362 public:
363 /**
364 * Start of UNIX time (see https://docs.kde.org/trunk5/en/kstars/kstars/ai-julianday.html, https://aa.usno.navy.mil/data/JulianDate)
365 */
366 static constexpr ReferencePoint kUNIXEpoch{1970y / January / 1, 2440588};
367
368 public:
369 /**
370 * See https://en.wikipedia.org/wiki/Gregorian_calendar
371 * September, 14d, 1752 (even this not sure of, but used this in Stroika v2.1)
372 *
373 * "Algorithm 199 from Communications of the ACM, Volume 6, No. 8,
374 * (Aug. 1963), p. 444. Gregorian calendar started on Sep. 14, 1752"
375 */
376 static constexpr ReferencePoint kGregorianCalendarEpoch{1752y / September / 14d, 2361222};
377
378 public:
379 /**
380 * The Stroika Date class works with any date after this date (apx 4000 BC), but mostly
381 * just very accurate post Gregorian Calendar era (1753 apx).
382 */
383 static constexpr ReferencePoint kMinDateReference = kStartOfJulianCalendar;
384 static_assert (kMinDateReference.fYMD.year () == Year::eFirstYear);
385
386 public:
387 /**
388 * Very hard to figure out how todo this. But this algorithm appears correct at least for dates > Gregorian Calendar era.
389 *
390 * Also, web is littered with Julian date converters that are wrong, somewhat wrong, or very wrong (at least all disagreeing).
391 * I used https://aa.usno.navy.mil/data/JulianDate as my reference/final arbiter/check (at least for dates past 1800).
392 */
393 constexpr static JulianDayNumber ToJulianRep (month m, day d, year y,
394 DataExchange::ValidationStrategy validationStrategy = DataExchange::ValidationStrategy::eAssertion);
395 constexpr static JulianDayNumber
396 ToJulianRep (year_month_day ymd, DataExchange::ValidationStrategy validationStrategy = DataExchange::ValidationStrategy::eAssertion);
397
398 public:
399 /**
400 * Compute the month/day/year associated with a given Julian day number. NOTE, this is only
401 * really accurate (as of Stroika v3.0d1) for dates in the Gregorian Calendar epoch (roughly since 1753).
402 */
403 constexpr static year_month_day
404 FromJulianRep (JulianDayNumber j, DataExchange::ValidationStrategy validationStrategy = DataExchange::ValidationStrategy::eAssertion);
405
406 public:
407 /**
408 * kMinJulianRep is defined (later) constexpr.
409 *
410 * \note In Stroika v2.1, 2361222, aka Date::ToJulianRep (September, 14d, year{1752})
411 * but now its around 4000BC (see kMinDateReference)
412 */
413 static const JulianDayNumber kMinJulianRep = kMinDateReference.fJulianRep;
414
415 public:
416 /**
417 * kMaxJulianRep is defined (later) constexpr.
418 */
419 static const JulianDayNumber kMaxJulianRep; // = Date::ToJulianRep (December, 31d, Year::eLastYear)
420
421 public:
422 class FormatException;
423
424 public:
425 /**
426 * if DataExchange::ValidationStrategy is NOT specified, or == DataExchange::ValidationStrategy::eAssertion, then
427 * \pre kMinJulianRep <= julianRep <= kMaxJulianRep AND Date::kMin <= d <= Date::kMax
428 * else if eThrow, then throw when arguments out of range.
429 *
430 * \par Example Usage
431 * \code
432 * Assert (1906y/May/12d == Date{1906y, May, 12d});
433 * \endcode
434 */
435 constexpr Date (Date&& src) noexcept = default;
436 constexpr Date (const Date& src) noexcept = default;
437 explicit constexpr Date (JulianDayNumber julianRep,
438 DataExchange::ValidationStrategy validationStrategy = DataExchange::ValidationStrategy::eAssertion);
439 constexpr Date (year y, month m, day d, DataExchange::ValidationStrategy validationStrategy = DataExchange::ValidationStrategy::eAssertion);
440 constexpr Date (year_month_day ymd, DataExchange::ValidationStrategy validationStrategy = DataExchange::ValidationStrategy::eAssertion);
441
442 public:
443 /**
444 */
445 nonvirtual Date& operator= (Date&& rhs) noexcept = default;
446 nonvirtual Date& operator= (const Date& rhs) = default;
447
448 public:
449 /**
450 * Return the current Date - shorthand for DateTime::Now ().GetDate () (so uses localtime)
451 */
452 static Date Now () noexcept;
453
454 public:
455 /**
456 * \brief Y-M-D format - locale independent, and ISO-8601 date format standard
457 *
458 * \note sometimes represented as %F (see https://en.cppreference.com/w/cpp/chrono/c/wcsftime), but that's not supported in https://en.cppreference.com/w/cpp/locale/time_get/get.
459 * so equivalent to %Y-%m-%d
460 * \note this is LOCALE-INDEPENDENT
461 * \see kMonthDayYearFormat
462 *
463 * \note also used for XML
464 */
465 static constexpr string_view kISO8601Format = "%Y-%m-%d"sv;
466
467 public:
468 /**
469 * \note https://en.cppreference.com/w/cpp/locale/time_get/get
470 */
471 static constexpr string_view kLocaleStandardFormat = "%x"sv;
472
473 public:
474 /**
475 * \note https://en.cppreference.com/w/cpp/locale/time_get/get
476 */
477 static constexpr string_view kLocaleStandardAlternateFormat = "%Ex"sv;
478
479 public:
480 /**
481 * \brief classic (american) month-day-year format, but unlike %D, this uses %Y, so the 4-digit form of year
482 *
483 * \note https://en.cppreference.com/w/cpp/locale/time_get/get
484 * \note This format is LOCALE INDEPENDENT (according to https://en.cppreference.com/w/cpp/locale/time_get/get)
485 * \see kISO8601Format
486 */
487 static constexpr string_view kMonthDayYearFormat = "%m/%d/%Y"sv;
488
489 public:
490 /**
491 * Default formats used by Date::Parse () to parse time strings. The first of these - kLocaleStandardFormat, is
492 * the locale-specific date format.
493 */
494 static const Traversal::Iterable<String> kDefaultParseFormats;
495
496 public:
497 /**
498 * Note that for the consumedCharsInStringUpTo overload, the consumedCharsInStringUpTo is filled in with the position after the last
499 * character read (so before the next character to be read).
500 *
501 * \note Parse (... locale) with no formats specified, defaults to parsing with kDefaultParseFormats formats.
502 *
503 * \note if the locale is not specified, its assumed to be the current locale (locale{}))
504 *
505 * \note an empty string produces BadFormat exception.
506 *
507 * \see https://en.cppreference.com/w/cpp/locale/time_get/get for allowed formatPatterns
508 *
509 * \note when calling Parse with a format string and no locale, the default locale is assumed
510 */
511 static Date Parse (const String& rep, const locale& l = locale{});
512 static Date Parse (const String& rep, const locale& l, const Traversal::Iterable<String>& formatPatterns);
513 static Date Parse (const String& rep, const locale& l, const Traversal::Iterable<String>& formatPatterns, size_t* consumedCharsInStringUpTo);
514 static Date Parse (const String& rep, const locale& l, size_t* consumedCharsInStringUpTo);
515 static Date Parse (const String& rep, const locale& l, const String& formatPattern);
516 static Date Parse (const String& rep, const locale& l, const String& formatPattern, size_t* consumedCharsInStringUpTo);
517 static Date Parse (const String& rep, const Traversal::Iterable<String>& formatPatterns);
518 static Date Parse (const String& rep, const Traversal::Iterable<String>& formatPatterns, size_t* consumedCharsInStringUpTo);
519 static Date Parse (const String& rep, const String& formatPattern);
520 static Date Parse (const String& rep, const String& formatPattern, size_t* consumedCharsInStringUpTo);
521
522 public:
523 /**
524 * \brief like Parse(), but returns nullopt on parse error, not throwing exception.
525 * if locale is missing, and formatPattern is not locale independent, the current locale (locale{}) is used.
526 * if rep is empty, this will return nullopt
527 */
528 static optional<Date> ParseQuietly (const String& rep, const String& formatPattern);
529 static optional<Date> ParseQuietly (const String& rep, const locale& l, const String& formatPattern);
530
531 private:
532 static optional<Date> ParseQuietly_ (const wstring& rep, const time_get<wchar_t>& tmget, const String& formatPattern,
533 size_t* consumedCharsInStringUpTo);
534
535 private:
536 static Date Parse_ (const String& rep, const locale& l, const Traversal::Iterable<String>& formatPatterns, size_t* consumedCharsInStringUpTo);
537
538 public:
539 /**
540 * Date::kMin is the first date this Date class supports representing.
541 */
542 static const Date kMin; // defined constexpr
543
544 public:
545 /**
546 * Date::kMax is the last date this Date class supports representing.
547 */
548 static const Date kMax; // defined constexpr
549
550 public:
551 /**
552 */
553 nonvirtual constexpr year GetYear () const;
554
555 public:
556 /**
557 */
558 nonvirtual constexpr month GetMonth () const;
559
560 public:
561 /**
562 */
563 nonvirtual constexpr day GetDayOfMonth () const;
564
565 public:
566 /**
567 */
568 nonvirtual weekday GetDayOfWeek () const;
569
570 public:
571 /**
572 * \brief return the Julian Day Number (JDN) - corresponding to this date object (https://en.wikipedia.org/wiki/Julian_day) - days since Monday, January 1, 4713 BC
573 */
574 nonvirtual constexpr JulianDayNumber GetJulianRep () const;
575
576 public:
577 /**
578 * \brief DisplayFormat is a representation which a date can be transformed in and out of
579 *
580 * eCurrentLocale_WithZerosStripped
581 * eCurrentLocale_WithZerosStripped is locale{}, but with many cases of leading zero's,
582 * stripped, so for example, 03/05/2013 becomes 3/5/2013. This only affects the day/month, and not the
583 * year.
584 *
585 * \note Common::DefaultNames<> supported
586 */
587 enum class NonStandardPrintFormat : uint8_t {
588 eCurrentLocale_WithZerosStripped,
589
590 eDEFAULT = eCurrentLocale_WithZerosStripped,
591
592 Stroika_Define_Enum_Bounds (eCurrentLocale_WithZerosStripped, eCurrentLocale_WithZerosStripped)
593 };
594
595 public:
596 static constexpr NonStandardPrintFormat eCurrentLocale_WithZerosStripped = NonStandardPrintFormat::eCurrentLocale_WithZerosStripped;
597
598 public:
599 /**
600 * For formatPattern, see http://en.cppreference.com/w/cpp/locale/time_put/put
601 * If only formatPattern specified, and no locale, use default (global) locale.
602 */
603 nonvirtual String Format (NonStandardPrintFormat pf = NonStandardPrintFormat::eDEFAULT) const;
604 nonvirtual String Format (const locale& l) const;
605 nonvirtual String Format (const locale& l, const String& formatPattern) const;
606 nonvirtual String Format (const String& formatPattern) const;
607
608 public:
609 /**
610 * @see Characters::ToString ()
611 */
612 nonvirtual String ToString () const;
613
614 public:
615 /**
616 * Returns a new Date object based on this Date, with 'dayCount' days added.
617 *
618 * \par Example Usage
619 * \code
620 * Date d = 1906y/May/12d;
621 * d = d.Add (1); // OR d = d + 1;
622 * Assert (d == 1906y/May/13d);
623 * \endcode
624 *
625 * \note - a duration is much more precise than a day, so that overload rounds.
626 */
627 nonvirtual [[nodiscard]] Date Add (int d) const;
628 nonvirtual [[nodiscard]] Date Add (days d) const;
629 nonvirtual [[nodiscard]] Date Add (const Duration& d) const;
630
631 public:
632 /**
633 * returns number of days between a start day and end day: Never less than zero.
634 * No argument version computes days between *this and 'now'.
635 */
636 static days Since (Date dStart, Date dEnd);
637 nonvirtual days Since () const;
638
639 public:
640 /**
641 * \brief Returns the difference (*this - rhs) between the two Date records;
642 *
643 * \note Before Stroika v3.0d1, this returned SignedJulianDayNumber.
644 */
645 nonvirtual days Difference (const Date& rhs) const;
646
647 public:
648 /**
649 * \brief Syntactic sure for Add (n);
650 *
651 * \par Example Usage
652 * \code
653 * Date d = 1906y/May/12d;
654 * d = d + 1;
655 * Assert (d == 1906y/May/13d);
656 * \endcode
657 */
658 nonvirtual Date operator+ (int daysOffset) const;
659 nonvirtual Date operator+ (days daysOffset) const;
660 nonvirtual Date operator+ (const Duration& d) const;
661
662 public:
663 /**
664 * days operator- (const Date& rhs): Syntactic sugar on Difference()
665 * Date operator- (days or int daysOffset) Syntactic sugar on Add(-arg)
666 *
667 * \note subtracting by duration 'd' rounds to days...
668 */
669 nonvirtual days operator- (const Date& rhs) const;
670 nonvirtual Date operator- (int daysOffset) const;
671 nonvirtual Date operator- (days daysOffset) const;
672 nonvirtual Date operator- (const Duration& d) const;
673
674 public:
675 /**
676 */
677 constexpr strong_ordering operator<=> (const Date& rhs) const = default;
678
679 public:
680 /**
681 * Defined for
682 * struct tm
683 * year_month_day
684 *
685 * Generally constexpr where possible.
686 */
687 template <typename T>
688 nonvirtual T As () const;
689
690 public:
691 using JulianRepType [[deprecated ("Since Stroika v3.0d1 - use JulianDayNumber")]] = JulianDayNumber;
692 using SignedJulianRepType [[deprecated ("Since Stroika v3.0d1 - use SignedJulianDayNumber")]] = SignedJulianDayNumber;
693 [[deprecated ("Since Stroika v3.0d1 - use.Add () - now Date immutable")]] Date& operator++ ()
694 {
695 *this = Add (1);
696 return *this;
697 }
698 [[deprecated ("Since Stroika v3.0d1 - use.Add () - now Date immutable")]] Date operator++ (int)
699 {
700 return *this + 1;
701 }
702 [[deprecated ("Since Stroika v3.0d1, use As<year_month_day> ()")]] void mdy (month* m, day* d, year* y) const
703 {
704 RequireNotNull (m);
705 RequireNotNull (d);
706 RequireNotNull (y);
707 *m = fRep_.month ();
708 *d = fRep_.day ();
709 *y = fRep_.year ();
710 }
711 [[deprecated ("Since Stroika v3.0d1 - use Add(days)")]] Date AddDays (SignedJulianDayNumber dayCount) const
712 {
713 return Add (chrono::days{dayCount});
714 }
715 [[deprecated ("Since Stroika 3.0d1 - use Since")]] JulianDayNumber DaysSince () const
716 {
717 return static_cast<JulianDayNumber> (Since ().count ());
718 }
719
720 private:
721 static optional<Date> LocaleFreeParseQuietly_kMonthDayYearFormat_ (const wstring& rep, size_t* consumedCharsInStringUpTo);
722
723 private:
724 static constexpr int kTM_Year_RelativeToYear_{1900}; // see https://man7.org/linux/man-pages/man3/ctime.3.html
725
726 private:
727 static Date AsDate_ (const ::tm& when);
728
729 private:
730 year_month_day fRep_;
731 };
732 static_assert (sizeof (Date) == sizeof (year_month_day)); // generally 4 bytes
733 static_assert (totally_ordered<Date>);
734
735 template <>
736 constexpr ::tm Date::As () const;
737 template <>
738 constexpr year_month_day Date::As () const;
739
740 class Date::FormatException : public Execution::RuntimeErrorException<> {
741 public:
742 FormatException ();
743
744 public:
745 /**
746 */
747 static const FormatException kThe;
748 };
749 inline const Date::FormatException Date::FormatException::kThe;
750 inline const Traversal::Iterable<String> Date::kDefaultParseFormats{
751 kLocaleStandardFormat, // x (kLocaleStandardFormat) parses the locale's standard date representation
752 kLocaleStandardAlternateFormat, // Ex (kLocaleStandardAlternateFormat) parses the locale's alternative date representation, e.g. expecting 平成23年 (year Heisei 23) instead of 2011年 (year 2011) in ja_JP locale
753 kMonthDayYearFormat, // Before Stroika 2.1b10, this was L"%D" (=="%m/%d/%y) which is the 2-digit year
754 kISO8601Format,
755 };
756
757 Date::SignedJulianDayNumber DayDifference (const Date& lhs, const Date& rhs);
758 int YearDifference (const Date& lhs, const Date& rhs);
759 float YearDifferenceF (const Date& lhs, const Date& rhs);
760
761 String GetFormattedAge (const optional<Date>& birthDate, const optional<Date>& deathDate = {}); // returns ? if not a good src date
762 String GetFormattedAgeWithUnit (const optional<Date>& birthDate, const optional<Date>& deathDate = {}, bool abbrevUnit = true); // returns ? if not a good src date
763
764}
765
767
768 template <>
769 struct DefaultOpenness<Time::Date> : ExplicitOpenness<Openness::eClosed, Openness::eClosed> {};
770 template <>
771 struct DefaultDifferenceTypes<Time::Date> : ExplicitDifferenceTypes<chrono::days> {};
772 template <>
773 struct Default<Time::Date> : ExplicitOpennessAndDifferenceType<Time::Date> {
774 static const Time::Date kLowerBound;
775 static const Time::Date kUpperBound;
776
777 static Time::Date GetNext (Time::Date n);
778 static Time::Date GetPrevious (Time::Date n);
779
780 static size_t DifferenceToSizeT (chrono::days s)
781 {
782 return size_t (s.count ());
783 }
784 };
785
786}
787
788/*
789 ********************************************************************************
790 ***************************** Implementation Details ***************************
791 ********************************************************************************
792 */
793#include "Date.inl"
794
795#endif /*_Stroika_Foundation_Time_Date_h_*/
#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
NonStandardPrintFormat
DisplayFormat is a representation which a date can be transformed in and out of.
Definition Date.h:587
make_signed_t< JulianDayNumber > SignedJulianDayNumber
Definition Date.h:340
static const Date kMax
Definition Date.h:548
static const JulianDayNumber kMaxJulianRep
Definition Date.h:419
static const Date kMin
Definition Date.h:542
constexpr Date(Date &&src) noexcept=default
static const Traversal::Iterable< String > kDefaultParseFormats
Definition Date.h:494
Duration is a chrono::duration<double> (=.
Definition Duration.h:96
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237
STL namespace.
Simple wrapper on std::chrono::day, with some helpful validation properties (assures constructed 'ok'...
Definition Date.h:163
Simple wrapper on std::chrono::weekday, with some helpful validation properties (assures constructed ...
Definition Date.h:105
Simple wrapper on std::chrono::month, with some helpful validation properties (assures constructed 'o...
Definition Date.h:131
this defines undefined but important properties of the x (read/write date) format string in stdc++ ti...
Definition Date.h:252
Simple wrapper on std::chrono::year, with some helpful validation properties (assures constructed 'ok...
Definition Date.h:215
static constexpr year eFirstYear
4713 BC (no year zero)
Definition Date.h:227
static constexpr year eLastYear
Definition Date.h:237