1 | #pragma once |
2 | |
3 | #include <string> |
4 | #include <iomanip> |
5 | #include <exception> |
6 | #include <common/DateLUT.h> |
7 | #include <common/LocalDate.h> |
8 | |
9 | |
10 | /** Stores calendar date and time in broken-down form. |
11 | * Could be initialized from date and time in text form like '2011-01-01 00:00:00' or from time_t. |
12 | * Could be implicitly casted to time_t. |
13 | * NOTE: Transforming between time_t and LocalDate is done in local time zone! |
14 | * |
15 | * When local time was shifted backwards (due to daylight saving time or whatever reason) |
16 | * - then to resolve the ambiguity of transforming to time_t, lowest of two possible values is selected. |
17 | */ |
18 | class LocalDateTime |
19 | { |
20 | private: |
21 | unsigned short m_year; |
22 | unsigned char m_month; |
23 | unsigned char m_day; |
24 | unsigned char m_hour; |
25 | unsigned char m_minute; |
26 | unsigned char m_second; |
27 | |
28 | /// For struct to fill 8 bytes and for safe invocation of memcmp. |
29 | /// NOTE We may use attribute packed instead, but it is less portable. |
30 | unsigned char pad = 0; |
31 | |
32 | void init(time_t time) |
33 | { |
34 | if (unlikely(time > DATE_LUT_MAX || time == 0)) |
35 | { |
36 | m_year = 0; |
37 | m_month = 0; |
38 | m_day = 0; |
39 | m_hour = 0; |
40 | m_minute = 0; |
41 | m_second = 0; |
42 | |
43 | return; |
44 | } |
45 | |
46 | const auto & date_lut = DateLUT::instance(); |
47 | const auto & values = date_lut.getValues(time); |
48 | |
49 | m_year = values.year; |
50 | m_month = values.month; |
51 | m_day = values.day_of_month; |
52 | m_hour = date_lut.toHour(time); |
53 | m_minute = date_lut.toMinute(time); |
54 | m_second = date_lut.toSecond(time); |
55 | |
56 | (void)pad; /// Suppress unused private field warning. |
57 | } |
58 | |
59 | void init(const char * s, size_t length) |
60 | { |
61 | if (length < 19) |
62 | throw std::runtime_error("Cannot parse LocalDateTime: " + std::string(s, length)); |
63 | |
64 | m_year = (s[0] - '0') * 1000 + (s[1] - '0') * 100 + (s[2] - '0') * 10 + (s[3] - '0'); |
65 | m_month = (s[5] - '0') * 10 + (s[6] - '0'); |
66 | m_day = (s[8] - '0') * 10 + (s[9] - '0'); |
67 | |
68 | m_hour = (s[11] - '0') * 10 + (s[12] - '0'); |
69 | m_minute = (s[14] - '0') * 10 + (s[15] - '0'); |
70 | m_second = (s[17] - '0') * 10 + (s[18] - '0'); |
71 | |
72 | (void)pad; |
73 | } |
74 | |
75 | public: |
76 | explicit LocalDateTime(time_t time) |
77 | { |
78 | init(time); |
79 | } |
80 | |
81 | LocalDateTime(unsigned short year_, unsigned char month_, unsigned char day_, |
82 | unsigned char hour_, unsigned char minute_, unsigned char second_) |
83 | : m_year(year_), m_month(month_), m_day(day_), m_hour(hour_), m_minute(minute_), m_second(second_) |
84 | { |
85 | } |
86 | |
87 | explicit LocalDateTime(const std::string & s) |
88 | { |
89 | if (s.size() < 19) |
90 | throw std::runtime_error("Cannot parse LocalDateTime: " + s); |
91 | |
92 | init(s.data(), s.size()); |
93 | } |
94 | |
95 | LocalDateTime() : m_year(0), m_month(0), m_day(0), m_hour(0), m_minute(0), m_second(0) |
96 | { |
97 | } |
98 | |
99 | LocalDateTime(const char * data, size_t length) |
100 | { |
101 | init(data, length); |
102 | } |
103 | |
104 | LocalDateTime(const LocalDateTime &) noexcept = default; |
105 | LocalDateTime & operator= (const LocalDateTime &) noexcept = default; |
106 | |
107 | LocalDateTime & operator= (time_t time) |
108 | { |
109 | init(time); |
110 | return *this; |
111 | } |
112 | |
113 | operator time_t() const |
114 | { |
115 | return m_year == 0 |
116 | ? 0 |
117 | : DateLUT::instance().makeDateTime(m_year, m_month, m_day, m_hour, m_minute, m_second); |
118 | } |
119 | |
120 | unsigned short year() const { return m_year; } |
121 | unsigned char month() const { return m_month; } |
122 | unsigned char day() const { return m_day; } |
123 | unsigned char hour() const { return m_hour; } |
124 | unsigned char minute() const { return m_minute; } |
125 | unsigned char second() const { return m_second; } |
126 | |
127 | void year(unsigned short x) { m_year = x; } |
128 | void month(unsigned char x) { m_month = x; } |
129 | void day(unsigned char x) { m_day = x; } |
130 | void hour(unsigned char x) { m_hour = x; } |
131 | void minute(unsigned char x) { m_minute = x; } |
132 | void second(unsigned char x) { m_second = x; } |
133 | |
134 | LocalDate toDate() const { return LocalDate(m_year, m_month, m_day); } |
135 | |
136 | LocalDateTime toStartOfDate() { return LocalDateTime(m_year, m_month, m_day, 0, 0, 0); } |
137 | |
138 | bool operator< (const LocalDateTime & other) const |
139 | { |
140 | return 0 > memcmp(this, &other, sizeof(*this)); |
141 | } |
142 | |
143 | bool operator> (const LocalDateTime & other) const |
144 | { |
145 | return 0 < memcmp(this, &other, sizeof(*this)); |
146 | } |
147 | |
148 | bool operator<= (const LocalDateTime & other) const |
149 | { |
150 | return 0 >= memcmp(this, &other, sizeof(*this)); |
151 | } |
152 | |
153 | bool operator>= (const LocalDateTime & other) const |
154 | { |
155 | return 0 <= memcmp(this, &other, sizeof(*this)); |
156 | } |
157 | |
158 | bool operator== (const LocalDateTime & other) const |
159 | { |
160 | return 0 == memcmp(this, &other, sizeof(*this)); |
161 | } |
162 | |
163 | bool operator!= (const LocalDateTime & other) const |
164 | { |
165 | return !(*this == other); |
166 | } |
167 | }; |
168 | |
169 | static_assert(sizeof(LocalDateTime) == 8); |
170 | |
171 | |
172 | inline std::ostream & operator<< (std::ostream & ostr, const LocalDateTime & datetime) |
173 | { |
174 | ostr << std::setfill('0') << std::setw(4) << datetime.year(); |
175 | |
176 | ostr << '-' << (datetime.month() / 10) << (datetime.month() % 10) |
177 | << '-' << (datetime.day() / 10) << (datetime.day() % 10) |
178 | << ' ' << (datetime.hour() / 10) << (datetime.hour() % 10) |
179 | << ':' << (datetime.minute() / 10) << (datetime.minute() % 10) |
180 | << ':' << (datetime.second() / 10) << (datetime.second() % 10); |
181 | |
182 | return ostr; |
183 | } |
184 | |
185 | |
186 | namespace std |
187 | { |
188 | inline string to_string(const LocalDateTime & datetime) |
189 | { |
190 | stringstream str; |
191 | str << datetime; |
192 | return str.str(); |
193 | } |
194 | } |
195 | |