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