#pragma once #include #include #include #include #include #include #include namespace NDatetime { extern const ui32 MonthDays[2][12]; // !leapYear; !!leapYear extern const ui32 MonthDaysNewYear[2][13]; // !leapYear; !!leapYear inline ui32 YearDaysAD(ui32 year) { year = Max(year, 1) - 1; // 1 AD comes straight after 1 BC, no 0 AD return year * 365 + year / 4 - year / 100 + year / 400; } inline bool LeapYearAD(ui32 year) { return (!(year % 4) && (year % 100)) || !(year % 400); } inline ui32 YDayFromMonthAndDay(ui32 month /*0 - based*/, ui32 mday /*1 - based*/, bool isleap) { return MonthDaysNewYear[isleap][Min(month, (ui32)11u)] + mday - 1; } void YDayToMonthAndDay(ui32 yday /*0 - based*/, bool isleap, ui32* month /*0 - based*/, ui32* mday /*1 - based*/); struct TSimpleTM { enum EField { F_NONE = 0, F_SEC, F_MIN, F_HOUR, F_DAY, F_MON, F_YEAR }; i32 GMTOff = 0; // -43200 - 50400 seconds ui16 Year = 0; // from 1900 ui16 YDay = 0; // 0-365 ui8 Mon = 0; // 0-11 ui8 MDay = 0; // 1-31 ui8 WDay = 0; // 0-6 ui8 Hour = 0; // 0-23 ui8 Min = 0; // 0-59 ui8 Sec = 0; // 0-60 - doesn't care for leap seconds. Most of the time it's ok. i8 IsDst = 0; // -1/0/1 bool IsLeap = false; public: static TSimpleTM New(time_t t = 0, i32 gmtoff = 0, i8 isdst = 0); static TSimpleTM NewLocal(time_t = 0); static TSimpleTM New(const struct tm&); static TSimpleTM CurrentUTC(); TSimpleTM() = default; TSimpleTM(ui32 year, ui32 mon, ui32 day, ui32 h = 0, ui32 m = 0, ui32 s = 0) { Zero(*this); SetRealDate(year, mon, day, h, m, s); } // keeps the object consistent TSimpleTM& Add(EField f, i32 amount = 1); TString ToString(const char* fmt = "%a, %d %b %Y %H:%M:%S %z") const; TSimpleTM& ToUTC() { return *this = New(AsTimeT()); } bool IsUTC() const { return !IsDst && !GMTOff; } time_t AsTimeT() const; operator time_t() const { return AsTimeT(); } struct tm AsStructTmLocal() const; struct tm AsStructTmUTC() const; operator struct tm() const { return AsStructTmLocal(); } ui32 RealYear() const { return ui32(Year + 1900); } ui32 RealMonth() const { return ui32(Mon + 1); } TSimpleTM& SetRealDate(ui32 year, ui32 mon, ui32 mday, ui32 hour = -1, ui32 min = -1, ui32 sec = -1, i32 isdst = 0); // regenerates all fields from Year, MDay, Hour, Min, Sec, IsDst, GMTOffset TSimpleTM& RegenerateFields(); friend bool operator==(const TSimpleTM& a, const TSimpleTM& b) { return a.AsTimeT() == b.AsTimeT(); } friend bool operator==(const TSimpleTM& s, const struct tm& t) { return s == New(t); } friend bool operator==(const struct tm& t, const TSimpleTM& s) { return s == t; } friend bool operator!=(const TSimpleTM& a, const TSimpleTM& b) { return !(a == b); } friend bool operator!=(const TSimpleTM& s, const struct tm& t) { return !(s == t); } friend bool operator!=(const struct tm& t, const TSimpleTM& s) { return s != t; } }; } // namespace NDatetime inline TString date2str(const time_t date) { struct tm dateTm; memset(&dateTm, 0, sizeof(dateTm)); localtime_r(&date, &dateTm); char buf[9]; strftime(buf, sizeof(buf), "%Y%m%d", &dateTm); return TString(buf); } inline time_t str2date(const TString& dateStr) { struct tm dateTm; memset(&dateTm, 0, sizeof(tm)); strptime(dateStr.data(), "%Y%m%d", &dateTm); return mktime(&dateTm); } // checks whether time2 > time1 and close enough to it inline bool AreTimesSeqAndClose(time_t time1, time_t time2, time_t closeInterval = 10) { return (time2 - time1) <= closeInterval; } // checks whether time2 and time1 are close enough inline bool AreTimesClose(time_t time1, time_t time2, time_t closeInterval = 10) { return std::abs(time2 - time1) <= closeInterval; } //////////////////////////////// struct TMonth { ui16 Year; ui8 Month; TMonth(ui16 year = 0, ui8 month = 0) : Year(year) , Month(month) { } TMonth operator-(ui16 n) { if (n <= Month) { return TMonth(Year, Month - (ui8)n); } else { n -= Month; return (n % 12) ? TMonth(Year - 1 - (n / 12), 12 - (n % 12)) : TMonth(Year - (n / 12), 0); } } }; Y_DECLARE_PODTYPE(NDatetime::TSimpleTM);