aboutsummaryrefslogtreecommitdiffstats
path: root/util/datetime/base.cpp
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /util/datetime/base.cpp
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'util/datetime/base.cpp')
-rw-r--r--util/datetime/base.cpp336
1 files changed, 336 insertions, 0 deletions
diff --git a/util/datetime/base.cpp b/util/datetime/base.cpp
new file mode 100644
index 0000000000..38ecc3ab96
--- /dev/null
+++ b/util/datetime/base.cpp
@@ -0,0 +1,336 @@
+#include "base.h"
+
+#include <util/string/cast.h>
+#include <util/stream/output.h>
+#include <util/stream/mem.h>
+#include <util/system/compat.h>
+#include <util/memory/tempbuf.h>
+#include <util/generic/string.h>
+#include <util/generic/strbuf.h>
+#include <util/generic/yexception.h>
+
+TString Strftime(const char* format, const struct tm* tm) {
+ size_t size = Max<size_t>(strlen(format) * 2 + 1, 107);
+ for (;;) {
+ TTempBuf buf(size);
+ int r = strftime(buf.Data(), buf.Size(), format, tm);
+ if (r != 0) {
+ return TString(buf.Data(), r);
+ }
+ size *= 2;
+ }
+}
+
+template <>
+TDuration FromStringImpl<TDuration, char>(const char* s, size_t len) {
+ return TDuration::Parse(TStringBuf(s, len));
+}
+
+template <>
+bool TryFromStringImpl<TDuration, char>(const char* s, size_t len, TDuration& result) {
+ return TDuration::TryParse(TStringBuf(s, len), result);
+}
+
+namespace {
+ template <size_t N>
+ struct TPad {
+ int I;
+ };
+
+ template <size_t N>
+ inline TPad<N> Pad(int i) {
+ return {i};
+ }
+
+ inline IOutputStream& operator<<(IOutputStream& o, const TPad<2>& p) {
+ if (p.I < 10) {
+ if (p.I >= 0) {
+ o << '0';
+ }
+ }
+
+ return o << p.I;
+ }
+
+ inline IOutputStream& operator<<(IOutputStream& o, const TPad<4>& p) {
+ if (p.I < 1000) {
+ if (p.I >= 0) {
+ if (p.I < 10) {
+ o << '0' << '0' << '0';
+ } else if (p.I < 100) {
+ o << '0' << '0';
+ } else {
+ o << '0';
+ }
+ }
+ }
+
+ return o << p.I;
+ }
+
+ inline IOutputStream& operator<<(IOutputStream& o, const TPad<6>& p) {
+ if (p.I < 100000) {
+ if (p.I >= 0) {
+ if (p.I < 10) {
+ o << '0' << '0' << '0' << '0' << '0';
+ } else if (p.I < 100) {
+ o << '0' << '0' << '0' << '0';
+ } else if (p.I < 1000) {
+ o << '0' << '0' << '0';
+ } else if (p.I < 10000) {
+ o << '0' << '0';
+ } else {
+ o << '0';
+ }
+ }
+ }
+
+ return o << p.I;
+ }
+
+ void WriteMicroSecondsToStream(IOutputStream& os, ui32 microSeconds) {
+ os << '.' << Pad<6>(microSeconds);
+ }
+
+ void WriteTmToStream(IOutputStream& os, const struct tm& theTm) {
+ os << Pad<4>(theTm.tm_year + 1900) << '-' << Pad<2>(theTm.tm_mon + 1) << '-' << Pad<2>(theTm.tm_mday) << 'T'
+ << Pad<2>(theTm.tm_hour) << ':' << Pad<2>(theTm.tm_min) << ':' << Pad<2>(theTm.tm_sec);
+ }
+
+ template <bool PrintUpToSeconds, bool iso>
+ void WritePrintableLocalTimeToStream(IOutputStream& os, const ::NPrivate::TPrintableLocalTime<PrintUpToSeconds, iso>& timeToPrint) {
+ const TInstant& momentToPrint = timeToPrint.MomentToPrint;
+ struct tm localTime;
+ momentToPrint.LocalTime(&localTime);
+ WriteTmToStream(os, localTime);
+ if (!PrintUpToSeconds) {
+ WriteMicroSecondsToStream(os, momentToPrint.MicroSecondsOfSecond());
+ }
+#ifndef _win_
+ i64 utcOffsetInMinutes = localTime.tm_gmtoff / 60;
+#else
+ TIME_ZONE_INFORMATION tz;
+ if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_INVALID) {
+ ythrow TSystemError() << "Failed to get the system time zone";
+ }
+ i64 utcOffsetInMinutes = -tz.Bias;
+#endif
+ if (utcOffsetInMinutes == 0) {
+ os << 'Z';
+ } else {
+ if (utcOffsetInMinutes < 0) {
+ os << '-';
+ utcOffsetInMinutes = -utcOffsetInMinutes;
+ } else {
+ os << '+';
+ }
+ os << Pad<2>(utcOffsetInMinutes / 60);
+ if (iso) {
+ os << ':';
+ }
+ os << Pad<2>(utcOffsetInMinutes % 60);
+ }
+ }
+}
+
+template <>
+void Out<TDuration>(IOutputStream& os, TTypeTraits<TDuration>::TFuncParam duration) {
+ os << duration.Seconds();
+ WriteMicroSecondsToStream(os, duration.MicroSecondsOfSecond());
+ os << 's';
+}
+
+template <>
+void Out<TInstant>(IOutputStream& os, TTypeTraits<TInstant>::TFuncParam instant) {
+ char buf[64];
+ auto len = FormatDate8601(buf, sizeof(buf), instant.TimeT());
+
+ // shouldn't happen due to current implementation of FormatDate8601() and GmTimeR()
+ Y_ENSURE(len, TStringBuf("Out<TInstant>: year does not fit into an integer"));
+
+ os.Write(buf, len - 1 /* 'Z' */);
+ WriteMicroSecondsToStream(os, instant.MicroSecondsOfSecond());
+ os << 'Z';
+}
+
+template <>
+void Out<::NPrivate::TPrintableLocalTime<false, false>>(IOutputStream& os, TTypeTraits<::NPrivate::TPrintableLocalTime<false, false>>::TFuncParam localTime) {
+ WritePrintableLocalTimeToStream(os, localTime);
+}
+
+template <>
+void Out<::NPrivate::TPrintableLocalTime<false, true>>(IOutputStream& os, TTypeTraits<::NPrivate::TPrintableLocalTime<false, true>>::TFuncParam localTime) {
+ WritePrintableLocalTimeToStream(os, localTime);
+}
+
+template <>
+void Out<::NPrivate::TPrintableLocalTime<true, false>>(IOutputStream& os, TTypeTraits<::NPrivate::TPrintableLocalTime<true, false>>::TFuncParam localTime) {
+ WritePrintableLocalTimeToStream(os, localTime);
+}
+
+template <>
+void Out<::NPrivate::TPrintableLocalTime<true, true>>(IOutputStream& os, TTypeTraits<::NPrivate::TPrintableLocalTime<true, true>>::TFuncParam localTime) {
+ WritePrintableLocalTimeToStream(os, localTime);
+}
+
+TString TDuration::ToString() const {
+ return ::ToString(*this);
+}
+
+TString TInstant::ToString() const {
+ return ::ToString(*this);
+}
+
+TString TInstant::ToRfc822String() const {
+ return FormatGmTime("%a, %d %b %Y %H:%M:%S GMT");
+}
+
+TString TInstant::ToStringUpToSeconds() const {
+ char buf[64];
+ auto len = FormatDate8601(buf, sizeof(buf), TimeT());
+ if (!len) {
+ ythrow yexception() << "TInstant::ToStringUpToSeconds: year does not fit into an integer";
+ }
+ return TString(buf, len);
+}
+
+TString TInstant::ToIsoStringLocal() const {
+ return ::ToString(FormatIsoLocal(*this));
+}
+
+TString TInstant::ToStringLocal() const {
+ return ::ToString(FormatLocal(*this));
+}
+
+TString TInstant::ToRfc822StringLocal() const {
+ return FormatLocalTime("%a, %d %b %Y %H:%M:%S %Z");
+}
+
+TString TInstant::ToIsoStringLocalUpToSeconds() const {
+ return ::ToString(FormatIsoLocalUpToSeconds(*this));
+}
+
+TString TInstant::ToStringLocalUpToSeconds() const {
+ return ::ToString(FormatLocalUpToSeconds(*this));
+}
+
+TString TInstant::FormatLocalTime(const char* format) const noexcept {
+ struct tm theTm;
+ LocalTime(&theTm);
+ return Strftime(format, &theTm);
+}
+
+TString TInstant::FormatGmTime(const char* format) const noexcept {
+ struct tm theTm;
+ GmTime(&theTm);
+ return Strftime(format, &theTm);
+}
+
+::NPrivate::TPrintableLocalTime<false, true> FormatIsoLocal(TInstant instant) {
+ return ::NPrivate::TPrintableLocalTime<false, true>(instant);
+}
+
+::NPrivate::TPrintableLocalTime<false, false> FormatLocal(TInstant instant) {
+ return ::NPrivate::TPrintableLocalTime<false, false>(instant);
+}
+
+::NPrivate::TPrintableLocalTime<true, true> FormatIsoLocalUpToSeconds(TInstant instant) {
+ return ::NPrivate::TPrintableLocalTime<true, true>(instant);
+}
+
+::NPrivate::TPrintableLocalTime<true, false> FormatLocalUpToSeconds(TInstant instant) {
+ return ::NPrivate::TPrintableLocalTime<true, false>(instant);
+}
+
+void Sleep(TDuration duration) {
+ NanoSleep(duration.NanoSeconds());
+}
+
+void sprint_gm_date(char* buf, time_t when, long* sec) {
+ struct tm theTm;
+ ::Zero(theTm);
+ GmTimeR(&when, &theTm);
+ DateToString(buf, theTm);
+ if (sec) {
+ *sec = seconds(theTm);
+ }
+}
+
+void DateToString(char* buf, const struct tm& theTm) {
+ Y_ENSURE(0 <= theTm.tm_year + 1900 && theTm.tm_year + 1900 <= 9999, "invalid year " + ToString(theTm.tm_year + 1900) + ", year should be in range [0, 9999]");
+
+ sprintf(buf, "%04d%02d%02d", theTm.tm_year + 1900, theTm.tm_mon + 1, theTm.tm_mday);
+}
+
+void DateToString(char* buf, time_t when, long* sec) {
+ struct tm theTm;
+ localtime_r(&when, &theTm);
+
+ DateToString(buf, theTm);
+
+ if (sec) {
+ *sec = seconds(theTm);
+ }
+}
+
+TString DateToString(const struct tm& theTm) {
+ char buf[DATE_BUF_LEN];
+ DateToString(buf, theTm);
+ return buf;
+}
+
+TString DateToString(time_t when, long* sec) {
+ char buf[DATE_BUF_LEN];
+ DateToString(buf, when, sec);
+ return buf;
+}
+
+TString YearToString(const struct tm& theTm) {
+ Y_ENSURE(0 <= theTm.tm_year + 1900 && theTm.tm_year + 1900 <= 9999, "invalid year " + ToString(theTm.tm_year + 1900) + ", year should be in range [0, 9999]");
+ char buf[16];
+ sprintf(buf, "%04d", theTm.tm_year + 1900);
+ return buf;
+}
+
+TString YearToString(time_t when) {
+ struct tm theTm;
+ localtime_r(&when, &theTm);
+
+ return YearToString(theTm);
+}
+
+bool sscan_date(const char* date, struct tm& theTm) {
+ int year, mon, mday;
+ if (sscanf(date, "%4d%2d%2d", &year, &mon, &mday) != 3) {
+ return false;
+ }
+ theTm.tm_year = year - 1900;
+ theTm.tm_mon = mon - 1;
+ theTm.tm_mday = mday;
+ return true;
+}
+
+size_t FormatDate8601(char* buf, size_t len, time_t when) {
+ struct tm theTm;
+ struct tm* ret = GmTimeR(&when, &theTm);
+
+ if (ret) {
+ TMemoryOutput out(buf, len);
+
+ WriteTmToStream(out, theTm);
+ out << 'Z';
+
+ return out.Buf() - buf;
+ }
+
+ return 0;
+}
+
+void SleepUntil(TInstant instant) {
+ TInstant now = TInstant::Now();
+ if (instant <= now) {
+ return;
+ }
+ TDuration duration = instant - now;
+ Sleep(duration);
+}