1 | #pragma once |
2 | |
3 | #include <string.h> |
4 | #include <string> |
5 | #include <sstream> |
6 | #include <exception> |
7 | #include <common/DateLUT.h> |
8 | |
9 | |
10 | /** Stores a calendar date in broken-down form (year, month, day-in-month). |
11 | * Could be initialized from date in text form, like '2011-01-01' or from time_t with rounding to date. |
12 | * Also could be initialized from date in text form like '20110101... (only first 8 symbols are used). |
13 | * Could be implicitly casted to time_t. |
14 | * NOTE: Transforming between time_t and LocalDate is done in local time zone! |
15 | * |
16 | * When local time was shifted backwards (due to daylight saving time or whatever reason) |
17 | * - then to resolve the ambiguity of transforming to time_t, lowest of two possible values is selected. |
18 | * |
19 | * packed - for memcmp to work naturally (but because m_year is 2 bytes, on little endian, comparison is correct only before year 2047) |
20 | */ |
21 | class LocalDate |
22 | { |
23 | private: |
24 | unsigned short m_year; |
25 | unsigned char m_month; |
26 | unsigned char m_day; |
27 | |
28 | void init(time_t time) |
29 | { |
30 | const auto & date_lut = DateLUT::instance(); |
31 | const auto & values = date_lut.getValues(time); |
32 | |
33 | m_year = values.year; |
34 | m_month = values.month; |
35 | m_day = values.day_of_month; |
36 | } |
37 | |
38 | void init(const char * s, size_t length) |
39 | { |
40 | if (length < 8) |
41 | throw std::runtime_error("Cannot parse LocalDate: " + std::string(s, length)); |
42 | |
43 | m_year = (s[0] - '0') * 1000 + (s[1] - '0') * 100 + (s[2] - '0') * 10 + (s[3] - '0'); |
44 | |
45 | if (s[4] == '-') |
46 | { |
47 | if (length < 10) |
48 | throw std::runtime_error("Cannot parse LocalDate: " + std::string(s, length)); |
49 | m_month = (s[5] - '0') * 10 + (s[6] - '0'); |
50 | m_day = (s[8] - '0') * 10 + (s[9] - '0'); |
51 | } |
52 | else |
53 | { |
54 | m_month = (s[4] -'0') * 10 + (s[5] -'0'); |
55 | m_day = (s[6] - '0')* 10 + (s[7] -'0'); |
56 | } |
57 | } |
58 | |
59 | public: |
60 | explicit LocalDate(time_t time) |
61 | { |
62 | init(time); |
63 | } |
64 | |
65 | LocalDate(DayNum day_num) |
66 | { |
67 | const auto & values = DateLUT::instance().getValues(day_num); |
68 | m_year = values.year; |
69 | m_month = values.month; |
70 | m_day = values.day_of_month; |
71 | } |
72 | |
73 | LocalDate(unsigned short year_, unsigned char month_, unsigned char day_) |
74 | : m_year(year_), m_month(month_), m_day(day_) |
75 | { |
76 | } |
77 | |
78 | explicit LocalDate(const std::string & s) |
79 | { |
80 | init(s.data(), s.size()); |
81 | } |
82 | |
83 | LocalDate(const char * data, size_t length) |
84 | { |
85 | init(data, length); |
86 | } |
87 | |
88 | LocalDate() : m_year(0), m_month(0), m_day(0) |
89 | { |
90 | } |
91 | |
92 | LocalDate(const LocalDate &) noexcept = default; |
93 | LocalDate & operator= (const LocalDate &) noexcept = default; |
94 | |
95 | LocalDate & operator= (time_t time) |
96 | { |
97 | init(time); |
98 | return *this; |
99 | } |
100 | |
101 | operator time_t() const |
102 | { |
103 | return DateLUT::instance().makeDate(m_year, m_month, m_day); |
104 | } |
105 | |
106 | DayNum getDayNum() const |
107 | { |
108 | return DateLUT::instance().makeDayNum(m_year, m_month, m_day); |
109 | } |
110 | |
111 | operator DayNum() const |
112 | { |
113 | return getDayNum(); |
114 | } |
115 | |
116 | unsigned short year() const { return m_year; } |
117 | unsigned char month() const { return m_month; } |
118 | unsigned char day() const { return m_day; } |
119 | |
120 | void year(unsigned short x) { m_year = x; } |
121 | void month(unsigned char x) { m_month = x; } |
122 | void day(unsigned char x) { m_day = x; } |
123 | |
124 | bool operator< (const LocalDate & other) const |
125 | { |
126 | return 0 > memcmp(this, &other, sizeof(*this)); |
127 | } |
128 | |
129 | bool operator> (const LocalDate & other) const |
130 | { |
131 | return 0 < memcmp(this, &other, sizeof(*this)); |
132 | } |
133 | |
134 | bool operator<= (const LocalDate & other) const |
135 | { |
136 | return 0 >= memcmp(this, &other, sizeof(*this)); |
137 | } |
138 | |
139 | bool operator>= (const LocalDate & other) const |
140 | { |
141 | return 0 <= memcmp(this, &other, sizeof(*this)); |
142 | } |
143 | |
144 | bool operator== (const LocalDate & other) const |
145 | { |
146 | return 0 == memcmp(this, &other, sizeof(*this)); |
147 | } |
148 | |
149 | bool operator!= (const LocalDate & other) const |
150 | { |
151 | return !(*this == other); |
152 | } |
153 | |
154 | /// NOTE Inefficient. |
155 | std::string toString(char separator = '-') const |
156 | { |
157 | std::stringstream ss; |
158 | if (separator) |
159 | ss << year() << separator << (month() / 10) << (month() % 10) |
160 | << separator << (day() / 10) << (day() % 10); |
161 | else |
162 | ss << year() << (month() / 10) << (month() % 10) |
163 | << (day() / 10) << (day() % 10); |
164 | return ss.str(); |
165 | } |
166 | }; |
167 | |
168 | static_assert(sizeof(LocalDate) == 4); |
169 | |
170 | |
171 | inline std::ostream & operator<< (std::ostream & ostr, const LocalDate & date) |
172 | { |
173 | return ostr << date.year() |
174 | << '-' << (date.month() / 10) << (date.month() % 10) |
175 | << '-' << (date.day() / 10) << (date.day() % 10); |
176 | } |
177 | |
178 | |
179 | namespace std |
180 | { |
181 | inline string to_string(const LocalDate & date) |
182 | { |
183 | return date.toString(); |
184 | } |
185 | } |
186 | |