/src/hobbes/include/hobbes/util/time.H
Line | Count | Source |
1 | | |
2 | | #ifndef HOBBES_UTIL_TIME_HPP_INCLUDED |
3 | | #define HOBBES_UTIL_TIME_HPP_INCLUDED |
4 | | |
5 | | #include <cstdint> |
6 | | #include <cstring> |
7 | | #include <ctime> |
8 | | #include <hobbes/util/str.H> |
9 | | #include <limits> |
10 | | #include <sstream> |
11 | | #include <string> |
12 | | |
13 | | namespace hobbes { |
14 | | |
15 | | /******* |
16 | | * timespans (a number of microseconds) |
17 | | * |
18 | | * e.g.: 5s, 20m, 4day2h3m30s200ms456us, ... |
19 | | * |
20 | | * *****/ |
21 | | // timespans are accumulated out of non-negative terms, so rather than let an |
22 | | // absurdly large literal (e.g. "9223372036854775807day") wrap around, we |
23 | | // saturate at the largest representable timespan |
24 | 0 | inline long saturatingTimespanScale(uint64_t n, long m) { |
25 | 0 | static const uint64_t maxts = static_cast<uint64_t>(std::numeric_limits<long>::max()); |
26 | |
|
27 | 0 | const uint64_t um = static_cast<uint64_t>(m); |
28 | 0 | if (um == 0 || n > (maxts / um)) { |
29 | 0 | return (um == 0) ? 0 : std::numeric_limits<long>::max(); |
30 | 0 | } |
31 | 0 | return static_cast<long>(n * um); |
32 | 0 | } |
33 | | |
34 | 0 | inline long saturatingTimespanAdd(long x, long y) { |
35 | 0 | static const long maxts = std::numeric_limits<long>::max(); |
36 | |
|
37 | 0 | if (x > (maxts - y)) { |
38 | 0 | return maxts; |
39 | 0 | } |
40 | 0 | return x + y; |
41 | 0 | } |
42 | | |
43 | 0 | inline long readTimespan(std::string s) { |
44 | 0 | long r = 0; |
45 | |
|
46 | 0 | while (!s.empty()) { |
47 | 0 | str::pair p = str::readWhile(&str::isDigit, s); |
48 | 0 | str::pair u = str::readWhile(&str::isNotDigit, p.second); |
49 | |
|
50 | 0 | long m = 1; |
51 | 0 | if (u.first == "us") { // microseconds |
52 | 0 | m = 1; |
53 | 0 | } else if (u.first == "ms") { // milliseconds |
54 | 0 | m = 1000; |
55 | 0 | } else if (u.first == "s") { // seconds |
56 | 0 | m = 1000000; |
57 | 0 | } else if (u.first == "m" || u.first == "min") { // minutes |
58 | 0 | m = 1000000*60; |
59 | 0 | } else if (u.first == "h" || u.first == "hour") { // hours |
60 | 0 | m = 1000000L*60*60; |
61 | 0 | } else if (u.first == "d" || u.first == "day") { // days |
62 | 0 | m = 1000000L*60*60*24; |
63 | 0 | } |
64 | | |
65 | | // an out-of-range digit sequence reads as UINT64_MAX, which saturates below |
66 | 0 | r = saturatingTimespanAdd(r, saturatingTimespanScale(str::to<uint64_t>(p.first), m)); |
67 | 0 | s = u.second; |
68 | 0 | } |
69 | |
|
70 | 0 | return r; |
71 | 0 | } |
72 | | |
73 | 0 | inline long readTimespan(const str::seq& ss) { |
74 | 0 | long r = 0; |
75 | 0 | for (const auto& s : ss) { |
76 | 0 | r = saturatingTimespanAdd(r, readTimespan(s)); |
77 | 0 | } |
78 | 0 | return r; |
79 | 0 | } |
80 | | |
81 | 0 | inline std::string showTimespan(long ts) { |
82 | 0 | static const long msus = 1000; |
83 | 0 | static const long sus = msus * 1000; |
84 | 0 | static const long mus = sus * 60; |
85 | 0 | static const long hus = mus * 60; |
86 | 0 | static const long dayus = hus * 24; |
87 | 0 |
|
88 | 0 | uint64_t x = labs(ts); |
89 | 0 | uint64_t d = x / dayus; x -= d*dayus; |
90 | 0 | uint64_t h = x / hus; x -= h*hus; |
91 | 0 | uint64_t m = x / mus; x -= m*mus; |
92 | 0 | uint64_t s = x / sus; x -= s*sus; |
93 | 0 | uint64_t ms = x / msus; x -= ms*msus; |
94 | 0 | uint64_t us = x; |
95 | 0 |
|
96 | 0 | std::ostringstream ss; |
97 | 0 | if (ts < 0) ss << "-"; |
98 | 0 | if (d > 0) ss << d << "d"; |
99 | 0 | if (h > 0) ss << h << "h"; |
100 | 0 | if (m > 0) ss << m << "m"; |
101 | 0 | if (s > 0 || (d == 0 && h == 0 && m == 0 && ms == 0 && us == 0)) { |
102 | 0 | ss << s << "s"; |
103 | 0 | } |
104 | 0 | if (ms > 0) ss << ms << "ms"; |
105 | 0 | if (us > 0) ss << us << "us"; |
106 | 0 |
|
107 | 0 | return ss.str(); |
108 | 0 | } |
109 | | |
110 | | /******* |
111 | | * times (microseconds since a base time) |
112 | | * |
113 | | * e.g.: 01:00:00.000000, 15:34:57.123456, ... |
114 | | * |
115 | | *******/ |
116 | 0 | inline long mkTime(int h, int m, int s, int u) { |
117 | 0 | tm t; |
118 | 0 | memset(&t, 0, sizeof(t)); |
119 | |
|
120 | 0 | t.tm_sec = s; |
121 | 0 | t.tm_min = m; |
122 | 0 | t.tm_hour = h; |
123 | 0 | t.tm_mday = 1; |
124 | 0 | t.tm_mon = 0; |
125 | 0 | t.tm_year = 70; |
126 | 0 | t.tm_isdst = -1; |
127 | |
|
128 | 0 | return (mktime(&t) * 1000 * 1000) + u; |
129 | 0 | } |
130 | | |
131 | 0 | inline long readTime(const std::string& x) { |
132 | 0 | str::pair h_msu = str::lsplit(x, ":"); |
133 | 0 | str::pair m_su = str::lsplit(h_msu.second, ":"); |
134 | 0 | str::pair s_u = str::lsplit(m_su.second, "."); |
135 | |
|
136 | 0 | int h = h_msu.first.empty() ? 0 : str::to<int>(h_msu.first); |
137 | 0 | int m = m_su.first.empty() ? 0 : str::to<int>(m_su.first); |
138 | 0 | int s = s_u.first.empty() ? 0 : str::to<int>(s_u.first); |
139 | 0 | int u = s_u.second.empty() ? 0 : (str::to<int>(s_u.second) * (s_u.second.size()==3?1000:1)); |
140 | |
|
141 | 0 | return mkTime(h,m,s,u); |
142 | 0 | } |
143 | | |
144 | 0 | inline std::string showTime(long x) { |
145 | 0 | int64_t s = x / (1000 * 1000); |
146 | 0 | int64_t us = x % (1000 * 1000); |
147 | 0 | if (us < 0) { |
148 | 0 | s -= 1; |
149 | 0 | us += 1000L * 1000L; |
150 | 0 | } |
151 | 0 |
|
152 | 0 | static char buf[256]; |
153 | 0 | strftime(buf, sizeof(buf), "%H:%M:%S", localtime(reinterpret_cast<time_t*>(&s))); |
154 | 0 |
|
155 | 0 | std::ostringstream uss; |
156 | 0 | uss << us; |
157 | 0 |
|
158 | 0 | std::ostringstream ss; |
159 | 0 | ss << buf; |
160 | 0 | ss << "."; |
161 | 0 | ss << std::string((uss.str().size() < 6) ? (6 - uss.str().size()) : 0, '0') << uss.str(); |
162 | 0 |
|
163 | 0 | return ss.str(); |
164 | 0 | } |
165 | | |
166 | | /******* |
167 | | * datetimes (microseconds since epoch) |
168 | | * |
169 | | * e.g.: 2015-01-01T01:00:00.000000, 1980-05-19T15:34:57.123456, ... |
170 | | * |
171 | | *******/ |
172 | 0 | inline long mkDateTime(int y, int mon, int d, int h, int min, int s, int u) { |
173 | 0 | tm t; |
174 | 0 | memset(&t, 0, sizeof(t)); |
175 | |
|
176 | 0 | t.tm_sec = s; |
177 | 0 | t.tm_min = min; |
178 | 0 | t.tm_hour = h; |
179 | 0 | t.tm_mday = d; |
180 | 0 | t.tm_mon = mon - 1; |
181 | 0 | t.tm_year = y - 1900; |
182 | 0 | t.tm_isdst = -1; |
183 | |
|
184 | 0 | return (mktime(&t) * 1000L * 1000L) + u; |
185 | 0 | } |
186 | | |
187 | 0 | inline long timeFromDateTime(long x) { |
188 | 0 | int64_t s = x / (1000L * 1000L); |
189 | 0 | int64_t us = x % (1000L * 1000L); |
190 | 0 | tm* t = localtime(reinterpret_cast<time_t*>(&s)); |
191 | 0 |
|
192 | 0 | if (t != nullptr) { |
193 | 0 | return mkTime(t->tm_hour, t->tm_min, t->tm_sec, us); |
194 | 0 | } else { |
195 | 0 | return mkTime(0, 0, 0, 0); |
196 | 0 | } |
197 | 0 | } |
198 | | |
199 | 0 | inline long dateFromDateTime(long x) { |
200 | 0 | int64_t s = x / (1000L * 1000L); |
201 | 0 | tm t; |
202 | 0 | localtime_r(reinterpret_cast<time_t*>(&s), &t); |
203 | 0 | |
204 | 0 | t.tm_sec = 0; |
205 | 0 | t.tm_min = 0; |
206 | 0 | t.tm_hour = 0; |
207 | 0 |
|
208 | 0 | return mktime(&t) * 1000L * 1000L; |
209 | 0 | } |
210 | | |
211 | 0 | inline long readDateTime(const std::string& x) { |
212 | 0 | str::pair y_mdThmsu = str::lsplit(x, "-"); |
213 | 0 | str::pair m_dThmsu = str::lsplit(y_mdThmsu.second, "-"); |
214 | 0 | str::pair d_Thmsu = str::lsplit(m_dThmsu.second, "T"); |
215 | 0 | str::pair h_msu = str::lsplit(d_Thmsu.second, ":"); |
216 | 0 | str::pair m_su = str::lsplit(h_msu.second, ":"); |
217 | 0 | str::pair s_u = str::lsplit(m_su.second, "."); |
218 | |
|
219 | 0 | int y = y_mdThmsu.first.empty() ? 0 : str::to<int>(y_mdThmsu.first); |
220 | 0 | int mon = m_dThmsu.first.empty() ? 0 : str::to<int>(m_dThmsu.first); |
221 | 0 | int d = d_Thmsu.first.empty() ? 0 : str::to<int>(d_Thmsu.first); |
222 | |
|
223 | 0 | int h = h_msu.first.empty() ? 0 : str::to<int>(h_msu.first); |
224 | 0 | int min = m_su.first.empty() ? 0 : str::to<int>(m_su.first); |
225 | 0 | int s = s_u.first.empty() ? 0 : str::to<int>(s_u.first); |
226 | 0 | int u = s_u.second.empty() ? 0 : (str::to<int>(s_u.second) * (s_u.second.size()==3?1000:1)); |
227 | |
|
228 | 0 | return mkDateTime(y, mon, d, h, min, s, u); |
229 | 0 | } |
230 | | |
231 | 0 | inline std::string showDateTime(long x) { |
232 | 0 | int64_t s = x / (1000L * 1000L); |
233 | 0 | int64_t us = x % (1000L * 1000L); |
234 | 0 | if (us < 0) { |
235 | 0 | s -= 1; |
236 | 0 | us += 1000L * 1000L; |
237 | 0 | } |
238 | 0 |
|
239 | 0 | static char buf[256]; |
240 | 0 | strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", localtime(reinterpret_cast<time_t*>(&s))); |
241 | 0 |
|
242 | 0 | std::ostringstream uss; |
243 | 0 | uss << us; |
244 | 0 |
|
245 | 0 | std::ostringstream ss; |
246 | 0 | ss << buf; |
247 | 0 | ss << "."; |
248 | 0 | ss << std::string((uss.str().size() < 6) ? (6 - uss.str().size()) : 0, '0') << uss.str(); |
249 | 0 |
|
250 | 0 | return ss.str(); |
251 | 0 | } |
252 | | |
253 | | } |
254 | | |
255 | | #endif |