#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);
}