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 */
21class LocalDate
22{
23private:
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
59public:
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
168static_assert(sizeof(LocalDate) == 4);
169
170
171inline 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
179namespace std
180{
181inline string to_string(const LocalDate & date)
182{
183 return date.toString();
184}
185}
186