1
2
3
4
5
6 #include <soulng/util/Time.hpp>
7 #include <soulng/util/TextUtils.hpp>
8 #include <boost/lexical_cast.hpp>
9 #include <stdexcept>
10 #include <ctime>
11 #include <chrono>
12
13 namespace soulng { namespace util {
14
15 int GetMonthDays(Month month, int year)
16 {
17 static int monthDays[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
18 if (month == Month::february&&( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0))
19 {
20 return 29;
21 }
22 return monthDays[static_cast<int>(month)];
23 }
24
25 Date Date::AddDays(int n)
26 {
27 if (n > 0)
28 {
29 int d = day + n;
30 Month m = month;
31 int y = year;
32 int md = GetMonthDays(m, y);
33 while (d > md)
34 {
35 d = d - md;
36 if (m == Month::december)
37 {
38 m = Month::january;
39 ++y;
40 }
41 else
42 {
43 m = static_cast<Month>(static_cast<int>(m) + 1);
44 }
45 md = GetMonthDays(m, y);
46 }
47 return Date(y, m, d);
48 }
49 else if (n < 0)
50 {
51 int d = day + n;
52 Month m = month;
53 int y = year;
54 while (d < 1)
55 {
56 if (m == Month::january)
57 {
58 m = Month::december;
59 --y;
60 }
61 else
62 {
63 m = static_cast<Month>(static_cast<int>(m) - 1);
64 }
65 d = d + GetMonthDays(m, y);
66 }
67 return Date(y, m, d);
68 }
69 else
70 {
71 return *this;
72 }
73 }
74
75 Date Date::AddMonths(int n)
76 {
77 if (n > 0)
78 {
79 int m = static_cast<int>(month) + n;
80 int y = year;
81 int d = day;
82 while (m > 12)
83 {
84 m = m - 12;
85 ++y;
86 }
87 Month mnth = static_cast<Month>(m);
88 int md = GetMonthDays(mnth, y);
89 if (d > md)
90 {
91 d = md;
92 }
93 return Date(y, mnth, d);
94 }
95 else if (n < 0)
96 {
97 int m = static_cast<int>(month) + n;
98 int y = year;
99 int d = day;
100 while (m < 1)
101 {
102 m = m + 12;
103 --y;
104 }
105 Month mnth = static_cast<Month>(m);
106 int md = GetMonthDays(mnth, y);
107 if (d > md)
108 {
109 d = md;
110 }
111 return Date(y, mnth, d);
112 }
113 else
114 {
115 return *this;
116 }
117 }
118
119 Date Date::AddYears(int n)
120 {
121 int y = year + n;
122 int d = day;
123 int md = GetMonthDays(month, y);
124 if (d > md)
125 {
126 d = md;
127 }
128 return Date(y, month, d);
129 }
130
131 std::string Date::ToString() const
132 {
133 return ToString(false);
134 }
135
136 std::string Date::ToString(bool omitDashes) const
137 {
138 std::string date;
139 date.append(1, static_cast<char>(static_cast<int16_t>('0') + ((year / 1000) % 10)));
140 date.append(1, static_cast<char>(static_cast<int16_t>('0') + ((year / 100) % 10)));
141 date.append(1, static_cast<char>(static_cast<int16_t>('0') + ((year / 10) % 10)));
142 date.append(1, static_cast<char>(static_cast<int16_t>('0') + (year % 10)));
143 if (!omitDashes)
144 {
145 date.append(1, '-');
146 }
147 date.append(1, static_cast<char>(static_cast<int8_t>('0') + ((static_cast<int8_t>(month) / 10) % 10)));
148 date.append(1, static_cast<char>(static_cast<int8_t>('0') + (static_cast<int8_t>(month) % 10)));
149 if (!omitDashes)
150 {
151 date.append(1, '-');
152 }
153 date.append(1, static_cast<char>(static_cast<int8_t>('0') + ((day / 10) % 10)));
154 date.append(1, static_cast<char>(static_cast<int8_t>('0') + (day % 10)));
155 return date;
156 }
157
158 Date GetCurrentDate()
159 {
160 std::time_t currentTime;
161 std::time(¤tTime);
162 struct tm* localTime = nullptr;
163 localTime = std::localtime(¤tTime);
164 return Date(1900 + localTime->tm_year, static_cast<Month>(1 + localTime->tm_mon), static_cast<int8_t>(localTime->tm_mday));
165 }
166
167 bool operator==(const Date& left, const Date& right)
168 {
169 return left.Year() == right.Year() && left.GetMonth() == right.GetMonth() && left.Day() == right.Day();
170 }
171
172 bool operator<(const Date& left, const Date& right)
173 {
174 if (left.Year() < right.Year()) return true;
175 if (left.Year() > right.Year()) return false;
176 if (left.GetMonth() < right.GetMonth()) return true;
177 if (left.GetMonth() > right.GetMonth()) return false;
178 return left.Day() < right.Day();
179 }
180
181 void ThrowRuntimeError(const std::string& message)
182 {
183 throw std::runtime_error(message);
184 }
185
186 void ThrowInvalidDate(const std::string& s)
187 {
188 ThrowRuntimeError("cannot parse date from string '" + s + "': not in format YYYY-MM-DD or YYYYMMDD");
189 }
190
191 void ThrowInvalidDateTime(const std::string& s)
192 {
193 ThrowRuntimeError("cannot parse date time from string '" + s + "': not in format YYYY[-]MM[-]DD or YYYY[-]MM[-]DDTHH[[:]MM[[:]SS]");
194 }
195
196 Date ParseDate(const std::string& dateStr, int& dateEnd)
197 {
198 if (dateStr.length() < 4 + 2 + 2)
199 {
200 ThrowInvalidDate(dateStr);
201 }
202 int16_t year = boost::lexical_cast<short>(dateStr.substr(0, 4));
203 int monthStart = 4;
204 if (dateStr[4] == '-')
205 {
206 ++monthStart;
207 }
208 if (dateStr.length() < monthStart + 2)
209 {
210 ThrowInvalidDate(dateStr);
211 }
212 int8_t month = static_cast<int8_t>(boost::lexical_cast<int>(dateStr.substr(monthStart, 2)));
213 if (month < 1 || month > 12)
214 {
215 ThrowInvalidDate(dateStr);
216 }
217 int dayStart = monthStart + 2;
218 if (dateStr[dayStart] == '-')
219 {
220 ++dayStart;
221 }
222 if (dateStr.length() < dayStart + 2)
223 {
224 ThrowInvalidDate(dateStr);
225 }
226 int8_t day = static_cast<int8_t>(boost::lexical_cast<int>(dateStr.substr(dayStart, 2)));
227 if (day < 1 || day > 31)
228 {
229 ThrowInvalidDate(dateStr);
230 }
231 dateEnd = dayStart + 2;
232 return Date(year, static_cast<Month>(month), day);
233 }
234
235 Date ParseDate(const std::string& dateStr)
236 {
237 int dateEnd = 0;
238 return ParseDate(dateStr, dateEnd);
239 }
240
241 std::string DateTime::ToString() const
242 {
243 return ToString(false, false, false, false);
244 }
245
246 std::string DateTime::ToString(bool omitDashes, bool omitColons, bool omitMins, bool omitSecs) const
247 {
248 std::string dateTime;
249 dateTime.append(date.ToString(omitDashes));
250 dateTime.append(1, 'T');
251 int32_t hh = Hours() % 24;
252 int32_t mm = Minutes() % 60;
253 int32_t ss = Seconds() % 60;
254 dateTime.append(1, static_cast<char>(static_cast<int32_t>('0') + ((hh / 10) % 10)));
255 dateTime.append(1, static_cast<char>(static_cast<int32_t>('0') + (hh % 10)));
256 if (!omitMins)
257 {
258 if (!omitColons)
259 {
260 dateTime.append(1, ':');
261 }
262 dateTime.append(1, static_cast<char>(static_cast<int32_t>('0') + ((mm / 10) % 10)));
263 dateTime.append(1, static_cast<char>(static_cast<int>('0') + (mm % 10)));
264 if (!omitSecs)
265 {
266 if (!omitColons)
267 {
268 dateTime.append(1, ':');
269 }
270 dateTime.append(1, static_cast<char>(static_cast<int32_t>('0') + ((ss / 10) % 10)));
271 dateTime.append(1, static_cast<char>(static_cast<int32_t>('0') + (ss % 10)));
272 }
273 }
274 return dateTime;
275 }
276
277 std::string FormatTimeMs(int32_t milliseconds)
278 {
279 int32_t hh = milliseconds / 3600000;
280 int32_t mm = milliseconds / 60000 % 60;
281 int32_t ss = milliseconds / 1000 % 60;
282 int32_t ms = milliseconds % 1000;
283 std::string time;
284 time.append(1, static_cast<char>(static_cast<int32_t>('0') + ((hh / 10) % 10)));
285 time.append(1, static_cast<char>(static_cast<int32_t>('0') + (hh % 10)));
286 time.append(1, ':');
287 time.append(1, static_cast<char>(static_cast<int32_t>('0') + ((mm / 10) % 10)));
288 time.append(1, static_cast<char>(static_cast<int>('0') + (mm % 10)));
289 time.append(1, ':');
290 time.append(1, static_cast<char>(static_cast<int32_t>('0') + ((ss / 10) % 10)));
291 time.append(1, static_cast<char>(static_cast<int32_t>('0') + (ss % 10)));
292 time.append(1, '.');
293 time.append(1, static_cast<char>(static_cast<int32_t>('0') + ((ms / 100) % 10)));
294 time.append(1, static_cast<char>(static_cast<int32_t>('0') + ((ms / 10) % 10)));
295 time.append(1, static_cast<char>(static_cast<int32_t>('0') + (ms % 10)));
296 return time;
297 }
298
299 DateTime GetCurrentDateTime()
300 {
301 std::time_t currentTime;
302 std::time(¤tTime);
303 struct tm* localTime = nullptr;
304 localTime = std::localtime(¤tTime);
305 return DateTime(Date(1900 + localTime->tm_year, static_cast<Month>(1 + localTime->tm_mon), static_cast<int8_t>(localTime->tm_mday)), localTime->tm_hour * 3600 + localTime->tm_min * 60 + localTime->tm_sec);
306 }
307
308 bool operator==(const DateTime& left, const DateTime& right)
309 {
310 return left.GetDate() == right.GetDate() && left.Seconds() == right.Seconds();
311 }
312
313 bool operator<(const DateTime& left, const DateTime& right)
314 {
315 if (left.GetDate() < right.GetDate()) return true;
316 if (left.GetDate() > right.GetDate()) return false;
317 return left.Seconds() < right.Seconds();
318 }
319
320 DateTime ParseDateTime(const std::string& dateTimeStr)
321 {
322 int dateEnd = 0;
323 Date date = ParseDate(dateTimeStr, dateEnd);
324 int hours = 0;
325 int mins = 0;
326 int secs = 0;
327 if (dateTimeStr.length() > dateEnd)
328 {
329 if (dateTimeStr[dateEnd] == 'T')
330 {
331 int hoursStart = dateEnd + 1;
332 hours = boost::lexical_cast<int>(dateTimeStr.substr(hoursStart, 2));
333 if (hours < 0 || hours > 24)
334 {
335 ThrowInvalidDateTime(dateTimeStr);
336 }
337 if (dateTimeStr.length() > hoursStart + 2)
338 {
339 int minsStart = hoursStart + 2;
340 if (dateTimeStr[minsStart] == ':')
341 {
342 ++minsStart;
343 }
344 mins = boost::lexical_cast<int>(dateTimeStr.substr(minsStart, 2));
345 if (mins < 0 || mins >= 60)
346 {
347 ThrowInvalidDateTime(dateTimeStr);
348 }
349 if (dateTimeStr.length() > minsStart + 2)
350 {
351 int secsStart = minsStart + 2;
352 if (dateTimeStr[secsStart] == ':')
353 {
354 ++secsStart;
355 }
356 secs = boost::lexical_cast<int>(dateTimeStr.substr(secsStart, 2));
357 if (secs < 0 || secs > 60)
358 {
359 ThrowInvalidDateTime(dateTimeStr);
360 }
361 }
362 }
363 }
364 }
365 int totalSecs = hours * 3600 + mins * 60 + secs;
366 return DateTime(date, totalSecs);
367 }
368
369 std::string Timestamp::ToString() const
370 {
371 std::string s(dateTime.ToString());
372 s.append(1, '.').append(Format(std::to_string(nanosecs), 9, FormatWidth::exact, FormatJustify::right, '0'));
373 return s;
374 }
375
376 bool operator==(const Timestamp& left, const Timestamp& right)
377 {
378 return left.GetDateTime() == right.GetDateTime() && left.Nanoseconds() == right.Nanoseconds();
379 }
380
381 bool operator<(const Timestamp& left, const Timestamp& right)
382 {
383 if (left.GetDateTime() < right.GetDateTime()) return true;
384 if (left.GetDateTime() > right.GetDateTime()) return false;
385 return left.Nanoseconds() < right.Nanoseconds();
386 }
387
388 class TimestampProvider
389 {
390 public:
391 static void Init();
392 static void Done();
393 static TimestampProvider& Instance() { return *instance; }
394 Timestamp GetCurrentTimestamp();
395 private:
396 static std::unique_ptr<TimestampProvider> instance;
397 TimestampProvider();
398 DateTime startDateTime;
399 std::chrono::steady_clock::time_point startTimePoint;
400 void Reset();
401 };
402
403 std::unique_ptr<TimestampProvider> TimestampProvider::instance;
404
405 void TimestampProvider::Init()
406 {
407 instance.reset(new TimestampProvider());
408 }
409
410 void TimestampProvider::Done()
411 {
412 instance.reset();
413 }
414
415 void TimestampProvider::Reset()
416 {
417 startDateTime = GetCurrentDateTime();
418 startTimePoint = std::chrono::steady_clock::now();
419 }
420
421 TimestampProvider::TimestampProvider() : startDateTime(), startTimePoint()
422 {
423 Reset();
424 }
425
426 Timestamp TimestampProvider::GetCurrentTimestamp()
427 {
428 if (GetCurrentDate() != startDateTime.GetDate())
429 {
430 Reset();
431 }
432 std::chrono::nanoseconds elapsed = std::chrono::steady_clock::now() - startTimePoint;
433 int64_t elapsedNanosecs = elapsed.count();
434 int elapsedSecs = static_cast<int>(elapsedNanosecs / 1000000000ll);
435 int nanosecs = static_cast<int>(elapsedNanosecs % 1000000000ll);
436 Date date = startDateTime.GetDate();
437 int secs = startDateTime.Seconds() + elapsedSecs;
438 if (secs >= secsInDay)
439 {
440 date = date.AddDays(1);
441 secs = secs - secsInDay;
442 }
443 Timestamp timestamp(DateTime(date, secs), nanosecs);
444 return timestamp;
445 }
446
447 Timestamp GetCurrentTimestamp()
448 {
449 return TimestampProvider::Instance().GetCurrentTimestamp();
450 }
451
452 Timestamp ParseTimestamp(const std::string& timestampStr)
453 {
454 DateTime dateTime = ParseDateTime(timestampStr.substr(0, 19));
455 int32_t nanosecs = boost::lexical_cast<int>(timestampStr.substr(20));
456 return Timestamp(dateTime, nanosecs);
457 }
458
459 std::int64_t CurrentMs()
460 {
461 return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - std::chrono::steady_clock::time_point()).count();
462 }
463
464 int64_t GetCurrentTime()
465 {
466 std::time_t currentTime;
467 std::time(¤tTime);
468 return currentTime;
469 }
470
471 int64_t Hours(int64_t nanosecs)
472 {
473 return nanosecs / (3600ll * int64_t(1000000000ll));
474 }
475
476 int64_t Minutes(int64_t nanosecs)
477 {
478 return nanosecs / (60ll * int64_t(1000000000ll));
479 }
480
481 int64_t Seconds(int64_t nanosecs)
482 {
483 return nanosecs / int64_t(1000000000ll);
484 }
485
486 int64_t Milliseconds(int64_t nanosecs)
487 {
488 return nanosecs / int64_t(1000000ll);
489 }
490
491 int64_t Microseconds(int64_t nanosecs)
492 {
493 return nanosecs / int64_t(1000ll);
494 }
495
496 std::string DurationStr(const std::chrono::nanoseconds& duration)
497 {
498 std::string s;
499 int64_t hh = Hours(duration.count()) % 24;
500 s.append(1, static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(hh / 10 % 10)));
501 s.append(1, static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(hh % 10)));
502 s.append(1, ':');
503 int64_t mm = Minutes(duration.count()) % 60;
504 s.append(1, static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(mm / 10 % 10)));
505 s.append(1, static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(mm % 10)));
506 s.append(1, ':');
507 int64_t ss = Seconds(duration.count()) % 60;
508 s.append(1, static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(ss / 10 % 10)));
509 s.append(1, static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(ss % 10)));
510 s.append(1, '.');
511 int64_t ms = Milliseconds(duration.count()) % 1000;
512 s.append(1, static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(ms / 100 % 10)));
513 s.append(1, static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(ms / 10 % 10)));
514 s.append(1, static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(ms % 10)));
515 s.append(1, '.');
516 int64_t us = Microseconds(duration.count()) % 1000;
517 s.append(1, static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(us / 100 % 10)));
518 s.append(1, static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(us / 10 % 10)));
519 s.append(1, static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(us % 10)));
520 return s;
521 }
522
523 void TimeInit()
524 {
525 TimestampProvider::Init();
526 }
527
528 void TimeDone()
529 {
530 TimestampProvider::Done();
531 }
532
533 } }