4#include "Stroika/Foundation/StroikaPreComp.h"
14#include "Stroika/Foundation/Execution/Exceptions.h"
15#include "Stroika/Foundation/Execution/Throw.h"
24using namespace Stroika::Foundation::Memory;
25using namespace Stroika::Foundation::Time;
31 constexpr int kSecondsPerMinute_ = 60;
32 constexpr int kSecondsPerHour_ = 60 * kSecondsPerMinute_;
33 constexpr int kSecondsPerDay_ = 24 * kSecondsPerHour_;
37 constexpr int kTM_Year_RelativeToYear_{1900};
41 constexpr bool kRequireImbueToUseFacet_ =
52#if qStroika_Foundation_Common_Platform_Windows
54 TimeOfDay mkTimeOfDay_ (const ::SYSTEMTIME& sysTime)
56 ::WORD hour = max (sysTime.wHour,
static_cast<WORD
> (0));
57 hour = min (hour,
static_cast<WORD
> (23));
58 ::WORD minute = max (sysTime.wMinute,
static_cast<WORD
> (0));
59 minute = min (minute,
static_cast<WORD
> (59));
60 ::WORD secs = max (sysTime.wSecond,
static_cast<WORD
> (0));
61 secs = min (secs,
static_cast<WORD
> (59));
62 return TimeOfDay{hour, minute, secs, DataExchange::ValidationStrategy::eThrow};
64 Date mkDate_ (
const SYSTEMTIME& sysTime)
72#if qStroika_Foundation_Common_Platform_Windows
77 unsigned int minutes = seconds / 60;
78 unsigned int hours = minutes / 60;
80 hours = min (hours, 23U);
81 t.wHour =
static_cast<WORD
> (hours);
83 minutes -= hours * 60;
84 minutes = min (minutes, 59U);
85 t.wMinute =
static_cast<WORD
> (minutes);
87 seconds -= (60 * 60 * hours + 60 * minutes);
88 seconds = min (seconds, 59U);
89 t.wSecond =
static_cast<WORD
> (seconds);
96 inline constexpr uint32_t GetSecondCount_ (
const optional<TimeOfDay>& tod)
98 return tod.has_value () ? tod->GetAsSecondsCount () : 0;
103#if qStroika_Foundation_Common_Platform_Windows
104 ::SYSTEMTIME toSYSTEM_ (
const Date& date)
107 st.wMonth =
static_cast<::WORD
> (
static_cast<unsigned int> (date.
As<year_month_day> ().month ()));
108 st.wDay =
static_cast<::WORD
> (
static_cast<unsigned int> (date.
As<year_month_day> ().day ()));
109 st.wYear =
static_cast<::WORD
> (
static_cast<int> (date.
As<year_month_day> ().year ()));
117 time_t mkgmtime_ (
const tm* ptm)
121 constexpr int kDaysOfMonth_[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
124 int year = ptm->tm_year + kTM_Year_RelativeToYear_;
125 for (
int y = 1970; y < year; ++y) {
126 secs += (chrono::year{y}.is_leap () ? 366 : 365) * kSecondsPerDay_;
129 for (
int m = 0; m < ptm->tm_mon; ++m) {
130 secs += kDaysOfMonth_[m] * kSecondsPerDay_;
131 if (m == 1 && chrono::year{year}.is_leap ())
132 secs += kSecondsPerDay_;
134 secs += (ptm->tm_mday - 1) * kSecondsPerDay_;
135 secs += ptm->tm_hour * kSecondsPerHour_;
136 secs += ptm->tm_min * kSecondsPerMinute_;
138#if qCompilerAndStdLib_Supported_mkgmtime64
139 Assert (_mkgmtime64 (
const_cast<tm*
> (ptm)) == secs);
150DateTime::FormatException::FormatException ()
160DateTime::DateTime (time_t unixEpochTime) noexcept
165#if qStroika_Foundation_Common_Platform_Windows
166 (void)::_gmtime64_s (&tmTime, &unixEpochTime);
168 (void)::gmtime_r (&unixEpochTime, &tmTime);
171 DataExchange::ValidationStrategy::eThrow};
172 fTimeOfDay_ =
TimeOfDay{
static_cast<unsigned> (tmTime.tm_hour),
static_cast<unsigned> (tmTime.tm_min),
static_cast<unsigned> (tmTime.tm_sec)};
175DateTime::DateTime (const ::tm& tmTime,
const optional<Timezone>& tz) noexcept
178 , fTimeOfDay_{
TimeOfDay{
static_cast<unsigned> (tmTime.tm_hour),
static_cast<unsigned> (tmTime.tm_min),
179 static_cast<unsigned> (tmTime.tm_sec), DataExchange::ValidationStrategy::eThrow}}
183DateTime::DateTime (const ::timespec& tmTime,
const optional<Timezone>& tz) noexcept
187 time_t unixTime = tmTime.tv_sec;
188#if qStroika_Foundation_Common_Platform_POSIX
189 ::tm tmTimeDataBuf{};
190 ::tm* tmTimeData = ::gmtime_r (&unixTime, &tmTimeDataBuf);
191#elif qStroika_Foundation_Common_Platform_Windows
192 ::tm tmTimeDataBuf{};
193 if (
errno_t e = ::gmtime_s (&tmTimeDataBuf, &unixTime)) {
196 ::tm* tmTimeData = &tmTimeDataBuf;
198 ::tm* tmTimeData = ::gmtime (&unixTime);
200 fDate_ =
Date{
Year (tmTimeData->tm_year + kTM_Year_RelativeToYear_),
MonthOfYear (tmTimeData->tm_mon + 1),
201 DayOfMonth (tmTimeData->tm_mday), DataExchange::ValidationStrategy::eThrow};
202 fTimeOfDay_ =
TimeOfDay{
static_cast<unsigned> (tmTimeData->tm_hour),
static_cast<unsigned> (tmTimeData->tm_min),
203 static_cast<unsigned> (tmTimeData->tm_sec), DataExchange::ValidationStrategy::eThrow};
206#if qStroika_Foundation_Common_Platform_POSIX
207DateTime::DateTime (
const timeval& tmTime,
const optional<Timezone>& tz) noexcept
211 time_t unixTime = tmTime.tv_sec;
213 (void)::gmtime_r (&unixTime, &tmTimeData);
214 fDate_ =
Date{
Year (tmTimeData.tm_year + kTM_Year_RelativeToYear_),
MonthOfYear (tmTimeData.tm_mon + 1),
215 DayOfMonth (tmTimeData.tm_mday), DataExchange::ValidationStrategy::eThrow};
216 fTimeOfDay_ =
TimeOfDay{
static_cast<unsigned> (tmTimeData.tm_hour),
static_cast<unsigned> (tmTimeData.tm_min),
217 static_cast<unsigned> (tmTimeData.tm_sec), DataExchange::ValidationStrategy::eThrow};
221#if qStroika_Foundation_Common_Platform_Windows
222DateTime::DateTime (const ::SYSTEMTIME& sysTime,
const optional<Timezone>& tz) noexcept
224 , fDate_{mkDate_ (sysTime)}
225 , fTimeOfDay_{mkTimeOfDay_ (sysTime)}
229DateTime::DateTime (const ::FILETIME& fileTime,
const optional<Timezone>& tz) noexcept
233 ::SYSTEMTIME sysTime{};
234 if (::FileTimeToSystemTime (&fileTime, &sysTime)) {
235 fDate_ = mkDate_ (sysTime);
236 fTimeOfDay_ = mkTimeOfDay_ (sysTime);
241DateTime DateTime::Parse (
const String& rep, LocaleIndependentFormat format)
243 size_t nCharsConsumed;
244 if (
auto o = ParseQuietly (rep, format, &nCharsConsumed); o and nCharsConsumed == rep.
size ()) {
250DateTime DateTime::Parse (
const String& rep,
const locale& l,
const String& formatPattern)
252 if (rep.empty ()) [[unlikely]] {
255 size_t nCharsConsumed;
256 if (
auto o = ParseQuietly_ (rep.
As<wstring> (), use_facet<time_get<wchar_t>> (l), formatPattern, &nCharsConsumed);
257 o and nCharsConsumed == rep.
size ()) {
265 if (rep.empty ()) [[unlikely]] {
268 wstring wRep = rep.
As<wstring> ();
269 const time_get<wchar_t>& tmget = use_facet<time_get<wchar_t>> (l);
270 for (
const auto& formatPattern : formatPatterns) {
271 size_t nCharsConsumed;
272 if (
auto o = ParseQuietly_ (wRep, tmget, formatPattern, &nCharsConsumed); o and nCharsConsumed == rep.
size ()) {
279DateTime DateTime::Parse (
const String& rep,
const String& formatPattern)
281 return Parse (rep, locale{}, formatPattern);
284optional<DateTime> DateTime::ParseQuietly (
const String& rep, LocaleIndependentFormat format,
size_t* consumedCharacters)
286 if (rep.empty ()) [[unlikely]] {
290 case LocaleIndependentFormat::eISO8601: {
292 int numCharsConsumed{};
300 int nItems = ::swscanf (rep.As<wstring> ().c_str (), L"%d-%d-%d%n", &year, &month, &day, &numCharsConsumed);
301 DISABLE_COMPILER_MSC_WARNING_END (4996)
302 if (nItems < 3 or numCharsConsumed < 8) [[unlikely]] {
308 optional<TimeOfDay> t;
312 const wchar_t* startOfTimePart = get<0> (timePart.
c_str (&timePartBuf));
315 if (*startOfTimePart ==
'T' or *startOfTimePart ==
't' or *startOfTimePart ==
' ') [[likely]] {
323 int nItems = ::swscanf (startOfTimePart + 1, L"%d:%d:%f%n", &hour, &minute, &secsFloat, &ncc);
324 DISABLE_COMPILER_MSC_WARNING_END (4996)
325 if (nItems == 3 and ncc >= 8) {
327 second =
static_cast<int> (secsFloat);
332 numCharsConsumed += ncc;
333 t =
TimeOfDay{
static_cast<unsigned> (hour),
static_cast<unsigned> (minute),
static_cast<unsigned> (second),
334 DataExchange::ValidationStrategy::eThrow};
338 optional<Timezone> tz;
341 wstring tzAreaW = tzArea.
As<wstring> ();
342 const wchar_t* startTZArea = tzAreaW.c_str ();
343 if (*startTZArea ==
'Z' or *startTZArea ==
'z') {
345 numCharsConsumed += 1;
350 wchar_t plusMinusChar{};
353 int nItems = ::swscanf (startTZArea, L"%c%d:%d%n", &plusMinusChar, &tzHr, &tzMn, &ncc);
354 DISABLE_COMPILER_MSC_WARNING_END (4996)
355 if ((nItems == 3) and (plusMinusChar == '+' or plusMinusChar == '-')) {
356 if (plusMinusChar ==
'-') {
360 tz =
Timezone{
static_cast<int16_t
> (tzHr * 60 + tzMn), DataExchange::ValidationStrategy::eThrow};
361 numCharsConsumed += ncc;
363 else if ((nItems == 2) and (plusMinusChar ==
'+' or plusMinusChar ==
'-')) {
366 nItems = ::swscanf (startTZArea, L"%c%d%n", &plusMinusChar, &tzMn, &ncc);
367 DISABLE_COMPILER_MSC_WARNING_END (4996)
376 int hrs = tzMn / 100;
377 int min = tzMn % 100;
378 tzMn = hrs * 60 + min;
383 if (plusMinusChar ==
'-') {
386 tz =
Timezone{
static_cast<int16_t
> (tzMn), DataExchange::ValidationStrategy::eThrow};
387 numCharsConsumed += ncc;
394 Assert (0 <= numCharsConsumed and numCharsConsumed <=
static_cast<int> (rep.
length ()));
395 if (consumedCharacters !=
nullptr) {
396 *consumedCharacters = numCharsConsumed;
398 return t.has_value () ? DateTime{*d, t, tz} : DateTime{*d};
400 case LocaleIndependentFormat::eRFC1123: {
435 unsigned int numCharsConsumed{};
437 if (
auto i = tmp.
Find (
',')) {
439 numCharsConsumed +=
static_cast<unsigned int> (rep.
length () - tmp.
length ());
447 wchar_t monthStr[4]{};
448 wchar_t tzStr[101]{};
453 nItems = ::swscanf (tmp.As<wstring> ().c_str (), L"%d %3ls %d %d:%d:%d %100ls%n", &day, &monthStr, &year, &hour, &minute,
454 &second, &tzStr, &ncc);
455 DISABLE_COMPILER_MSC_WARNING_END (4996)
462 ncc =
static_cast<int> (tmp.
size ());
464 numCharsConsumed += ncc;
467 constexpr wchar_t kMonths_[12][4] = {L
"Jan", L
"Feb", L
"Mar", L
"Apr", L
"May", L
"Jun",
468 L
"Jul", L
"Aug", L
"Sep", L
"Oct", L
"Nov", L
"Dec"};
469 for (
size_t i = 0; i < NEltsOf (kMonths_); ++i) {
470 if (::wcscmp (monthStr, kMonths_[i]) == 0) {
471 month =
static_cast<int> (i + 1);
479 optional<TimeOfDay> t;
481 t =
TimeOfDay{
static_cast<unsigned> (hour),
static_cast<unsigned> (minute),
static_cast<unsigned> (second),
482 DataExchange::ValidationStrategy::eThrow};
484 optional<Timezone> tz;
485 constexpr pair<const wchar_t*, Timezone> kNamedTimezones_[]{
496 for (
size_t i = 0; i < NEltsOf (kNamedTimezones_); ++i) {
497 if (::wcscmp (tzStr, kNamedTimezones_[i].first) == 0) {
498 tz = kNamedTimezones_[i].second;
502 if (not tz.has_value ()) {
505 Assert (0 <= numCharsConsumed and numCharsConsumed <= rep.
length ());
506 if (consumedCharacters !=
nullptr) {
507 *consumedCharacters = numCharsConsumed;
509 return t.has_value () ? DateTime{d, *t, tz} : DateTime{d};
520 if (rep.empty ()) [[unlikely]] {
523 wstring wRep = rep.
As<wstring> ();
524 const time_get<wchar_t>& tmget = use_facet<time_get<wchar_t>> (l);
525 for (
const auto& formatPattern : formatPatterns) {
528 if (
auto o = ParseQuietly_ (wRep, tmget, formatPattern, consumedCharacters)) {
535optional<DateTime> DateTime::ParseQuietly_ (
const wstring& rep,
const time_get<wchar_t>& tmget,
const String& formatPattern,
size_t* consumedCharacters)
537 Require (not rep.empty ());
539 ios::iostate errState = ios::goodbit;
541 size_t nCharsConsumed{};
543 wstring formatPatternWS = formatPattern.
As<wstring> ();
544 wistringstream iss{rep};
545 istreambuf_iterator<wchar_t> itbegin{iss};
546 istreambuf_iterator<wchar_t> i = tmget.get (itbegin, istreambuf_iterator<wchar_t>{}, iss, errState, &when, formatPatternWS.c_str (),
547 formatPatternWS.c_str () + formatPatternWS.length ());
548 if (errState & ios::eofbit) {
549 nCharsConsumed = rep.size ();
553 nCharsConsumed =
static_cast<size_t> (distance (itbegin, i));
557 if constexpr (qCompilerAndStdLib_locale_time_get_reverses_month_day_with_2digit_year_Buggy) {
559 if (formatPattern ==
"%x %X"sv) {
561 wistringstream iss{rep};
562 istreambuf_iterator<wchar_t> itbegin{iss};
563 istreambuf_iterator<wchar_t> itend;
564 errState = ios::goodbit;
565 (void)tmget.get_date (itbegin, itend, iss, errState, &when);
569 if ((errState & ios::badbit) or (errState & ios::failbit)) [[unlikely]] {
572 Assert (0 <= nCharsConsumed and nCharsConsumed <= rep.length ());
573 if (consumedCharacters !=
nullptr) {
574 *consumedCharacters = nCharsConsumed;
581DateTime DateTime::AsLocalTime ()
const
595 return AsUTC ().AsLocalTime ();
599DateTime DateTime::AsUTC ()
const
601 [[maybe_unused]]
auto oldCode = [&] () {
606 DateTime tmp = fTimezone_.has_value ()
607 ? AddSeconds (-fTimezone_->GetBiasFromUTC (fDate_, Memory::NullCoalesce (fTimeOfDay_,
TimeOfDay{0})))
609 return DateTime{tmp.GetDate (), tmp.GetTimeOfDay (),
Timezone::kUTC};
616DateTime DateTime::AsTimezone (
Timezone tz)
const
618 if (GetTimezone () == tz) {
623 fTimezone_.has_value () ? AddSeconds (-fTimezone_->GetBiasFromUTC (fDate_, Memory::NullCoalesce (fTimeOfDay_,
TimeOfDay{0}))) : *this;
624 return DateTime{tmp.GetDate (), tmp.GetTimeOfDay (), tz};
628DateTime DateTime::Now () noexcept
630#if qStroika_Foundation_Common_Platform_POSIX
633 return DateTime{::time (
nullptr)}.AsLocalTime ();
634#elif qStroika_Foundation_Common_Platform_Windows
636 ::GetLocalTime (&st);
644DateTime DateTime ::NowUTC () noexcept
647 return Now ().AsUTC ();
650optional<bool> DateTime::IsDaylightSavingsTime ()
const
652 if (optional<Timezone> otz = GetTimezone ()) {
653 return otz->IsDaylightSavingsTime (GetDate (), GetTimeOfDay ());
658String DateTime::Format (LocaleIndependentFormat format)
const
661 case LocaleIndependentFormat::eISO8601: {
663 if (fTimeOfDay_.has_value ()) {
665 r <<
"T"sv << timeStr;
668 static const String kZ_{
"Z"sv};
672 auto tzBias = fTimezone_->GetBiasFromUTC (fDate_, Memory::NullCoalesce (fTimeOfDay_,
TimeOfDay{0}));
673 int minuteBias = abs (
static_cast<int> (tzBias)) / 60;
674 int hrs = minuteBias / 60;
675 int mins = minuteBias - hrs * 60;
676 r << ::Format (
"{}{:02}:{:02}"_f, (tzBias < 0 ? L
"-" : L
"+"), hrs, mins);
682 case LocaleIndependentFormat::eRFC1123: {
683 optional<Timezone> tz = GetTimezone ();
684 static const String kFMT_ =
"%a, %d %b %Y %H:%M:%S"_k;
685 String result = Format (locale::classic (), {kFMT_});
690 return result +
" "sv + tz->AsRFC1123 (fDate_, Memory::NullCoalesce (fTimeOfDay_,
TimeOfDay{0}));
700String DateTime::Format (NonStandardPrintFormat pf)
const
703 case eCurrentLocale_WithZerosStripped: {
707 String mungedData = Format (locale{});
712 while (
auto o = mungedData.
Find (kZero2StripPattern_, startAt)) {
713 Assert (o->first >= startAt);
716 mungedData = mungedData.
RemoveAt (*o);
723 while (
auto o = mungedData.
Find (kZero2StripPattern_, startAt)) {
724 Assert (o->first >= startAt);
725 mungedData = mungedData.
RemoveAt (o->first);
732 while (
auto o = mungedData.
Find (kZero2StripPattern_, startAt)) {
733 Assert (o->first >= startAt);
735 mungedData = mungedData.
RemoveAt (*o);
745String DateTime::Format (
const locale& l)
const
747 if (GetTimeOfDay ().has_value ()) {
748 return Format (l, kLocaleStandardFormat);
752 return GetDate ().Format (l);
756String DateTime::Format (
const String& formatPattern)
const
758 return Format (locale{}, formatPattern);
761String DateTime::Format (
const locale& l,
const String& formatPattern)
const
764 const time_put<wchar_t>& tmput = use_facet<time_put<wchar_t>> (l);
767 if constexpr (kRequireImbueToUseFacet_) {
773 if constexpr (qCompilerAndStdLib_locale_pctC_returns_numbers_not_alphanames_Buggy) {
774 if (l == locale::classic () and formatPattern == kLocaleStandardFormat) {
776 static const wstring_view kAltPattern_{L
"%a %b %e %T %Y"sv};
777 tmput.put (oss, oss,
' ', &when, kAltPattern_.data (), kAltPattern_.data () + kAltPattern_.length ());
778 return String{oss.str ()};
782 wstring formatPatternWS = formatPattern.
As<wstring> ();
783 tmput.put (oss, oss,
' ', &when, formatPatternWS.c_str (), formatPatternWS.c_str () + formatPatternWS.length ());
789String DateTime::ToString ()
const
793 if (
const auto& tz = GetTimezone ()) {
794 tmp <<
" "sv << Characters::ToString (*tz);
799Date::JulianDayNumber DateTime::DaysSince ()
const
801 int r = DayDifference (GetToday (), As<Date> ());
811time_t DateTime::As_Simple_ ()
const
813 DateTime useDT = this->AsUTC ();
814 Date d = useDT.GetDate ();
816 if (useDT.GetDate ().GetYear () <
Year{1970}) [[unlikely]] {
817 static const range_error kRangeErrror_{
"DateTime cannot be converted to time_t - before 1970"};
822 tm.tm_year =
static_cast<int> (d.GetYear ()) - kTM_Year_RelativeToYear_;
823 tm.tm_mon =
static_cast<unsigned int> (d.GetMonth ()) - 1;
824 tm.tm_mday =
static_cast<unsigned int> (d.GetDayOfMonth ());
825 unsigned int totalSecondsRemaining = GetSecondCount_ (useDT.GetTimeOfDay ());
826 tm.tm_hour = totalSecondsRemaining / (60 * 60);
827 totalSecondsRemaining -= tm.tm_hour * 60 * 60;
828 tm.tm_min = totalSecondsRemaining / 60;
829 totalSecondsRemaining -= tm.tm_min * 60;
830 tm.tm_sec = totalSecondsRemaining;
831 time_t result = mkgmtime_ (&tm);
837tm DateTime::As_Simple_ ()
const
839 if (GetDate ().GetYear () <
Year{kTM_Year_RelativeToYear_}) [[unlikely]] {
840 static const range_error kRangeErrror_{
"DateTime cannot be convered to time_t - before 1900"};
844 tm.tm_year =
static_cast<int> (fDate_.GetYear ()) - kTM_Year_RelativeToYear_;
845 tm.tm_mon =
static_cast<unsigned int> (fDate_.GetMonth ()) - 1;
846 tm.tm_mday =
static_cast<unsigned int> (fDate_.GetDayOfMonth ());
847 tm.tm_wday = fDate_.GetDayOfWeek ().c_encoding ();
848 unsigned int totalSecondsRemaining = fTimeOfDay_.has_value () ? fTimeOfDay_->GetAsSecondsCount () : 0;
849 tm.tm_hour = totalSecondsRemaining / (60 * 60);
850 totalSecondsRemaining -= tm.tm_hour * 60 * 60;
851 Assert (0 <= totalSecondsRemaining and totalSecondsRemaining < 60 * 60);
852 tm.tm_min = totalSecondsRemaining / 60;
853 totalSecondsRemaining -= tm.tm_min * 60;
854 Assert (0 <= totalSecondsRemaining and totalSecondsRemaining < 60);
855 tm.tm_sec = totalSecondsRemaining;
857 Ensure (0 <= tm.tm_hour and tm.tm_hour <= 23);
858 Ensure (0 <= tm.tm_min and tm.tm_min <= 59);
859 Ensure (0 <= tm.tm_sec and tm.tm_sec <= 59);
864timespec DateTime::As_Simple_ ()
const
867 tspec.tv_sec = As<time_t> ();
872#if qStroika_Foundation_Common_Platform_Windows
873::SYSTEMTIME DateTime::AsSYSTEMTIME_ ()
const
876 ::SYSTEMTIME d = toSYSTEM_ (fDate_);
877 ::SYSTEMTIME t = toSysTime_ (Memory::NullCoalesce (fTimeOfDay_,
TimeOfDay{0}));
880 r.wMinute = t.wMinute;
881 r.wSecond = t.wSecond;
882 r.wMilliseconds = t.wMilliseconds;
887DateTime DateTime::Add (
const Duration& d)
const
889 return AddSeconds (d.
As<int64_t> ());
892DateTime DateTime::AddDays (
int days)
const
894 return DateTime{GetDate ().Add (days), GetTimeOfDay (), GetTimezone ()};
897DateTime DateTime::AddSeconds (int64_t seconds)
const
900 int64_t n = GetSecondCount_ (GetTimeOfDay ());
905 Assert (dayDiff < 0);
912 Assert (dayDiff == 0);
919 Assert (numeric_limits<int>::lowest () <= dayDiff and dayDiff <= numeric_limits<int>::max ());
921 return DateTime{GetDate ().Add (days{dayDiff}), GetTimeOfDay (), GetTimezone ()};
924 return DateTime{GetDate ().Add (days{dayDiff}),
TimeOfDay{
static_cast<uint32_t
> (n)}, GetTimezone ()};
928Duration DateTime::Difference (
const DateTime& rhs)
const
930 if (GetTimezone () == rhs.GetTimezone ()) {
931 int64_t dayDiff =
static_cast<int64_t
> (GetDate ().GetJulianRep ()) -
static_cast<int64_t
> (rhs.GetDate ().GetJulianRep ());
937 return AsUTC ().Difference (rhs.AsUTC ());
946strong_ordering DateTime::ThreeWayComparer::operator() (
const DateTime& lhs,
const DateTime& rhs)
const
949 if (
auto cmp = lhs.GetDate () <=> rhs.GetDate (); cmp != strong_ordering::equal) {
952 return StdCompat::compare_three_way{}(lhs.GetTimeOfDay (), rhs.GetTimeOfDay ());
954 else if (fCoerceToCommonTimezone) {
955 return operator() (lhs.AsUTC (), rhs.AsUTC ());
962 if (
auto cmp = lhs.GetDate () <=> rhs.GetDate (); cmp != strong_ordering::equal) {
965 if (
auto cmp = StdCompat::compare_three_way{}(lhs.GetTimeOfDay (), rhs.GetTimeOfDay ()); cmp != strong_ordering::equal) {
968 return StdCompat::compare_three_way{}(lhs.GetTimezone (), rhs.GetTimezone ());
977DateTime Time::operator+ (
const DateTime& lhs,
const Duration& rhs)
980 return lhs.Add (rhs);
983DateTime Time::operator- (
const DateTime& lhs,
const Duration& rhs)
986 return lhs.Add (-rhs);
989Duration Time::operator- (
const DateTime& lhs,
const DateTime& rhs)
992 return lhs.Difference (rhs);
1000bool Math::NearlyEquals (Time::DateTime l, Time::DateTime r)
1007 return l == r or Math::NearlyEquals (
static_cast<Time::DurationSeconds::rep
> (l.As<time_t> ()),
1008 static_cast<Time::DurationSeconds::rep
> (r.As<time_t> ()), epsilon.count ());
#define AssertNotImplemented()
#define RequireNotReached()
#define AssertNotReached()
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
RegularExpression is a compiled regular expression which can be used to match on a String class.
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
nonvirtual size_t length() const noexcept
nonvirtual String NormalizeSpace(Character useSpaceCharacter=' ') const
Replace sequences of whitespace characters (space, tab, newline etc) with a single space (or argument...
nonvirtual tuple< const wchar_t *, wstring_view > c_str(Memory::StackBuffer< wchar_t > *possibleBackingStore) const
nonvirtual size_t size() const noexcept
nonvirtual String SubString(SZ from) const
nonvirtual String LTrim(bool(*shouldBeTrimmed)(Character)=Character::IsWhitespace) const
nonvirtual String RemoveAt(size_t charAt) const
nonvirtual String RTrim(bool(*shouldBeTrimmed)(Character)=Character::IsWhitespace) const
nonvirtual optional< size_t > Find(Character c, CompareOptions co=eWithCase) const
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
static constexpr string_view kISO8601Format
Y-M-D format - locale independent, and ISO-8601 date format standard.
static const JulianDayNumber kMinJulianRep
Duration is a chrono::duration<double> (=.
static constexpr uint32_t kMaxSecondsPerDay
static constexpr string_view kISO8601Format
nonvirtual constexpr uint32_t GetAsSecondsCount() const
static const optional< Timezone > kUnknown
static const Timezone kUTC
static const Timezone kLocalTime
static optional< Timezone > ParseTimezoneOffsetString(const char *tzStr)
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
DISABLE_COMPILER_MSC_WARNING_START(4996)
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
void ThrowPOSIXErrNo(errno_t errNo=errno)
treats errNo as a POSIX errno value, and throws a SystemError (subclass of @std::system_error) except...
Simple wrapper on std::chrono::day, with some helpful validation properties (assures constructed 'ok'...
Simple wrapper on std::chrono::month, with some helpful validation properties (assures constructed 'o...
Simple wrapper on std::chrono::year, with some helpful validation properties (assures constructed 'ok...