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 */
18class LocalDateTime
19{
20private:
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
75public:
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
169static_assert(sizeof(LocalDateTime) == 8);
170
171
172inline 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
186namespace std
187{
188inline string to_string(const LocalDateTime & datetime)
189{
190 stringstream str;
191 str << datetime;
192 return str.str();
193}
194}
195