#pragma once #include "systime.h" #include #include #include #include #include #include #include #include #include #include #if defined(__cpp_lib_three_way_comparison) #include #endif #include #include #include #include #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4244) // conversion from 'time_t' to 'long', possible loss of data #endif // _MSC_VER // Microseconds since epoch class TInstant; // Duration is microseconds. Could be used to store timeouts, for example. class TDuration; /// Current time static inline TInstant Now() noexcept; /// Use Now() method to obtain current time instead of *Seconds() unless you understand what are you doing. class TDateTimeParseException: public yexception { }; const int DATE_BUF_LEN = 4 + 2 + 2 + 1; // [YYYYMMDD*] constexpr long seconds(const struct tm& theTm) { return 60 * (60 * theTm.tm_hour + theTm.tm_min) + theTm.tm_sec; } void sprint_gm_date(char* buf, time_t when, long* sec = nullptr); bool sscan_date(const char* date, struct tm& theTm); const int DATE_8601_LEN = 21; // strlen("YYYY-MM-DDThh:mm:ssZ") = 20 + '\0' size_t FormatDate8601(char* buf, size_t len, time_t when); inline void sprint_date8601(char* buf, time_t when) { buf[FormatDate8601(buf, 64, when)] = 0; } bool ParseISO8601DateTimeDeprecated(const char* date, time_t& utcTime); bool ParseISO8601DateTimeDeprecated(const char* date, size_t dateLen, time_t& utcTime); bool ParseRFC822DateTimeDeprecated(const char* date, time_t& utcTime); bool ParseRFC822DateTimeDeprecated(const char* date, size_t dateLen, time_t& utcTime); bool ParseHTTPDateTimeDeprecated(const char* date, time_t& utcTime); bool ParseHTTPDateTimeDeprecated(const char* date, size_t dateLen, time_t& utcTime); bool ParseX509ValidityDateTimeDeprecated(const char* date, time_t& utcTime); bool ParseX509ValidityDateTimeDeprecated(const char* date, size_t dateLen, time_t& utcTime); bool ParseISO8601DateTime(const char* date, time_t& utcTime); bool ParseISO8601DateTime(const char* date, size_t dateLen, time_t& utcTime); bool ParseRFC822DateTime(const char* date, time_t& utcTime); bool ParseRFC822DateTime(const char* date, size_t dateLen, time_t& utcTime); bool ParseHTTPDateTime(const char* date, time_t& utcTime); bool ParseHTTPDateTime(const char* date, size_t dateLen, time_t& utcTime); bool ParseX509ValidityDateTime(const char* date, time_t& utcTime); bool ParseX509ValidityDateTime(const char* date, size_t dateLen, time_t& utcTime); constexpr long TVdiff(timeval r1, timeval r2) { return (1000000 * (r2.tv_sec - r1.tv_sec) + (r2.tv_usec - r1.tv_usec)); } TString Strftime(const char* format, const struct tm* tm); // Use functions below instead of sprint_date (check IGNIETFERRO-892 for details) void DateToString(char* buf, const struct tm& theTm); void DateToString(char* buf, time_t when, long* sec = nullptr); TString DateToString(const struct tm& theTm); TString DateToString(time_t when, long* sec = nullptr); // Year in format "YYYY", throws an exception if year not in range [0, 9999] TString YearToString(const struct tm& theTm); TString YearToString(time_t when); template class TTimeBase { public: using TValue = ui64; protected: constexpr TTimeBase(const TValue& value) noexcept : Value_(value) { } public: constexpr TTimeBase() noexcept : Value_(0) { } constexpr TTimeBase(const struct timeval& tv) noexcept : Value_(tv.tv_sec * (ui64)1000000 + tv.tv_usec) { } constexpr TValue GetValue() const noexcept { return Value_; } constexpr double SecondsFloat() const noexcept { return Value_ * (1 / 1000000.0); } constexpr double MillisecondsFloat() const noexcept { return Value_ * (1 / 1000.0); } constexpr TValue MicroSeconds() const noexcept { return Value_; } constexpr TValue MilliSeconds() const noexcept { return MicroSeconds() / 1000; } constexpr TValue Seconds() const noexcept { return MilliSeconds() / 1000; } constexpr TValue Minutes() const noexcept { return Seconds() / 60; } constexpr TValue Hours() const noexcept { return Minutes() / 60; } constexpr TValue Days() const noexcept { return Hours() / 24; } constexpr TValue NanoSeconds() const noexcept { return MicroSeconds() >= (Max() / (TValue)1000) ? Max() : MicroSeconds() * (TValue)1000; } constexpr ui32 MicroSecondsOfSecond() const noexcept { return MicroSeconds() % (TValue)1000000; } constexpr ui32 MilliSecondsOfSecond() const noexcept { return MicroSecondsOfSecond() / (TValue)1000; } constexpr ui32 NanoSecondsOfSecond() const noexcept { return MicroSecondsOfSecond() * (TValue)1000; } constexpr explicit operator bool() const noexcept { return Value_; } protected: TValue Value_; // microseconds count }; namespace NDateTimeHelpers { template struct TPrecisionHelper { using THighPrecision = ui64; }; template <> struct TPrecisionHelper { using THighPrecision = double; }; template <> struct TPrecisionHelper { using THighPrecision = double; }; } class TDuration: public TTimeBase { using TBase = TTimeBase; private: /** * private construct from microseconds */ constexpr explicit TDuration(TValue value) noexcept : TBase(value) { } public: constexpr TDuration() noexcept { } constexpr TDuration(const struct timeval& tv) noexcept : TBase(tv) { } /** * TDuration is compatible with std::chrono::duration: * it can be constructed and compared with std::chrono::duration. * But there are two significant and dangerous differences between them: * 1) TDuration is never negative and use saturation between 0 and maximum value. * std::chrono::duration can be negative and can overflow. * 2) TDuration uses integer number of microseconds. * std::chrono::duration is flexible, can be integer of floating point, * can have different precisions. * So when casted from std::chrono::duration to TDuration value is clamped and rounded. * In arithmetic operations std::chrono::duration argument is only rounded, * result is TDuration and it clamped and rounded. * In comparisons std::chrono::duration argument is rounded. */ template constexpr TDuration(std::chrono::duration duration) noexcept { static_assert( std::ratio_greater_equal::value && (!std::is_floating_point::value || std::ratio_greater::value), "Extremely likely it is loss of precision, because TDuration stores microseconds. " "Cast you duration explicitly to microseconds if you really need it."); if (duration.count() < 0) { *this = TDuration::Zero(); // clamp from the bottom } else { if #if !defined(__NVCC__) constexpr #endif /* if [constexpr] */ (std::ratio_greater::value || std::is_floating_point::value) { // clamp from the top using TCommonDuration = std::chrono::duration::type, TRatio>; constexpr auto maxDuration = std::chrono::duration(::Max()); if (std::chrono::duration_cast(duration) >= std::chrono::duration_cast(maxDuration)) { *this = TDuration::Max(); return; } } const TValue us = std::chrono::duration_cast>(duration).count(); *this = TDuration::MicroSeconds(us); } } static constexpr TDuration FromValue(TValue value) noexcept { return TDuration(value); } static constexpr TDuration MicroSeconds(ui64 us) noexcept { return TDuration(us); } /* noexcept(false) as conversion from T might throw, for example FromString("abc") */ template static constexpr TDuration MilliSeconds(T ms) noexcept(false) { return MicroSeconds((ui64)(typename NDateTimeHelpers::TPrecisionHelper::THighPrecision(ms) * 1000)); } using TBase::Days; using TBase::Hours; using TBase::MicroSeconds; using TBase::MilliSeconds; using TBase::Minutes; using TBase::Seconds; /// DeadLineFromTimeOut inline TInstant ToDeadLine() const; constexpr TInstant ToDeadLine(TInstant now) const; static constexpr TDuration Max() noexcept { return TDuration(::Max()); } static constexpr TDuration Zero() noexcept { return TDuration(); } /* noexcept(false) as conversion from T might throw, for example FromString("abc") */ template static constexpr TDuration Seconds(T s) noexcept(false) { return MilliSeconds(typename NDateTimeHelpers::TPrecisionHelper::THighPrecision(s) * 1000); } static constexpr TDuration Minutes(ui64 m) noexcept { return Seconds(m * 60); } static constexpr TDuration Hours(ui64 h) noexcept { return Minutes(h * 60); } static constexpr TDuration Days(ui64 d) noexcept { return Hours(d * 24); } /// parses strings like 10s, 15ms, 15.05s, 20us, or just 25 (s). See parser_ut.cpp for details [[nodiscard]] static TDuration Parse(const TStringBuf input); static bool TryParse(const TStringBuf input, TDuration& result); // note global Out method is defined for TDuration, so it could be written to IOutputStream as text template inline TDuration& operator+=(const T& t) noexcept { return (*this = (*this + t)); } template inline TDuration& operator-=(const T& t) noexcept { return (*this = (*this - t)); } template inline TDuration& operator*=(const T& t) noexcept { return (*this = (*this * t)); } template inline TDuration& operator/=(const T& t) noexcept { return (*this = (*this / t)); } TString ToString() const; }; Y_DECLARE_PODTYPE(TDuration); template <> struct THash { size_t operator()(const TDuration& key) const { return THash()(key.GetValue()); } }; /// TInstant and TDuration are guaranteed to have same precision class TInstant: public TTimeBase { using TBase = TTimeBase; private: /** * private construct from microseconds since epoch */ constexpr explicit TInstant(TValue value) noexcept : TBase(value) { } public: constexpr TInstant() noexcept { } constexpr TInstant(const struct timeval& tv) noexcept : TBase(tv) { } static constexpr TInstant FromValue(TValue value) noexcept { return TInstant(value); } static inline TInstant Now() { return TInstant::MicroSeconds(::MicroSeconds()); } using TBase::Days; using TBase::Hours; using TBase::MicroSeconds; using TBase::MilliSeconds; using TBase::Minutes; using TBase::Seconds; static constexpr TInstant Max() noexcept { return TInstant(::Max()); } static constexpr TInstant Zero() noexcept { return TInstant(); } /// us since epoch static constexpr TInstant MicroSeconds(ui64 us) noexcept { return TInstant(us); } /// ms since epoch static constexpr TInstant MilliSeconds(ui64 ms) noexcept { return MicroSeconds(ms * 1000); } /// seconds since epoch static constexpr TInstant Seconds(ui64 s) noexcept { return MilliSeconds(s * 1000); } /// minutes since epoch static constexpr TInstant Minutes(ui64 m) noexcept { return Seconds(m * 60); } /// hours since epoch static constexpr TInstant Hours(ui64 h) noexcept { return Minutes(h * 60); } /// days since epoch static constexpr TInstant Days(ui64 d) noexcept { return Hours(d * 24); } constexpr time_t TimeT() const noexcept { return (time_t)Seconds(); } inline struct timeval TimeVal() const noexcept { struct timeval tv; ::Zero(tv); tv.tv_sec = TimeT(); tv.tv_usec = MicroSecondsOfSecond(); return tv; } inline struct tm* LocalTime(struct tm* tm) const noexcept { time_t clock = Seconds(); return localtime_r(&clock, tm); } inline struct tm* GmTime(struct tm* tm) const noexcept { time_t clock = Seconds(); return GmTimeR(&clock, tm); } /** * Formats the instant using the UTC time zone, with microsecond precision. * * @returns An ISO 8601 formatted string, e.g. '2015-11-21T23:30:27.991669Z'. * @note Global Out method is defined to TInstant, so it can be written as text to IOutputStream. */ TString ToString() const; /** * Formats the instant using the UTC time zone. * * @returns An RFC822 formatted string, e.g. 'Sun, 06 Nov 1994 08:49:37 GMT'. */ TString ToRfc822String() const; /** * Formats the instant using the UTC time zone, with second precision. * * @returns An ISO 8601 formatted string, e.g. '2015-11-21T23:30:27Z'. */ TString ToStringUpToSeconds() const; /** * Formats the instant using the system time zone, with microsecond precision. * * @returns An ISO 8601 / RFC 3339 formatted string, * e.g. '2015-11-22T04:30:27.991669+05:00'. */ TString ToIsoStringLocal() const; /** * Formats the instant using the system time zone, with microsecond precision. * * @returns A semi-ISO 8601 formatted string with timezone without colon, * e.g. '2015-11-22T04:30:27.991669+0500'. */ TString ToStringLocal() const; /** * Formats the instant using the system time zone. * * @returns An RFC822 formatted string, e.g. 'Sun, 06 Nov 1994 08:49:37 MSK'. */ TString ToRfc822StringLocal() const; /** * Formats the instant using the system time zone, with second precision. * * @returns An ISO 8601 / RFC 3339 formatted string, * e.g. '2015-11-22T04:30:27+05:00'. */ TString ToIsoStringLocalUpToSeconds() const; /** * Formats the instant using the system time zone, with second precision. * * @returns A semi-ISO 8601 formatted string with timezone without colon, * e.g. '2015-11-22T04:30:27+0500'. */ TString ToStringLocalUpToSeconds() const; TString FormatLocalTime(const char* format) const noexcept; TString FormatGmTime(const char* format) const noexcept; /// See #TryParseIso8601. static TInstant ParseIso8601(TStringBuf); /// See #TryParseRfc822. static TInstant ParseRfc822(TStringBuf); /// See #TryParseHttp. static TInstant ParseHttp(TStringBuf); /// See #TryParseX509. static TInstant ParseX509Validity(TStringBuf); /// ISO 8601 Representation of Dates and Times /// /// @link https://www.iso.org/standard/40874.html Description of format. static bool TryParseIso8601(TStringBuf input, TInstant& instant); /// RFC 822 Date and Time specification /// /// @link https://tools.ietf.org/html/rfc822#section-5 Description of format. static bool TryParseRfc822(TStringBuf input, TInstant& instant); /// RFC 2616 3.3.1 Full Date /// /// @link https://tools.ietf.org/html/rfc2616#section-3.3.1 Description of format. static bool TryParseHttp(TStringBuf input, TInstant& instant); /// X.509 certificate validity time (see rfc5280 4.1.2.5.*) /// /// @link https://tools.ietf.org/html/rfc5280#section-4.1.2.5 Description of format. static bool TryParseX509(TStringBuf input, TInstant& instant); static TInstant ParseIso8601Deprecated(TStringBuf); static TInstant ParseRfc822Deprecated(TStringBuf); static TInstant ParseHttpDeprecated(TStringBuf); static TInstant ParseX509ValidityDeprecated(TStringBuf); static bool TryParseIso8601Deprecated(TStringBuf input, TInstant& instant); static bool TryParseRfc822Deprecated(TStringBuf input, TInstant& instant); static bool TryParseHttpDeprecated(TStringBuf input, TInstant& instant); static bool TryParseX509Deprecated(TStringBuf input, TInstant& instant); template inline TInstant& operator+=(const T& t) noexcept { return (*this = (*this + t)); } template inline TInstant& operator-=(const T& t) noexcept { return (*this = (*this - t)); } }; Y_DECLARE_PODTYPE(TInstant); template <> struct THash { size_t operator()(const TInstant& key) const { return THash()(key.GetValue()); } }; namespace NPrivate { template struct TPrintableLocalTime { TInstant MomentToPrint; constexpr explicit TPrintableLocalTime(TInstant momentToPrint) : MomentToPrint(momentToPrint) { } }; } /** @name Helpers for printing local times to `IOutputStream`s. * The FormatLocal* functions create an opaque object that, when written to * a `IOutputStream`, outputs this instant as an ISO 8601 formatted string * using the system time zone. * * @note The only reason behind this set of functions is to avoid excessive * allocations when you directly print the local time to a stream. * * If you need something beyond just printing the value or your code * is not performance-critical, feel free to use the corresponding * TInstant::ToString*() functions. */ ///@{ /// @see TInstant::ToIsoStringLocal() ::NPrivate::TPrintableLocalTime FormatIsoLocal(TInstant instant); /// @see TInstant::ToStringLocal() ::NPrivate::TPrintableLocalTime FormatLocal(TInstant instant); /// @see TInstant::ToIsoStringLocalUpToSeconds() ::NPrivate::TPrintableLocalTime FormatIsoLocalUpToSeconds(TInstant instant); /// @see TInstant::ToStringLocalUpToSeconds() ::NPrivate::TPrintableLocalTime FormatLocalUpToSeconds(TInstant instant); ///@} template static constexpr bool operator<(const TTimeBase& l, const TTimeBase& r) noexcept { return l.GetValue() < r.GetValue(); } template static constexpr bool operator<=(const TTimeBase& l, const TTimeBase& r) noexcept { return l.GetValue() <= r.GetValue(); } template static constexpr bool operator==(const TTimeBase& l, const TTimeBase& r) noexcept { return l.GetValue() == r.GetValue(); } template static constexpr bool operator!=(const TTimeBase& l, const TTimeBase& r) noexcept { return l.GetValue() != r.GetValue(); } template static constexpr bool operator>(const TTimeBase& l, const TTimeBase& r) noexcept { return l.GetValue() > r.GetValue(); } template static constexpr bool operator>=(const TTimeBase& l, const TTimeBase& r) noexcept { return l.GetValue() >= r.GetValue(); } namespace NDateTimeHelpers { template static constexpr T SumWithSaturation(T a, T b) { static_assert(!std::numeric_limits::is_signed, "expect !std::numeric_limits::is_signed"); return Max() - a < b ? Max() : a + b; } template static constexpr T DiffWithSaturation(T a, T b) { static_assert(!std::numeric_limits::is_signed, "expect !std::numeric_limits::is_signed"); return a < b ? 0 : a - b; } } constexpr TDuration operator-(const TInstant& l, const TInstant& r) noexcept { return TDuration::FromValue(::NDateTimeHelpers::DiffWithSaturation(l.GetValue(), r.GetValue())); } constexpr TInstant operator+(const TInstant& i, const TDuration& d) noexcept { return TInstant::FromValue(::NDateTimeHelpers::SumWithSaturation(i.GetValue(), d.GetValue())); } constexpr TInstant operator-(const TInstant& i, const TDuration& d) noexcept { return TInstant::FromValue(::NDateTimeHelpers::DiffWithSaturation(i.GetValue(), d.GetValue())); } constexpr TDuration operator-(const TDuration& l, const TDuration& r) noexcept { return TDuration::FromValue(::NDateTimeHelpers::DiffWithSaturation(l.GetValue(), r.GetValue())); } constexpr TDuration operator+(const TDuration& l, const TDuration& r) noexcept { return TDuration::FromValue(::NDateTimeHelpers::SumWithSaturation(l.GetValue(), r.GetValue())); } template constexpr bool operator==(const TDuration& l, const std::chrono::duration& r) noexcept { return r.count() >= 0 && l == TDuration(r); } #if defined(__cpp_lib_three_way_comparison) template constexpr std::strong_ordering operator<=>(const TDuration& l, const std::chrono::duration& r) noexcept { if (r.count() < 0) { return std::strong_ordering::greater; } return l.GetValue() <=> TDuration(r).GetValue(); } #else template constexpr bool operator<(const TDuration& l, const std::chrono::duration& r) noexcept { return r.count() >= 0 && l < TDuration(r); } template constexpr bool operator<=(const TDuration& l, const std::chrono::duration& r) noexcept { return r.count() >= 0 && l <= TDuration(r); } template constexpr bool operator!=(const TDuration& l, const std::chrono::duration& r) noexcept { return !(l == r); } template constexpr bool operator>(const TDuration& l, const std::chrono::duration& r) noexcept { return r.count() < 0 || l > TDuration(r); } template constexpr bool operator>=(const TDuration& l, const std::chrono::duration& r) noexcept { return r.count() < 0 || l >= TDuration(r); } template constexpr bool operator<(const std::chrono::duration& l, const TDuration& r) noexcept { return r > l; } template constexpr bool operator<=(const std::chrono::duration& l, const TDuration& r) noexcept { return r >= l; } template constexpr bool operator==(const std::chrono::duration& l, const TDuration& r) noexcept { return r == l; } template constexpr bool operator!=(const std::chrono::duration& l, const TDuration& r) noexcept { return r != l; } template constexpr bool operator>(const std::chrono::duration& l, const TDuration& r) noexcept { return r < l; } template constexpr bool operator>=(const std::chrono::duration& l, const TDuration& r) noexcept { return r >= l; } #endif template constexpr TDuration operator+(const TDuration& l, const std::chrono::duration& r) noexcept { return r < r.zero() ? l - TDuration(-r) : l + TDuration(r); } template constexpr TDuration operator+(const std::chrono::duration& l, const TDuration& r) noexcept { return r + l; } template constexpr TDuration operator-(const TDuration& l, const std::chrono::duration& r) noexcept { return l + (-r); } template constexpr TDuration operator-(const std::chrono::duration& l, const TDuration& r) noexcept { return TDuration(l) - r; } template constexpr TInstant operator+(const TInstant& l, const std::chrono::duration& r) noexcept { return r < r.zero() ? l - TDuration(-r) : l + TDuration(r); } template constexpr TInstant operator-(const TInstant& l, const std::chrono::duration& r) noexcept { return l + (-r); } template inline TDuration operator*(TDuration d, T t) noexcept { Y_ASSERT(t >= T()); Y_ASSERT(t == T() || Max() / t >= d.GetValue()); return TDuration::FromValue(d.GetValue() * t); } template <> inline TDuration operator*(TDuration d, double t) noexcept { Y_ASSERT(t >= 0 && MaxFloor() >= d.GetValue() * t); return TDuration::FromValue(d.GetValue() * t); } template <> inline TDuration operator*(TDuration d, float t) noexcept { return d * static_cast(t); } template inline TDuration operator*(T t, TDuration d) noexcept { return d * t; } template ::value, int> = 0> constexpr TDuration operator/(const TDuration& d, const T& t) noexcept { return TDuration::FromValue(d.GetValue() / t); } constexpr double operator/(const TDuration& x, const TDuration& y) noexcept { return static_cast(x.GetValue()) / static_cast(y.GetValue()); } inline TInstant TDuration::ToDeadLine() const { return ToDeadLine(TInstant::Now()); } constexpr TInstant TDuration::ToDeadLine(TInstant now) const { return now + *this; } void Sleep(TDuration duration); void SleepUntil(TInstant instant); static inline TInstant Now() noexcept { return TInstant::Now(); } #ifdef _MSC_VER #pragma warning(pop) #endif // _MSC_VER