aboutsummaryrefslogtreecommitdiffstats
path: root/util/draft/datetime.h
blob: 677de685a4f431ec101acd915c79bd1cf092375e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#pragma once

#include <util/generic/algorithm.h>
#include <util/generic/string.h>
#include <util/generic/yexception.h>
#include <util/generic/ymath.h>
#include <util/datetime/base.h>

#include <cstdlib>

#include <time.h>

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<ui32>(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);