diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /util/datetime/base.cpp | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'util/datetime/base.cpp')
-rw-r--r-- | util/datetime/base.cpp | 336 |
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); +} |