Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
DateTime.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_DateTime_h_
5#define _Stroika_Foundation_Time_DateTime_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <climits>
10#include <compare>
11#include <ctime>
12#include <locale>
13#include <string>
14
15#if qStroika_Foundation_Common_Platform_Windows
16#include <Windows.h>
17#endif
18
20#include "Stroika/Foundation/Common/Common.h"
22#include "Stroika/Foundation/Math/Common.h"
28
29/**
30 * \file
31 *
32 * \note Code-Status: <a href="Code-Status.md#Release">Release</a>
33 *
34 * TODO:
35 * @todo - http://stroika-bugs.sophists.com/browse/STK-671 - DateTime::Format and Parse () incorrectly handle the format strings %z and %Z (sort of)
36 *
37 * @todo Support various 64bit int (epoch time) types - even if time_t is 32-bit (such as on AIX).
38 * Be careful about overflow in underlying types like Date and TimeOfDay() however.
39 *
40 * @todo I'm not sure eCurrentLocale_WithZerosStripped is a good idea. Not sure if better
41 * to use separate format print arg or???
42 *
43 * @todo Should we PIN or throw OVERFLOW exception on values/requests which are out of range?
44 *
45 * @todo Future directions consider representing as big struct
46 * o And maybe store cached string reps for common cases as optimization and
47 * store cached second-offset (mutable) for quick compares
48 * o Note for timeofday it COULD be enhanced in the future to store TOD as
49 * fractional number of seconds. COULD use LINUX style struct with number of seconds and fixed
50 * point like number of nanoseconds (or some such)
51 */
52
53namespace Stroika::Foundation::Time {
54
55 using Characters::String;
56
57 class Duration; // forward declare for Difference ()
58
59 /*
60 * Description:
61 *
62 * DateTime is more than just a combination of Date, and Time. It also introduces the notion of TIMEZONE.
63 *
64 * Date part is always valid/required (use optional <DateTime> to support old DateTime{}.empty() behavior).
65 * TimeOfDay may be "unknown" (missing).
66 * Timezone may be "unknown" (missing), or a Timezone object (@see Timezone).
67 *
68 * \note DateTime constructors REQUIRE valid inputs, and any operations which might overflow throw range_error
69 * instead of creating invalid values.
70 *
71 * \note DateTime is only precise up to the nearest second - use TimePointSeconds for more precision.
72 *
73 * \note DateTime values are (optionally) associated with a particular timezone. If the value of the timezone is localtime and
74 * localtime changes, the DateTime then is relative to that new localtime. If the associated timezone is localtime, the
75 * interpretation of that timezone happens at the time a request requires it.
76 *
77 * \note Why no kISO8601Format string and use of enum eISO8601 instead? https://en.cppreference.com/w/cpp/locale/time_put/put may provide
78 * enough information (a little unclear on the Z/timezone part) to WRITE an ISO8601 format string for date time, but
79 * https://en.cppreference.com/w/cpp/locale/time_get/get doesn't come very close to supporting ISO 8601 format.
80 *
81 * \todo consider if eRFC1123 could be done as kRFC1123Format; see note about about kISO8601Format/eISO8601, but the same issues
82 * apply (unclear good enuf support for timezones). BUT could reconsider.
83 *
84 * \note c++20 clocks - and clock_cast etc.
85 * This class roughly corresponds to system_clock. Sadly - for conversions to / from TickCount () -
86 * clock_cast doesn't work - https://stackoverflow.com/questions/35282308/convert-between-c11-clocks
87 *
88 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
89 *
90 * \note <a href="Design-Overview.md#Comparisons">Comparisons</a>:
91 * o Standard Stroika Comparison support (operator<=>,operator==, etc);
92 *
93 * Also note - if the datetimes differ in their GetTimeZone() value, they are not necessarily
94 * considered different. If either one is unknown, they will both be treated as the same timezone.
95 * Otherwise, they will BOTH be converted to GMT, and compared as GMT.
96 *
97 * This coercion to GMT can be avoided by optional constructor argument to DateTime::ThreeWayComparer
98 */
99 class [[nodiscard]] DateTime {
100 public:
101 /**
102 * Construct a DateTime record with the given date and time value. Presume that these values
103 * apply to the given (argument) timezone. This does not adjust the value of date/time for the given tz -
104 * but just records that this datetime refers to the given timezone.
105 *
106 * To change TO a target timezone, use AsUTC () or AsLocalTime ().
107 *
108 * explicit DateTime (time_t unixEpochTime) noexcept
109 * Creates a DateTime object in UTC, using UNIX Epoch time.
110 *
111 * explicit DateTime (const FILETIME& fileTime, const optional<Timezone>& tz = Timezone::UTC ()) noexcept;
112 * Most windows APIs return file-times in UTC (or so it appears). Because of this,
113 * our default interpretation of a FILETIME structure as as UTC.
114 * Call DateTime (ft).AsLocalTime () to get the value returned in local time.
115 *
116 * \note DateTime (time_t unixEpochTime) returns a datetime in UTC
117 *
118 * \note All DateTime constructors REQUIRE valid (in range) arguments.
119 *
120 * \note Since DateTime is only precise up to the nearest second, some constructions (like from time_point)
121 * will necessarily round/lose precision.
122 */
123 constexpr DateTime (DateTime&& src) noexcept = default;
124 constexpr DateTime (const DateTime& src) = default;
125 constexpr explicit DateTime (const Date& d) noexcept;
126 constexpr explicit DateTime (const DateTime& dt, const Date& updateDate) noexcept;
127 constexpr explicit DateTime (const DateTime& dt, const TimeOfDay& updateTOD) noexcept;
128 constexpr DateTime (const Date& date, const optional<TimeOfDay>& timeOfDay, const optional<Timezone>& tz = Timezone::kUnknown) noexcept;
129 explicit DateTime (time_t unixEpochTime) noexcept;
130 explicit DateTime (const ::tm& tmTime, const optional<Timezone>& tz = Timezone::kUnknown) noexcept;
131 explicit DateTime (const ::timespec& tmTime, const optional<Timezone>& tz = Timezone::kUnknown) noexcept;
132#if qStroika_Foundation_Common_Platform_POSIX
133 explicit DateTime (const ::timeval& tmTime, const optional<Timezone>& tz = Timezone::kUnknown) noexcept;
134#elif qStroika_Foundation_Common_Platform_Windows
135 explicit DateTime (const ::SYSTEMTIME& sysTime, const optional<Timezone>& tz = Timezone::kLocalTime) noexcept;
136 explicit DateTime (const ::FILETIME& fileTime, const optional<Timezone>& tz = Timezone::kUTC) noexcept;
137#endif
138 template <Common::ITimePoint T>
139 explicit DateTime (T timePoint) noexcept;
140
141 public:
142 /**
143 */
144 nonvirtual DateTime& operator= (DateTime&& rhs) noexcept = default;
145 nonvirtual DateTime& operator= (const DateTime& rhs) = default;
146
147 public:
148 /**
149 * \brief LocaleIndependentFormat is a representation which a datetime can be transformed to & from
150 *
151 * eISO8601:
152 * This is the best (IMHO) preferred format for DateTime objects. Its simple, readable,
153 * sort order works naturally (alphabetical == by time) and its probably the most widely used
154 * portable date format.
155 *
156 * See https://datatracker.ietf.org/doc/html/rfc3339
157 *
158 * eRFC1123:
159 * eRFC1123 is a very old format (same as RFC822 except 4 digit year instead of 2-digit year), but is still used in the
160 * current HTTP specification (e.g. for cookies).
161 * The spec is originally documented in
162 * https://tools.ietf.org/html/rfc1123#5.2.14
163 * https://tools.ietf.org/html/rfc822#section-5
164 * EXAMPLE:
165 * Tue, 6 Nov 2018 06:25:51 -0800 (PST)
166 */
167 enum class LocaleIndependentFormat {
168 eISO8601,
169 eRFC1123,
170
171 Stroika_Define_Enum_Bounds (eISO8601, eRFC1123)
172 };
173
174 public:
175 /**
176 * This is the best (IMHO) preferred format for DateTime objects. Its simple, readable,
177 * sort order works naturally (alphabetical == by time) and its probably the most widely used
178 * portable date format.
179 */
180 static constexpr auto kISO8601Format = LocaleIndependentFormat::eISO8601;
181
182 public:
183 /**
184 * RFC1123 is a very old format (same as RFC822 except 4 digit year instead of 2-digit year), but is still used in the
185 * current HTTP specification (e.g. for cookies).
186 * The spec is originally documented in
187 * https://tools.ietf.org/html/rfc1123#5.2.14
188 * https://tools.ietf.org/html/rfc822#section-5
189 * EXAMPLE:
190 * Tue, 6 Nov 2018 06:25:51 -0800 (PST)
191 *
192 * NOTE - in this example, the -0800 redundantly says the same thing as the PST. With this format, its common
193 * to have junk/comments like that at the end of the date, and so this format - when parsed, will just ignore that stuff
194 * and will allow for extra whitespace in and around the date.
195 */
196 static constexpr auto kRFC1123Format = LocaleIndependentFormat::eRFC1123;
197
198 public:
199 /**
200 * \note https://en.cppreference.com/w/cpp/locale/time_get/get and https://en.cppreference.com/w/cpp/locale/time_put/put
201 */
202 static constexpr string_view kLocaleStandardFormat = "%c"sv;
203
204 public:
205 /**
206 * \note https://en.cppreference.com/w/cpp/locale/time_get/get and https://en.cppreference.com/w/cpp/locale/time_put/put
207 */
208 static constexpr string_view kLocaleStandardAlternateFormat = "%Ec"sv;
209
210 public:
211 /**
212 * Format patterns defined in:
213 * https://en.cppreference.com/w/cpp/locale/time_get/get
214 * https://en.cppreference.com/w/cpp/locale/time_put/put
215 *
216 * The default is locale-dependent DATE format followed by space and locale-dependent TIME.
217 *
218 * Note - there were two good choices here (all locale dependent):
219 * o %c - writes standard date and time string
220 * o %x %X - writes localized date representation / writes localized time representation
221 *
222 * \note VERY UNRELIABLE for year handling - @see Time::StdCPctxTraits
223 */
224 static constexpr string_view kShortLocaleFormatPattern = "%x %X"sv;
225
226 public:
227 /**
228 * Format patterns defined in:
229 * https://en.cppreference.com/w/cpp/locale/time_get/get
230 * https://en.cppreference.com/w/cpp/locale/time_put/put
231 *
232 * Default formats used by Date::Parse () to parse DateTime strings. The first of these - %c and %x %X, are
233 * locale-specific time formats.
234 */
235 static const Traversal::Iterable<String> kDefaultParseFormats;
236
237 public:
238 class FormatException;
239
240 public:
241 /**
242 * Parse will throw if the argument cannot be parsed as a valid DateTime.
243 *
244 * \note If the timezone cannot be identified in the source string, it will be returned as 'unknown'.
245 *
246 * \note for Parse () overloads which don't specify a locale, the default locale (locale{}) is used (unless the formatPattern is locale independent)
247 *
248 * For formatPattern, see https://en.cppreference.com/w/cpp/locale/time_get/get, and defaults to kLocaleStandardFormat
249 *
250 * \note A locale has no associated timezone (despite somewhat confusing documentation relating to this).
251 * @see https://stackoverflow.com/questions/52839648/does-a-c-locale-have-an-associated-timezone-and-if-yes-how-do-you-access-it
252 *
253 * \note an empty string produces BadFormat exception (whereas before 2.1d11 it produced an empty DateTime object (DateTime {}).
254 *
255 * \note @todo - http://stroika-bugs.sophists.com/browse/STK-671 - DateTime::Format and Parse () incorrectly handle the format strings %z and %Z (sort of)
256 *
257 * \note Handling of extra junk (including whitespace) at the start or end of the date MAY or MAY not be tollerated, depending on the
258 * format parameters given. But generally this API is struct, and will treat junk at the start or end of the date as a format
259 * exception. To get looser interpretation, use ParseQuietly().
260 */
261 static DateTime Parse (const String& rep, LocaleIndependentFormat format);
262 static DateTime Parse (const String& rep, const locale& l = locale{});
263 static DateTime Parse (const String& rep, const locale& l, const String& formatPattern);
264 static DateTime Parse (const String& rep, const locale& l, const Traversal::Iterable<String>& formatPatterns);
265 static DateTime Parse (const String& rep, const String& formatPattern);
266
267 public:
268 /**
269 * \brief like Parse(), but returns nullopt on parse error, not throwing exception.
270 *
271 * if locale is missing, and formatPattern is not locale independent, the current locale (locale{}) is used.
272 * if rep is empty, this will return nullopt
273 *
274 * if argument consumedCharacters != nullptr, and if ParseQuietly returns has_value (), then *consumedCharacters will contain
275 * the number of characters consumed from rep.
276 */
277 static optional<DateTime> ParseQuietly (const String& rep, LocaleIndependentFormat format, size_t* consumedCharacters = nullptr);
278 static optional<DateTime> ParseQuietly (const String& rep, const String& formatPattern, size_t* consumedCharacters = nullptr);
279 static optional<DateTime> ParseQuietly (const String& rep, const locale& l, const String& formatPattern, size_t* consumedCharacters = nullptr);
280 static optional<DateTime> ParseQuietly (const String& rep, const locale& l, const Traversal::Iterable<String>& formatPatterns = kDefaultParseFormats,
281 size_t* consumedCharacters = nullptr);
282
283 private:
284 // this requires rep!= ""
285 static optional<DateTime> ParseQuietly_ (const wstring& rep, const time_get<wchar_t>& tmget, const String& formatPattern, size_t* consumedCharacters);
286
287 public:
288 /**
289 * Return the current DateTime (in LocalTime)
290 */
291 static DateTime Now () noexcept;
292
293 public:
294 /**
295 * Return the current DateTime in UTC
296 *
297 * \brief equivilent to Now().AsUTC ()
298 */
299 static DateTime NowUTC () noexcept;
300
301 public:
302 /**
303 * Return the current Date (in LocalTime - local timezone)
304 */
305 static Date GetToday () noexcept;
306
307 public:
308 /**
309 * DateTime::kMin is the first date this DateTime class supports representing.
310 */
311 static const DateTime kMin; // defined constexpr
312
313 public:
314 /**
315 * DateTime::kMax is the last date this DateTime class supports representing.
316 */
317 static const DateTime kMax; // defined constexpr
318
319 public:
320 /**
321 */
322 nonvirtual constexpr optional<Timezone> GetTimezone () const noexcept;
323
324 public:
325 /**
326 * Creates a new DateTime object known to be in localtime. If this DateTime timezone is unknown, then the
327 * the datetime will be just assumed to have been in localtime.
328 *
329 * \post result.GetTimezone () == Timezone::LocalTime ()
330 */
331 nonvirtual DateTime AsLocalTime () const;
332
333 public:
334 /**
335 * Creates a new DateTime object known to be in UTC. If this DateTime timezone is unknown, then the
336 * the datetime will be just assumed to have been in UTC.
337 *
338 * Either way, this will always produce a result in the timezone UTC.
339 *
340 * \post result.GetTimezone () == Timezone::UTC ()
341 *
342 * @see AsTimezone ()
343 */
344 nonvirtual DateTime AsUTC () const;
345
346 public:
347 /**
348 * Return this DateTime converted to the given timezone. If the current timezone is unknown, this just
349 * sets the timezone. But if it is known, the time value is adjusted to correspond for the given timezone.
350 *
351 * Either way, this will always produce a result in the timezone tz.
352 *
353 * \post result.GetTimezone () == Timezone::UTC ()
354 */
355 nonvirtual DateTime AsTimezone (Timezone tz) const;
356
357 public:
358 /**
359 * return true if known true, and false if known false, and {} otherwise.
360 */
361 nonvirtual optional<bool> IsDaylightSavingsTime () const;
362
363 public:
364 /**
365 * \brief NonStandardPrintFormat is a representation which a datetime can be transformed into
366 *
367 * eCurrentLocale_WithZerosStripped
368 * eCurrentLocale_WithZerosStripped is eCurrentLocale, but with many cases of trailing zero's,
369 * and sometimes leading zeros, stripped, so for example, 01:03:05 PM will become 1:03:05 PM,
370 * and 04:06:00 PM will become 4:06 PM.
371 *
372 * This also may normalize extra space characters to a single space.
373 */
374 enum class NonStandardPrintFormat : uint8_t {
375 eCurrentLocale_WithZerosStripped,
376
377 eDEFAULT = eCurrentLocale_WithZerosStripped,
378
379 Stroika_Define_Enum_Bounds (eCurrentLocale_WithZerosStripped, eCurrentLocale_WithZerosStripped)
380 };
381
382 public:
383 static constexpr NonStandardPrintFormat eCurrentLocale_WithZerosStripped = NonStandardPrintFormat::eCurrentLocale_WithZerosStripped;
384
385 public:
386 /**
387 * For formatPattern, see http://en.cppreference.com/w/cpp/locale/time_put/put and defaults to kLocaleStandardFormat
388 * If only formatPattern specified, and no locale, use default (global) locale.
389 *
390 * \note A locale has no associated timezone (despite somewhat confusing documentation relating to this).
391 * @see https://stackoverflow.com/questions/52839648/does-a-c-locale-have-an-associated-timezone-and-if-yes-how-do-you-access-it
392 *
393 * \note for APIs with the locale not specified, its assumed to be the default (locale{}), except (perhaps) where the formatString is locale-independent.
394 *
395 * \note the default for Format() with no arguments is to use the default locale, but with the eCurrentLocale_WithZerosStripped flag set.
396 *
397 * \note @todo - http://stroika-bugs.sophists.com/browse/STK-671 - DateTime::Format and Parse () incorrectly handle the format strings %z and %Z (sort of)
398 *
399 * \par Example Usage
400 * \code
401 * // note example assumes current locale is 'C'
402 * DateTime d{Date{January / 4 / 2017y}, TimeOfDay{3, 3, 0}};
403 * EXPECT_EQ (d.Format (locale{}).NormalizeSpace (), "Wed Jan 4 03:03:00 2017"); // locale{} on windows adds extra space...
404 * EXPECT_EQ (d.Format (DateTime::eCurrentLocale_WithZerosStripped), d.Format ());
405 * EXPECT_EQ (d.Format (DateTime::eCurrentLocale_WithZerosStripped), "Wed Jan 4 3:03 2017"); // zeros stripped
406 * \endcode
407 *
408 * \par Example Usage
409 * \code
410 * DateTime d{Date (Year{1903}, April, DayOfMonth{5})};
411 * EXPECT_EQ (d.Format (DateTime::eCurrentLocale_WithZerosStripped), "4/5/03");
412 * \endcode
413 */
414 nonvirtual String Format (NonStandardPrintFormat pf = NonStandardPrintFormat::eDEFAULT) const;
415 nonvirtual String Format (LocaleIndependentFormat format) const;
416 nonvirtual String Format (const locale& l) const;
417 nonvirtual String Format (const locale& l, const String& formatPattern) const;
418 nonvirtual String Format (const String& formatPattern) const;
419
420 public:
421 /**
422 * @see Characters::ToString ()
423 */
424 nonvirtual String ToString () const;
425
426 public:
427 /**
428 * Returns number of days since this point - relative to NOW. Never less than zero.
429 */
430 nonvirtual Date::JulianDayNumber DaysSince () const;
431
432 public:
433 /**
434 * Defined for
435 * time_t
436 * struct tm
437 * struct timespec
438 * SYSTEMTIME (WINDOWS ONLY)
439 * Date
440 * String (Format (PrintFormat::eDEFAULT))
441 * chrono::time_point<CLOCK,DURATION> (satisfies Common::ITimePoint)
442 *
443 * NB: Intentionally NOT defined for TimeOfDay () - cuz it wouldn't make sense. A DateTime IS a Date, but its not a TimeOfDay. Time of day just
444 * logically extends Date with extra (TOD) information.
445 *
446 * \note As<time_t> Returns seconds since midnight 1970 UTC (UNIX 'Epoch time')
447 *
448 * \note As<struct tm> Returns a struct tm record without paying attention to timezone (in whatever timezone *this is) - but will throw if the date is before 1900
449 *
450 * \note As<struct timespec> inherits the limitations (UTC since 1970 - since it uses time_t internally)
451 *
452 * \note As<Date> ignores timezone, and returns the date object in this DateTime's timezone
453 *
454 * \note As<> will throw range_error() if it cannot perform the required conversions and produce a valid value.
455 */
456 template <typename T>
457 nonvirtual T As () const
458 // new bug define for clang/XCode? cannot do requires and tmeplate specialize?
459#if !qCompilerAndStdLib_template_requires_doesnt_work_with_specialization_Buggy
460 requires (Common::IAnyOf<T, time_t, struct tm, struct timespec, Date, Characters::String> or
461#if qStroika_Foundation_Common_Platform_Windows
462 same_as<T, SYSTEMTIME> or
463#endif
464 Common::ITimePoint<T>)
465#endif
466 ;
467
468 public:
469 /**
470 * returns the Date part of the DateTime object (in this datetime's timezone).
471 */
472 nonvirtual constexpr Date GetDate () const noexcept;
473
474 public:
475 /**
476 * returns the TimeOfDay part of the DateTime object (in this datetime's timezone). It returns 'missing' if there
477 * is no TimeOfDay associated with this DateTime object.
478 *
479 * \post (not result.has_value () or not result->empty ())
480 *
481 * \note Operations on a DateTime which require a 'TimeOfDay' will implicitly create a '0' time of day. So for example,
482 * if you AddSeconds (1) to a DateTime with a missing TimeOfDay it creates a TimeOfDay with value 12:00:01.
483 */
484 nonvirtual constexpr optional<TimeOfDay> GetTimeOfDay () const noexcept;
485
486 public:
487 /**
488 * Add the given amount of time to construct a new DateTime object. This function does NOT change
489 * the timezone value nor adjust for timezone issues.
490 *
491 * Add doesn't modify *this.
492 *
493 * \note This function will throw range_error() if it cannot perform the required conversions and produce a valid value.
494 */
495 nonvirtual DateTime Add (const Duration& d) const;
496
497 public:
498 /**
499 * Add the given number of days to construct a new DateTime object. This function does NOT change
500 * the timezone value nor adjust for timezone issues.
501 *
502 * AddDays doesn't modify this.
503 *
504 * \note This function will throw range_error() if it cannot perform the required conversions and produce a valid value.
505 */
506 nonvirtual DateTime AddDays (int days) const;
507
508 public:
509 /**
510 * Add the given number of seconds to construct a new DateTime object. This function does NOT change
511 * the timezone value nor adjust for timezone issues.
512 *
513 * AddSeconds doesn't modify this.
514 *
515 * \note This function will throw range_error() if it cannot perform the required conversions and produce a valid value.
516 *
517 * \note Operations on a DateTime which require a 'TimeOfDay' will implicitly create a '0' time of day. So for example,
518 * if you AddSeconds (1) to a DateTime with a missing TimeOfDay it creates a TimeOfDay with value 12:00:01.
519 */
520 nonvirtual DateTime AddSeconds (int64_t seconds) const;
521
522 public:
523 /**
524 * Returns the difference between the two DateTime records. This can then be easily converted to
525 * seconds, or days, or whatever.
526 *
527 * If the DateTime objects have different timezones, they are converted to a common timezone before the difference
528 * is computed.
529 */
530 nonvirtual Duration Difference (const DateTime& rhs) const;
531
532 public:
533 /**
534 */
535 nonvirtual strong_ordering operator<=> (const DateTime& rhs) const;
536
537 public:
538 /**
539 */
540 nonvirtual bool operator== (const DateTime& rhs) const;
541
542 public:
543 struct ThreeWayComparer;
544
545 private:
546 template <typename T>
547 nonvirtual T As_Simple_ () const
548#if !qCompilerAndStdLib_template_requires_doesnt_work_with_specialization_Buggy
549 requires (same_as<T, time_t> or same_as<T, struct tm> or same_as<T, struct timespec> or same_as<T, Date> or same_as<T, Characters::String>)
550#endif
551 ;
552#if qStroika_Foundation_Common_Platform_Windows
553 nonvirtual SYSTEMTIME AsSYSTEMTIME_ () const;
554#endif
555 template <typename CLOCK_T, typename DURATION_T>
556 nonvirtual time_point<CLOCK_T, DURATION_T> As_TP_ () const;
557
558 private:
559 optional<Timezone> fTimezone_;
560 Date fDate_;
561 optional<TimeOfDay> fTimeOfDay_; // for now - still can be 'empty' - but API (as of v2.1d4) disallows passing in or getting out empty TimeOfDay
562
563 public:
564 [[deprecated ("Since Stroika v3.0d5 - just use AsLocalTime ().As<Time::TimePointSeconds> ()")]] Time::TimePointSeconds ToTickCount () const
565 {
566 return AsLocalTime ().As<Time::TimePointSeconds> ();
567 }
568 [[deprecated ("Since Stroika v3.0d5 - just construct the DateTime with the value of GetTickCount()")]] static DateTime
569 FromTickCount (Time::TimePointSeconds tickCount)
570 {
571 return DateTime{tickCount};
572 }
573 };
574
575 class DateTime::FormatException : public Execution::RuntimeErrorException<> {
576 public:
577 FormatException ();
578
579 public:
580 /**
581 * just a predefined instance of the FormatException you can use, to avoid creating your own.
582 */
583 static const FormatException kThe;
584 };
585 inline const DateTime::FormatException DateTime::FormatException::kThe;
586
587 inline const Traversal::Iterable<String> DateTime::kDefaultParseFormats{
588 kLocaleStandardFormat,
589 kLocaleStandardAlternateFormat,
590 "%x %X"sv,
591 "%Ex %EX"sv,
592 "%Y-%b-%d %H:%M:%S"sv, // no obvious reference for this so maybe not a good idea
593 "%D%t%T"sv, // no obvious reference for this so maybe not a good idea
594 "%D%t%r"sv, // no obvious reference for this so maybe not a good idea
595 "%D%t%R"sv, // no obvious reference for this so maybe not a good idea
596 "%a %b %e %T %Y"sv, // no obvious reference for this so maybe not a good idea
597 };
598
599 /**
600 */
601 struct DateTime::ThreeWayComparer : Common::ComparisonRelationDeclarationBase<Common::ComparisonRelationType::eThreeWayCompare> {
602 /**
603 */
604 constexpr ThreeWayComparer (bool coerceToCommonTimezone = true);
605
606 /**
607 */
608 nonvirtual strong_ordering operator() (const DateTime& lhs, const DateTime& rhs) const;
609 bool fCoerceToCommonTimezone;
610 };
611
612 /**
613 * Syntactic sugar on Add()
614 */
615 DateTime operator+ (const DateTime& lhs, const Duration& rhs);
616
617 /**
618 * Syntactic sugar on Difference()
619 */
620 Duration operator- (const DateTime& lhs, const DateTime& rhs);
621
622 /**
623 * Syntactic sugar on Add() or Difference()
624 */
625 DateTime operator- (const DateTime&, const Duration& rhs);
626 Duration operator- (const DateTime&, const DateTime& rhs);
627
628}
629
630namespace Stroika::Foundation::Math {
631 bool NearlyEquals (Time::DateTime l, Time::DateTime r);
632 bool NearlyEquals (Time::DateTime l, Time::DateTime r, Time::DurationSeconds epsilon);
633}
634
635namespace Stroika::Foundation::Time {
636 class Duration;
637}
638
640
641 template <>
642 struct DefaultOpenness<Time::DateTime> : ExplicitOpenness<Openness::eClosed, Openness::eClosed> {};
643 template <>
644 struct DefaultDifferenceTypes<Time::DateTime> : ExplicitDifferenceTypes<Time::Duration> {};
645 /**
646 * \note Default<Time::DateTime> properties (kLowerBound/kUpperBound) can only be used after static initialization, and before
647 * static de-initialization.
648 */
649 template <>
650 struct Default<Time::DateTime> : ExplicitOpennessAndDifferenceType<Time::DateTime> {
651 static const Time::DateTime kLowerBound;
652 static const Time::DateTime kUpperBound;
653
654 // @todo - not important (just used for PinValue I think)
655 // static Time::DateTime GetNext (Time::DateTime n);
656 // static Time::DateTime GetPrevious (Time::DateTime n);
657 };
658
659}
660
661/*
662 ********************************************************************************
663 ***************************** Implementation Details ***************************
664 ********************************************************************************
665 */
666#include "DateTime.inl"
667
668#endif /*_Stroika_Foundation_Time_DateTime_h_*/
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
STRING_TYPE ToString(FLOAT_TYPE f, const ToStringOptions &options={})