1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  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 monthint year)
 16 {
 17     static int monthDays[] = { 0312831303130313130313031 };
 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(my);
 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(my);
 46         }
 47         return Date(ymd);
 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(my);
 66         }
 67         return Date(ymd);
 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(mnthy);
 89         if (d > md)
 90         {
 91             d = md;
 92         }
 93         return Date(ymnthd);
 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(mnthy);
107         if (d > md)
108         {
109             d = md;
110         }
111         return Date(ymnthd);
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(monthy);
124     if (d > md)
125     {
126         d = md;
127     }
128     return Date(ymonthd);
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(1static_cast<char>(static_cast<int16_t>('0') + ((year / 1000) % 10)));
140     date.append(1static_cast<char>(static_cast<int16_t>('0') + ((year / 100) % 10)));
141     date.append(1static_cast<char>(static_cast<int16_t>('0') + ((year / 10) % 10)));
142     date.append(1static_cast<char>(static_cast<int16_t>('0') + (year % 10)));
143     if (!omitDashes)
144     {
145         date.append(1'-');
146     }
147     date.append(1static_cast<char>(static_cast<int8_t>('0') + ((static_cast<int8_t>(month) / 10) % 10)));
148     date.append(1static_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(1static_cast<char>(static_cast<int8_t>('0') + ((day / 10) % 10)));
154     date.append(1static_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(&currentTime);
162     struct tm* localTime = nullptr;
163     localTime = std::localtime(&currentTime);
164     return Date(1900 + localTime->tm_yearstatic_cast<Month>(1 + localTime->tm_mon)static_cast<int8_t>(localTime->tm_mday));
165 }
166 
167 bool operator==(const Date& leftconst Date& right)
168 {
169     return left.Year() == right.Year() && left.GetMonth() == right.GetMonth() && left.Day() == right.Day();
170 }
171 
172 bool operator<(const Date& leftconst 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& dateStrint& dateEnd)
197 {
198     if (dateStr.length() < 4 + 2 + 2)
199     {
200         ThrowInvalidDate(dateStr);
201     }
202     int16_t year = boost::lexical_cast<short>(dateStr.substr(04));
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(monthStart2)));
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(dayStart2)));
227     if (day < 1 || day > 31)
228     {
229         ThrowInvalidDate(dateStr);
230     }
231     dateEnd = dayStart + 2;
232     return Date(yearstatic_cast<Month>(month)day);
233 }
234 
235 Date ParseDate(const std::string& dateStr)
236 {
237     int dateEnd = 0;
238     return ParseDate(dateStrdateEnd);
239 }
240 
241 std::string DateTime::ToString() const
242 {
243     return ToString(falsefalsefalsefalse);
244 }
245 
246 std::string DateTime::ToString(bool omitDashesbool omitColonsbool omitMinsbool 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(1static_cast<char>(static_cast<int32_t>('0') + ((hh / 10) % 10)));
255     dateTime.append(1static_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(1static_cast<char>(static_cast<int32_t>('0') + ((mm / 10) % 10)));
263         dateTime.append(1static_cast<char>(static_cast<int>('0') + (mm % 10)));
264         if (!omitSecs)
265         {
266             if (!omitColons)
267             {
268                 dateTime.append(1':');
269             }
270             dateTime.append(1static_cast<char>(static_cast<int32_t>('0') + ((ss / 10) % 10)));
271             dateTime.append(1static_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(1static_cast<char>(static_cast<int32_t>('0') + ((hh / 10) % 10)));
285     time.append(1static_cast<char>(static_cast<int32_t>('0') + (hh % 10)));
286     time.append(1':');
287     time.append(1static_cast<char>(static_cast<int32_t>('0') + ((mm / 10) % 10)));
288     time.append(1static_cast<char>(static_cast<int>('0') + (mm % 10)));
289     time.append(1':');
290     time.append(1static_cast<char>(static_cast<int32_t>('0') + ((ss / 10) % 10)));
291     time.append(1static_cast<char>(static_cast<int32_t>('0') + (ss % 10)));
292     time.append(1'.');
293     time.append(1static_cast<char>(static_cast<int32_t>('0') + ((ms / 100) % 10)));
294     time.append(1static_cast<char>(static_cast<int32_t>('0') + ((ms / 10) % 10)));
295     time.append(1static_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(&currentTime);
303     struct tm* localTime = nullptr;
304     localTime = std::localtime(&currentTime);
305     return DateTime(Date(1900 + localTime->tm_yearstatic_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& leftconst DateTime& right)
309 {
310     return left.GetDate() == right.GetDate() && left.Seconds() == right.Seconds();
311 }
312 
313 bool operator<(const DateTime& leftconst 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(dateTimeStrdateEnd);
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(hoursStart2));
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(minsStart2));
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(secsStart2));
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(datetotalSecs);
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)9FormatWidth::exactFormatJustify::right'0'));
373     return s;
374 }
375 
376 bool operator==(const Timestamp& leftconst Timestamp& right)
377 {
378     return left.GetDateTime() == right.GetDateTime() && left.Nanoseconds() == right.Nanoseconds();
379 }
380 
381 bool operator<(const Timestamp& leftconst 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(datesecs)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(019));
455     int32_t nanosecs = boost::lexical_cast<int>(timestampStr.substr(20));
456     return Timestamp(dateTimenanosecs);
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(&currentTime);
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(1static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(hh / 10 % 10)));
501     s.append(1static_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(1static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(mm / 10 % 10)));
505     s.append(1static_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(1static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(ss / 10 % 10)));
509     s.append(1static_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(1static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(ms / 100 % 10)));
513     s.append(1static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(ms / 10 % 10)));
514     s.append(1static_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(1static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(us / 100 % 10)));
518     s.append(1static_cast<char>(static_cast<uint8_t>('0') + static_cast<uint8_t>(us / 10 % 10)));
519     s.append(1static_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 } } // namespace soulng::util