Stroika Library 3.0d18
 
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} or 1d")]] static constexpr day e1{1};
172 [[deprecated ("Since Stroika v3.0d1, use day{2} or 2d")]] 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 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, (Aug. 1963), p. 444.
374 * 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 */
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,
395 constexpr static JulianDayNumber
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
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,
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 *
461 * \note this is LOCALE-INDEPENDENT
462 *
463 * \see kMonthDayYearFormat
464 *
465 * \note also used for XML
466 */
467 static constexpr string_view kISO8601Format = "%Y-%m-%d"sv;
468
469 public:
470 /**
471 * \note https://en.cppreference.com/w/cpp/locale/time_get/get
472 */
473 static constexpr string_view kLocaleStandardFormat = "%x"sv;
474
475 public:
476 /**
477 * \note https://en.cppreference.com/w/cpp/locale/time_get/get
478 */
479 static constexpr string_view kLocaleStandardAlternateFormat = "%Ex"sv;
480
481 public:
482 /**
483 * \brief classic (american) month-day-year format, but unlike %D, this uses %Y, so the 4-digit form of year
484 *
485 * \note https://en.cppreference.com/w/cpp/locale/time_get/get
486 * \note This format is LOCALE INDEPENDENT (according to https://en.cppreference.com/w/cpp/locale/time_get/get)
487 * \see kISO8601Format
488 */
489 static constexpr string_view kMonthDayYearFormat = "%m/%d/%Y"sv;
490
491 public:
492 /**
493 * Default formats used by Date::Parse () to parse time strings. The first of these - kLocaleStandardFormat, is
494 * the locale-specific date format.
495 */
496 static const Traversal::Iterable<String> kDefaultParseFormats;
497
498 public:
499 /**
500 * Note that for the consumedCharsInStringUpTo overload, the consumedCharsInStringUpTo is filled in with the position after the last
501 * character read (so before the next character to be read).
502 *
503 * \note Parse (... locale) with no formats specified, defaults to parsing with kDefaultParseFormats formats.
504 *
505 * \note if the locale is not specified, its assumed to be the current locale (locale{}))
506 *
507 * \note an empty string produces BadFormat exception.
508 *
509 * \see https://en.cppreference.com/w/cpp/locale/time_get/get for allowed formatPatterns
510 *
511 * \note when calling Parse with a format string and no locale, the default locale is assumed
512 */
513 static Date Parse (const String& rep, const locale& l = locale{});
514 static Date Parse (const String& rep, const locale& l, const Traversal::Iterable<String>& formatPatterns);
515 static Date Parse (const String& rep, const locale& l, const Traversal::Iterable<String>& formatPatterns, size_t* consumedCharsInStringUpTo);
516 static Date Parse (const String& rep, const locale& l, size_t* consumedCharsInStringUpTo);
517 static Date Parse (const String& rep, const locale& l, const String& formatPattern);
518 static Date Parse (const String& rep, const locale& l, const String& formatPattern, size_t* consumedCharsInStringUpTo);
519 static Date Parse (const String& rep, const Traversal::Iterable<String>& formatPatterns);
520 static Date Parse (const String& rep, const Traversal::Iterable<String>& formatPatterns, size_t* consumedCharsInStringUpTo);
521 static Date Parse (const String& rep, const String& formatPattern);
522 static Date Parse (const String& rep, const String& formatPattern, size_t* consumedCharsInStringUpTo);
523
524 public:
525 /**
526 * \brief like Parse(), but returns nullopt on parse error, not throwing exception.
527 * if locale is missing, and formatPattern is not locale independent, the current locale (locale{}) is used.
528 * if rep is empty, this will return nullopt
529 */
530 static optional<Date> ParseQuietly (const String& rep, const String& formatPattern);
531 static optional<Date> ParseQuietly (const String& rep, const locale& l, const String& formatPattern);
532
533 private:
534 static optional<Date> ParseQuietly_ (const wstring& rep, const time_get<wchar_t>& tmget, const String& formatPattern,
535 size_t* consumedCharsInStringUpTo);
536
537 private:
538 static Date Parse_ (const String& rep, const locale& l, const Traversal::Iterable<String>& formatPatterns, size_t* consumedCharsInStringUpTo);
539
540 public:
541 /**
542 * Date::kMin is the first date this Date class supports representing.
543 */
544 static const Date kMin; // defined constexpr
545
546 public:
547 /**
548 * Date::kMax is the last date this Date class supports representing.
549 */
550 static const Date kMax; // defined constexpr
551
552 public:
553 /**
554 */
555 nonvirtual constexpr year GetYear () const;
556
557 public:
558 /**
559 */
560 nonvirtual constexpr month GetMonth () const;
561
562 public:
563 /**
564 */
565 nonvirtual constexpr day GetDayOfMonth () const;
566
567 public:
568 /**
569 */
570 nonvirtual weekday GetDayOfWeek () const;
571
572 public:
573 /**
574 * \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
575 */
576 nonvirtual constexpr JulianDayNumber GetJulianRep () const;
577
578 public:
579 /**
580 * \brief DisplayFormat is a representation which a date can be transformed in and out of
581 *
582 * eCurrentLocale_WithZerosStripped
583 * eCurrentLocale_WithZerosStripped is locale{}, but with many cases of leading zero's,
584 * stripped, so for example, 03/05/2013 becomes 3/5/2013. This only affects the day/month, and not the
585 * year.
586 *
587 * \note Common::DefaultNames<> supported
588 */
589 enum class NonStandardPrintFormat : uint8_t {
590 eCurrentLocale_WithZerosStripped,
591
592 eDEFAULT = eCurrentLocale_WithZerosStripped,
593
594 Stroika_Define_Enum_Bounds (eCurrentLocale_WithZerosStripped, eCurrentLocale_WithZerosStripped)
595 };
596
597 public:
598 static constexpr NonStandardPrintFormat eCurrentLocale_WithZerosStripped = NonStandardPrintFormat::eCurrentLocale_WithZerosStripped;
599
600 public:
601 /**
602 * For formatPattern, see http://en.cppreference.com/w/cpp/locale/time_put/put
603 * If only formatPattern specified, and no locale, use default (global) locale.
604 */
605 nonvirtual String Format (NonStandardPrintFormat pf = NonStandardPrintFormat::eDEFAULT) const;
606 nonvirtual String Format (const locale& l) const;
607 nonvirtual String Format (const locale& l, const String& formatPattern) const;
608 nonvirtual String Format (const String& formatPattern) const;
609
610 public:
611 /**
612 * @see Characters::ToString ()
613 */
614 nonvirtual String ToString () const;
615
616 public:
617 /**
618 * Returns a new Date object based on this Date, with 'dayCount' days added.
619 *
620 * \par Example Usage
621 * \code
622 * Date d = 1906y/May/12d;
623 * d = d.Add (1); // OR d = d + 1;
624 * Assert (d == 1906y/May/13d);
625 * \endcode
626 *
627 * \note - a duration is much more precise than a day, so that overload rounds.
628 */
629 nonvirtual [[nodiscard]] Date Add (int d) const;
630 nonvirtual [[nodiscard]] Date Add (days d) const;
631 nonvirtual [[nodiscard]] Date Add (const Duration& d) const;
632
633 public:
634 /**
635 * returns number of days between a start day and end day: Never less than zero.
636 * No argument version computes days between *this and 'now'.
637 */
638 static days Since (Date dStart, Date dEnd);
639 nonvirtual days Since () const;
640
641 public:
642 /**
643 * \brief Returns the difference (*this - rhs) between the two Date records;
644 *
645 * \note Before Stroika v3.0d1, this returned SignedJulianDayNumber.
646 */
647 nonvirtual days Difference (const Date& rhs) const;
648
649 public:
650 /**
651 * \brief Syntactic sure for Add (n);
652 *
653 * \par Example Usage
654 * \code
655 * Date d = 1906y/May/12d;
656 * d = d + 1;
657 * Assert (d == 1906y/May/13d);
658 * \endcode
659 */
660 nonvirtual Date operator+ (int daysOffset) const;
661 nonvirtual Date operator+ (days daysOffset) const;
662 nonvirtual Date operator+ (const Duration& d) const;
663
664 public:
665 /**
666 * days operator- (const Date& rhs): Syntactic sugar on Difference()
667 * Date operator- (days or int daysOffset) Syntactic sugar on Add(-arg)
668 *
669 * \note subtracting by duration 'd' rounds to days...
670 */
671 nonvirtual days operator- (const Date& rhs) const;
672 nonvirtual Date operator- (int daysOffset) const;
673 nonvirtual Date operator- (days daysOffset) const;
674 nonvirtual Date operator- (const Duration& d) const;
675
676 public:
677 /**
678 */
679 constexpr strong_ordering operator<=> (const Date& rhs) const = default;
680
681 public:
682 /**
683 * Defined for
684 * struct tm
685 * year_month_day
686 * any std::time_point
687 *
688 * Generally constexpr where possible.
689 *
690 * @todo understand why on MSVC I need ::tm and tm in the IAnyOf<>
691 */
692 template <typename T>
693 constexpr T As () const
694 requires (Common::IAnyOf<::tm, tm, year_month_day> or Common::ITimePoint<T>);
695
696 public:
697 using JulianRepType [[deprecated ("Since Stroika v3.0d1 - use JulianDayNumber")]] = JulianDayNumber;
698 using SignedJulianRepType [[deprecated ("Since Stroika v3.0d1 - use SignedJulianDayNumber")]] = SignedJulianDayNumber;
699 [[deprecated ("Since Stroika v3.0d1 - use.Add () - now Date immutable")]] Date& operator++ ()
700 {
701 *this = Add (1);
702 return *this;
703 }
704 [[deprecated ("Since Stroika v3.0d1 - use.Add () - now Date immutable")]] Date operator++ (int)
705 {
706 return *this + 1;
707 }
708 [[deprecated ("Since Stroika v3.0d1, use As<year_month_day> ()")]] void mdy (month* m, day* d, year* y) const
709 {
710 RequireNotNull (m);
711 RequireNotNull (d);
712 RequireNotNull (y);
713 *m = fRep_.month ();
714 *d = fRep_.day ();
715 *y = fRep_.year ();
716 }
717 [[deprecated ("Since Stroika v3.0d1 - use Add(days)")]] Date AddDays (SignedJulianDayNumber dayCount) const
718 {
719 return Add (chrono::days{dayCount});
720 }
721 [[deprecated ("Since Stroika 3.0d1 - use Since")]] JulianDayNumber DaysSince () const
722 {
723 return static_cast<JulianDayNumber> (Since ().count ());
724 }
725
726 private:
727 static optional<Date> LocaleFreeParseQuietly_kMonthDayYearFormat_ (const wstring& rep, size_t* consumedCharsInStringUpTo);
728
729 private:
730 static constexpr int kTM_Year_RelativeToYear_{1900}; // see https://man7.org/linux/man-pages/man3/ctime.3.html
731
732 private:
733 static Date AsDate_ (const ::tm& when);
734
735 private:
736 year_month_day fRep_;
737 };
738 static_assert (sizeof (Date) == sizeof (year_month_day)); // generally 4 bytes
739 static_assert (totally_ordered<Date>);
740
741 class Date::FormatException : public Execution::RuntimeErrorException<> {
742 public:
743 FormatException ();
744
745 public:
746 /**
747 */
748 static const FormatException kThe;
749 };
750 inline const Date::FormatException Date::FormatException::kThe;
751 inline const Traversal::Iterable<String> Date::kDefaultParseFormats{
752 kLocaleStandardFormat, // x (kLocaleStandardFormat) parses the locale's standard date representation
753 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
754 kMonthDayYearFormat, // Before Stroika 2.1b10, this was L"%D" (=="%m/%d/%y) which is the 2-digit year
755 kISO8601Format,
756 };
757
758 Date::SignedJulianDayNumber DayDifference (const Date& lhs, const Date& rhs);
759 int YearDifference (const Date& lhs, const Date& rhs);
760 float YearDifferenceF (const Date& lhs, const Date& rhs);
761
762 String GetFormattedAge (const optional<Date>& birthDate, const optional<Date>& deathDate = {}); // returns ? if not a good src date
763 String GetFormattedAgeWithUnit (const optional<Date>& birthDate, const optional<Date>& deathDate = {}, bool abbrevUnit = true); // returns ? if not a good src date
764
765}
766
768
769 template <>
770 struct DefaultOpenness<Time::Date> : ExplicitOpenness<Openness::eClosed, Openness::eClosed> {};
771 template <>
772 struct DefaultDifferenceTypes<Time::Date> : ExplicitDifferenceTypes<chrono::days> {};
773 template <>
774 struct Default<Time::Date> : ExplicitOpennessAndDifferenceType<Time::Date> {
775 static const Time::Date kLowerBound;
776 static const Time::Date kUpperBound;
777
778 static Time::Date GetNext (Time::Date n);
779 static Time::Date GetPrevious (Time::Date n);
780
781 static size_t DifferenceToSizeT (chrono::days s)
782 {
783 return size_t (s.count ());
784 }
785 };
786
787}
788
789/*
790 ********************************************************************************
791 ***************************** Implementation Details ***************************
792 ********************************************************************************
793 */
794#include "Date.inl"
795
796#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
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
NonStandardPrintFormat
DisplayFormat is a representation which a date can be transformed in and out of.
Definition Date.h:589
nonvirtual days operator-(const Date &rhs) const
Definition Date.inl:435
make_signed_t< JulianDayNumber > SignedJulianDayNumber
Definition Date.h:340
nonvirtual Date operator+(int daysOffset) const
Syntactic sure for Add (n);.
Definition Date.inl:412
static constexpr string_view kISO8601Format
Y-M-D format - locale independent, and ISO-8601 date format standard.
Definition Date.h:467
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 Date Now() noexcept
Definition Date.cpp:42
static const JulianDayNumber kMinJulianRep
Definition Date.h:413
static constexpr ReferencePoint kUNIXEpoch
Definition Date.h:366
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 constexpr string_view kLocaleStandardAlternateFormat
Definition Date.h:479
static days Since(Date dStart, Date dEnd)
Definition Date.inl:424
static constexpr string_view kMonthDayYearFormat
classic (american) month-day-year format, but unlike D, this uses Y, so the 4-digit form of year
Definition Date.h:489
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 constexpr string_view kLocaleStandardFormat
Definition Date.h:473
static const Traversal::Iterable< String > kDefaultParseFormats
Definition Date.h:496
nonvirtual Date Add(int d) const
Definition Date.inl:408
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