#include "datetime.h" #include #include #include #include #include #include #include #include namespace NDatetime { const ui32 MonthDays[2][12] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, // nleap {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} // leap }; const ui32 MonthDaysNewYear[2][13] = { {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}, // nleap {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366} // leap }; void YDayToMonthAndDay(ui32 yday, bool isleap, ui32* month, ui32* mday) { const ui32* begin = MonthDaysNewYear[isleap] + 1; const ui32* end = begin + 12; // [31, ..., 365] or [31, ..., 366] (12 elements) const ui32* pos = UpperBound(begin, end, yday); Y_ENSURE(pos != end, "day no. " << yday << " does not exist in " << (isleap ? "leap" : "non-leap") << " year"); *month = pos - begin; *mday = yday - *(pos - 1) + 1; Y_ASSERT((*month < 12) && (1 <= *mday) && (*mday <= MonthDays[isleap][*month])); } struct TTimeData { i32 IsDst = 0; i32 GMTOff = 0; TTimeData(time_t t) { struct ::tm tt; ::localtime_r(&t, &tt); #ifndef _win_ GMTOff = tt.tm_gmtoff; #else TIME_ZONE_INFORMATION tz; switch (GetTimeZoneInformation(&tz)) { case TIME_ZONE_ID_UNKNOWN: GMTOff = tz.Bias * -60; break; case TIME_ZONE_ID_STANDARD: GMTOff = (tz.Bias + tz.StandardBias) * -60; break; case TIME_ZONE_ID_DAYLIGHT: GMTOff = (tz.Bias + tz.DaylightBias) * -60; break; default: break; } #endif IsDst = tt.tm_isdst; } }; TSimpleTM TSimpleTM::CurrentUTC() { return New((time_t)TInstant::MicroSeconds(InterpolatedMicroSeconds()).Seconds()); } TSimpleTM TSimpleTM::New(time_t t, i32 gmtoff, i8 isdst) { time_t tt = t + gmtoff + isdst * 3600; struct tm tmSys; Zero(tmSys); GmTimeR(&tt, &tmSys); tmSys.tm_isdst = isdst; #ifndef _win_ tmSys.tm_gmtoff = gmtoff; #endif return New(tmSys); } TSimpleTM TSimpleTM::NewLocal(time_t t) { TTimeData d(t); return New(t, d.GMTOff, d.IsDst); } TSimpleTM TSimpleTM::New(const struct tm& t) { TSimpleTM res; res.IsDst = t.tm_isdst; res.Sec = t.tm_sec; res.Min = t.tm_min; res.Hour = t.tm_hour; res.WDay = t.tm_wday; res.Mon = t.tm_mon; res.MDay = t.tm_mday; res.Year = t.tm_year; res.YDay = t.tm_yday; res.IsLeap = LeapYearAD(res.Year + 1900); #ifndef _win_ res.GMTOff = t.tm_gmtoff; #endif return res; } TSimpleTM& TSimpleTM::SetRealDate(ui32 year, ui32 mon, ui32 mday, ui32 hour, ui32 min, ui32 sec, i32 isdst) { mday = ::Max(mday, 1); mon = ::Min(::Max(mon, 1), 12); year = ::Max(year, 1900); IsLeap = LeapYearAD(year); Year = year - 1900; Mon = mon - 1; MDay = ::Min(mday, MonthDays[IsLeap][Mon]); Hour = Max() == hour ? Hour : ::Min(hour, 23); Min = Max() == min ? Min : ::Min(min, 59); Sec = Max() == sec ? Sec : ::Min(sec, 60); IsDst = isdst; return RegenerateFields(); } TSimpleTM& TSimpleTM::RegenerateFields() { return *this = New(AsTimeT(), GMTOff, IsDst); } TSimpleTM& TSimpleTM::Add(EField f, i32 amount) { if (!amount) { return *this; } switch (f) { default: return *this; case F_DAY: amount *= 24; [[fallthrough]]; case F_HOUR: amount *= 60; [[fallthrough]]; case F_MIN: amount *= 60; [[fallthrough]]; case F_SEC: { return *this = New(AsTimeT() + amount, GMTOff, IsDst); } case F_YEAR: { i32 y = amount + (i32)Year; y = ::Min(Max(y, 0), 255 /*max year*/); // YDay may correspond to different MDay if it's March or greater and the years have different leap status if (Mon > 1) { YDay += (i32)LeapYearAD(RealYear()) - (i32)LeapYearAD(RealYear()); } Year = y; IsLeap = LeapYearAD(RealYear()); return RegenerateFields(); } case F_MON: { i32 m = amount + Mon; i32 y = (m < 0 ? (-12 + m) : m) / 12; m = m - y * 12; if (y) { Add(F_YEAR, y); } if (m >= 0 && m < 12) { MDay = ::Min(MonthDays[IsLeap][m], MDay); Mon = m; } return RegenerateFields(); } } } TString TSimpleTM::ToString(const char* fmt) const { struct tm t = *this; return Strftime(fmt, &t); } time_t TSimpleTM::AsTimeT() const { struct tm t = AsStructTmLocal(); return TimeGM(&t) - GMTOff - IsDst * 3600; } struct tm TSimpleTM::AsStructTmUTC() const { struct tm res; Zero(res); time_t t = AsTimeT(); return *GmTimeR(&t, &res); } struct tm TSimpleTM::AsStructTmLocal() const { struct tm t; Zero(t); t.tm_isdst = IsDst; t.tm_sec = Sec; t.tm_min = Min; t.tm_hour = Hour; t.tm_wday = WDay; t.tm_mon = Mon; t.tm_mday = MDay; t.tm_year = Year; t.tm_yday = YDay; #ifndef _win_ t.tm_gmtoff = GMTOff; #endif return t; } } template <> void In(IInputStream& in, TMonth& t) { char buf[4]; LoadPodArray(&in, buf, 4); t.Year = FromString(buf, 4); LoadPodArray(&in, buf, 2); t.Month = ui8(FromString(buf, 2)) - 1; } template <> void Out(IOutputStream& o, const TMonth& t) { o << t.Year << Sprintf("%.2hu", (ui16)(t.Month + 1)); } template <> TMonth FromStringImpl(const char* s, size_t len) { TMonth res; TMemoryInput in(s, len); in >> res; return res; }