Xclox
C++11 header-only cross-platform date and time library with an asynchronous NTP client
time.hpp
1 /*
2  * Copyright (c) 2024 Abdullatif Kalla.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE.txt file in the root directory of this source tree.
6  */
7 
8 #ifndef XCLOX_TIME_HPP
9 #define XCLOX_TIME_HPP
10 
11 #include "internal.hpp"
12 
13 namespace xclox {
14 
46 class Time {
47  using Duration = std::chrono::nanoseconds;
48  using Days = internal::Days;
49 
50 public:
56  using Nanoseconds = std::chrono::nanoseconds;
57  using Microseconds = std::chrono::microseconds;
58  using Milliseconds = std::chrono::milliseconds;
59  using Seconds = std::chrono::seconds;
60  using Minutes = std::chrono::minutes;
61  using Hours = std::chrono::hours;
62 
64 
71  Time()
72  : m_duration(Hours(24))
73  {
74  }
75 
77  Time(const Time& other) = default;
78 
80  Time(Time&& other) = default;
81 
83  explicit Time(std::time_t scalarStdTime)
84  : m_duration(Seconds(scalarStdTime))
85  {
86  }
87 
89  explicit Time(const std::tm& brokenStdTime)
90  : m_duration(Hours(brokenStdTime.tm_hour) + Minutes(brokenStdTime.tm_min) + Seconds(brokenStdTime.tm_sec))
91  {
92  }
93 
103  explicit Time(const Duration& duration)
104  : m_duration(duration)
105  {
106  }
107 
109  explicit Time(const std::chrono::system_clock::time_point& timePoint)
110  : m_duration(timePoint.time_since_epoch() % Days(1))
111  {
112  }
113 
115  explicit Time(int hours, int minutes, int seconds)
116  : m_duration(Hours(hours) + Minutes(minutes) + Seconds(seconds))
117  {
118  }
119 
121  explicit Time(int hours, int minutes, int seconds, int milliseconds)
122  : m_duration(Hours(hours) + Minutes(minutes) + Seconds(seconds) + Milliseconds(milliseconds))
123  {
124  }
125 
130  explicit Time(int hours, int minutes, int seconds, const Duration& subseconds)
131  : m_duration(Hours(hours) + Minutes(minutes) + Seconds(seconds) + subseconds)
132  {
133  }
134 
144  explicit Time(Hours hours, Minutes minutes, Seconds seconds, const Duration& subseconds)
145  : m_duration(hours + minutes + seconds + subseconds)
146  {
147  }
148 
150  ~Time() = default;
151 
153 
160  Time& operator=(const Time& other) = default;
161 
163  Time& operator=(Time&& other) = default;
164 
166 
175  bool operator<(const Time& other) const
176  {
177  return this->m_duration < other.m_duration;
178  }
179 
181  bool operator<=(const Time& other) const
182  {
183  return this->m_duration <= other.m_duration;
184  }
185 
187  bool operator>(const Time& other) const
188  {
189  return this->m_duration > other.m_duration;
190  }
191 
193  bool operator>=(const Time& other) const
194  {
195  return this->m_duration >= other.m_duration;
196  }
197 
199  bool operator==(const Time& other) const
200  {
201  return this->m_duration == other.m_duration;
202  }
203 
205  bool operator!=(const Time& other) const
206  {
207  return this->m_duration != other.m_duration;
208  }
209 
211 
218  Time operator+(const Duration& duration) const
219  {
220  return Time(this->m_duration + duration);
221  }
222 
224  Time operator-(const Duration& duration) const
225  {
226  return Time(this->m_duration - duration);
227  }
228 
230  Nanoseconds operator-(const Time& other) const
231  {
232  return Nanoseconds(this->m_duration - other.m_duration);
233  }
234 
236 
257  bool isValid() const
258  {
259  return m_duration.count() >= 0 && m_duration < Days(1);
260  }
261 
263  long nanosecond() const
264  {
265  return static_cast<long>(std::chrono::duration_cast<Nanoseconds>(m_duration % Seconds(1)).count());
266  }
267 
269  long microsecond() const
270  {
271  return static_cast<long>(std::chrono::duration_cast<Microseconds>(m_duration % Seconds(1)).count());
272  }
273 
275  int millisecond() const
276  {
277  return static_cast<int>(std::chrono::duration_cast<Milliseconds>(m_duration % Seconds(1)).count());
278  }
279 
281  int second() const
282  {
283  return static_cast<int>(std::chrono::duration_cast<Seconds>(m_duration % Minutes(1)).count());
284  }
285 
287  int minute() const
288  {
289  return std::chrono::duration_cast<Minutes>(m_duration % Hours(1)).count();
290  }
291 
293  int hour() const
294  {
295  return std::chrono::duration_cast<Hours>(m_duration % Days(1)).count();
296  }
297 
299 
306  Time addNanoseconds(int nanoseconds) const
307  {
308  return Time(m_duration + Nanoseconds(nanoseconds));
309  }
310 
312  Time subtractNanoseconds(int nanoseconds) const
313  {
314  return Time(m_duration - Nanoseconds(nanoseconds));
315  }
316 
318  Time addMicroseconds(int microseconds) const
319  {
320  return Time(m_duration + Microseconds(microseconds));
321  }
322 
324  Time subtractMicroseconds(int microseconds) const
325  {
326  return Time(m_duration - Microseconds(microseconds));
327  }
328 
330  Time addMilliseconds(int milliseconds) const
331  {
332  return Time(m_duration + Milliseconds(milliseconds));
333  }
334 
336  Time subtractMilliseconds(int milliseconds) const
337  {
338  return Time(m_duration - Milliseconds(milliseconds));
339  }
340 
342  Time addSeconds(int seconds) const
343  {
344  return Time(m_duration + Seconds(seconds));
345  }
346 
348  Time subtractSeconds(int seconds) const
349  {
350  return Time(m_duration - Seconds(seconds));
351  }
352 
354  Time addMinutes(int minutes) const
355  {
356  return Time(m_duration + Minutes(minutes));
357  }
358 
360  Time subtractMinutes(int minutes) const
361  {
362  return Time(m_duration - Minutes(minutes));
363  }
364 
366  Time addHours(int hours) const
367  {
368  return Time(m_duration + Hours(hours));
369  }
370 
372  Time subtractHours(int hours) const
373  {
374  return Time(m_duration - Hours(hours));
375  }
376 
378  Time addDuration(const Duration& duration) const
379  {
380  return Time(m_duration + duration);
381  }
382 
384  Time subtractDuration(const Duration& duration) const
385  {
386  return Time(m_duration - duration);
387  }
388 
390 
397  long long toNanosecondsSinceMidnight() const
398  {
399  return std::chrono::duration_cast<Nanoseconds>(m_duration).count();
400  }
401 
403  long long toMicrosecondsSinceMidnight() const
404  {
405  return std::chrono::duration_cast<Microseconds>(m_duration).count();
406  }
407 
410  {
411  return static_cast<long>(std::chrono::duration_cast<Milliseconds>(m_duration).count());
412  }
413 
416  {
417  return static_cast<long>(std::chrono::duration_cast<Seconds>(m_duration).count());
418  }
419 
422  {
423  return std::chrono::duration_cast<Minutes>(m_duration).count();
424  }
425 
428  {
429  return std::chrono::duration_cast<Hours>(m_duration).count();
430  }
431 
434  {
435  return m_duration;
436  }
437 
439  std::tm toBrokenStdTime() const
440  {
441  std::tm cTime = { 0 };
442  cTime.tm_hour = hour();
443  cTime.tm_min = minute();
444  cTime.tm_sec = second();
445  return cTime;
446  }
447 
449  std::time_t toScalarStdTime() const
450  {
451  return std::time_t(toSecondsSinceMidnight());
452  }
453 
483  std::string toString(const std::string& format) const
484  {
485  if (!isValid())
486  return std::string();
487 
488  std::stringstream output;
489 
490  for (size_t pos = 0; pos < format.size(); ++pos) {
491  const int patternLength = internal::countIdenticalCharsFrom(pos, format);
492 
493  if (format[pos] == 'h') {
494  output << std::setfill('0') << std::setw(patternLength) << hour();
495  pos += patternLength - 1; // skip all identical characters except the last.
496  } else if (format[pos] == 'H') {
497  int hours12f = ((hour() == 0 || hour() == 12) ? 12 : hour() % 12);
498  output << std::setfill('0') << std::setw(patternLength) << hours12f;
499  pos += patternLength - 1;
500  } else if (format[pos] == 'm') {
501  output << std::setfill('0') << std::setw(patternLength) << minute();
502  pos += patternLength - 1;
503  } else if (format[pos] == 's') {
504  output << std::setfill('0') << std::setw(patternLength) << second();
505  pos += patternLength - 1;
506  } else if (format[pos] == 'f') {
507  std::string subseconds = std::to_string(std::chrono::duration_cast<Nanoseconds>(m_duration % Seconds(1)).count());
508  std::string padddedSubsecondsString = subseconds.insert(0, 9 - subseconds.size(), '0');
509  output << padddedSubsecondsString.substr(0, static_cast<size_t>(patternLength));
510  pos += patternLength - 1;
511  } else if (format[pos] == 'A') {
512  output << (hour() >= 12 ? "PM" : "AM");
513  } else if (format[pos] == 'a') {
514  output << (hour() >= 12 ? "pm" : "am");
515  } else {
516  output << format[pos];
517  }
518  }
519 
520  return output.str();
521  }
522 
524 
529  static Time current()
530  {
531  return Time(std::chrono::duration_cast<Nanoseconds>(std::chrono::system_clock::now().time_since_epoch() % Days(1)));
532  }
533 
535  static Time midnight()
536  {
537  return Time(Nanoseconds::zero());
538  }
539 
544  static Time fromString(const std::string& time, const std::string& format)
545  {
546  int _hour = 0, _minute = 0, _second = 0;
547  long _subsecond = 0;
548 
549  for (size_t fmtPos = 0, timPos = 0; fmtPos < format.size() && timPos < time.size(); ++fmtPos) {
550  const int patternLength = internal::countIdenticalCharsFrom(fmtPos, format);
551 
552  if (format[fmtPos] == 'h' || format[fmtPos] == 'H') {
553  _hour = internal::readIntAndAdvancePos(time, timPos, 2);
554  fmtPos += patternLength - 1; // skip all identical characters except the last.
555  } else if (format[fmtPos] == 'm') {
556  _minute = internal::readIntAndAdvancePos(time, timPos, 2);
557  fmtPos += patternLength - 1;
558  } else if (format[fmtPos] == 's') {
559  _second = internal::readIntAndAdvancePos(time, timPos, 2);
560  fmtPos += patternLength - 1;
561  } else if (format[fmtPos] == 'f') {
562  std::string subsecondString = time.substr(timPos, static_cast<size_t>(patternLength));
563  _subsecond = std::stoi(subsecondString.append(9 - subsecondString.size(), '0'));
564  timPos += patternLength;
565  fmtPos += patternLength - 1;
566  } else if (format[fmtPos] == 'a' || format[fmtPos] == 'A') {
567  if (time.substr(timPos, 2) == "pm" || time.substr(timPos, 2) == "PM") {
568  _hour = (_hour > 12 ? _hour : _hour + 12);
569  timPos += 2;
570  }
571  } else {
572  ++timPos;
573  }
574  }
575 
576  return Time(Hours(_hour) + Minutes(_minute) + Seconds(_second) + Nanoseconds(_subsecond));
577  }
578 
587  static long long nanosecondsBetween(const Time& from, const Time& to)
588  {
590  }
591 
593  static long long microsecondsBetween(const Time& from, const Time& to)
594  {
596  }
597 
599  static long millisecondsBetween(const Time& from, const Time& to)
600  {
602  }
603 
605  static long secondsBetween(const Time& from, const Time& to)
606  {
607  return to.toSecondsSinceMidnight() - from.toSecondsSinceMidnight();
608  }
609 
611  static int minutesBetween(const Time& from, const Time& to)
612  {
613  return to.toMinutesSinceMidnight() - from.toMinutesSinceMidnight();
614  }
615 
617  static int hoursBetween(const Time& from, const Time& to)
618  {
619  return to.toHoursSinceMidnight() - from.toHoursSinceMidnight();
620  }
621 
623 
624 private:
625  Duration m_duration;
626 };
627 
635 std::ostream& operator<<(std::ostream& os, const Time& t)
636 {
637  os << t.toString("hh:mm:ss.fff");
638  return os;
639 }
640 
642 std::istream& operator>>(std::istream& is, Time& t)
643 {
644  const int TimeFormatWidth = 12;
645  char result[TimeFormatWidth];
646  is.read(result, TimeFormatWidth);
647 
648  t = Time::fromString(std::string(result, TimeFormatWidth), "hh:mm:ss.fff");
649  return is;
650 }
651 
653 
654 } // namespace xclox
655 
656 #endif // XCLOX_TIME_HPP
Time is an immutable time class representing a time without a time zone in the ISO-8601 calendar syst...
Definition: time.hpp:46
long long toMicrosecondsSinceMidnight() const
Returns the elapsed microseconds since midnight.
Definition: time.hpp:403
long toSecondsSinceMidnight() const
Returns the elapsed seconds since midnight.
Definition: time.hpp:415
static long millisecondsBetween(const Time &from, const Time &to)
Returns the number of milliseconds between from and to.
Definition: time.hpp:599
Time(int hours, int minutes, int seconds, const Duration &subseconds)
Constructs a Time object from the given hours, minutes, seconds, and subseconds.
Definition: time.hpp:130
Time addMilliseconds(int milliseconds) const
Returns the result of adding milliseconds to this time as a new Time object.
Definition: time.hpp:330
Time subtractSeconds(int seconds) const
Returns the result of subtracting seconds from this time as a new Time object.
Definition: time.hpp:348
Time(Hours hours, Minutes minutes, Seconds seconds, const Duration &subseconds)
Constructs a Time object from the given hours, minutes, seconds, and subseconds.
Definition: time.hpp:144
Time operator+(const Duration &duration) const
Returns the result of adding duration to this time as a new Time object.
Definition: time.hpp:218
Time addSeconds(int seconds) const
Returns the result of adding seconds to this time as a new Time object.
Definition: time.hpp:342
bool operator>(const Time &other) const
Returns whether this time is later than other.
Definition: time.hpp:187
Time addMicroseconds(int microseconds) const
Returns the result of adding microseconds to this time as a new Time object.
Definition: time.hpp:318
std::string toString(const std::string &format) const
Returns the time as a string formatted according to the format string format.
Definition: time.hpp:483
Time subtractMinutes(int minutes) const
Returns the result of subtracting minutes from this time as a new Time object.
Definition: time.hpp:360
Time(const Duration &duration)
Constructs a Time object from time duration elapsed since midnight ("00:00:00").
Definition: time.hpp:103
static int minutesBetween(const Time &from, const Time &to)
Returns the number of minutes between from and to.
Definition: time.hpp:611
Time(int hours, int minutes, int seconds)
Constructs a Time object from the given hours, minutes and seconds.
Definition: time.hpp:115
Time()
Constructs an invalid Time object with every field is set to zero.
Definition: time.hpp:71
long nanosecond() const
Returns the nanosecond of second (0, 999999999).
Definition: time.hpp:263
bool operator!=(const Time &other) const
Returns whether this time is different from other.
Definition: time.hpp:205
Time(Time &&other)=default
Move-constructs a Time object from other.
bool operator>=(const Time &other) const
Returns whether this time is later than other or equal to it.
Definition: time.hpp:193
Time & operator=(const Time &other)=default
Copy assignment operator.
std::tm toBrokenStdTime() const
Returns a std::tm representation of this time.
Definition: time.hpp:439
int minute() const
Returns the minute of hour (0, 59).
Definition: time.hpp:287
int millisecond() const
Returns the millisecond of second (0, 999).
Definition: time.hpp:275
Time subtractDuration(const Duration &duration) const
Returns the result of subtracting duration from this time as a new Time object.
Definition: time.hpp:384
static Time current()
Returns a Time object set to the current time obtained from the system clock.
Definition: time.hpp:529
int hour() const
Returns the hour of day (0, 23).
Definition: time.hpp:293
Nanoseconds operator-(const Time &other) const
Returns the result of subtracting other from this time as a Time::Nanoseconds duration.
Definition: time.hpp:230
Time(const Time &other)=default
Copy-constructs a Time object from other.
std::chrono::milliseconds Milliseconds
Millisecond duration.
Definition: time.hpp:58
Time subtractNanoseconds(int nanoseconds) const
Returns the result of subtracting nanoseconds from this time as a new Time object.
Definition: time.hpp:312
std::chrono::minutes Minutes
Minute duration.
Definition: time.hpp:60
static Time fromString(const std::string &time, const std::string &format)
Returns a Time object from the string time according to the format string format.
Definition: time.hpp:544
Time addHours(int hours) const
Returns the result of adding hours to this time as a new Time object.
Definition: time.hpp:366
long microsecond() const
Returns the microsecond of second (0, 999999).
Definition: time.hpp:269
bool operator<(const Time &other) const
Returns whether this time is earlier than other.
Definition: time.hpp:175
static long long microsecondsBetween(const Time &from, const Time &to)
Returns the number of microseconds between from and to.
Definition: time.hpp:593
Time(int hours, int minutes, int seconds, int milliseconds)
Constructs a Time object from the given hours, minutes, seconds and milliseconds.
Definition: time.hpp:121
static long long nanosecondsBetween(const Time &from, const Time &to)
Returns the number of nanoseconds between from and to.
Definition: time.hpp:587
std::time_t toScalarStdTime() const
Returns a std::time_t representation of this time.
Definition: time.hpp:449
std::chrono::microseconds Microseconds
Microsecond duration.
Definition: time.hpp:57
Time(const std::tm &brokenStdTime)
Constructs a Time object from the standard library std::tm object brokenStdTime.
Definition: time.hpp:89
Time(const std::chrono::system_clock::time_point &timePoint)
Constructs a Time object from the given system time point timePoint.
Definition: time.hpp:109
Time & operator=(Time &&other)=default
Move assignment operator.
Time subtractMilliseconds(int milliseconds) const
Returns the result of subtracting milliseconds from this time as a new Time object.
Definition: time.hpp:336
std::istream & operator>>(std::istream &is, Time &t)
Reads a time in ISO-8601 time format "hh:mm:ss.fff" from stream is and stores it in time t....
Definition: time.hpp:642
std::ostream & operator<<(std::ostream &os, const Time &t)
Writes time t to stream os in ISO-8601 time format "hh:mm:ss.fff". See toString() for information abo...
Definition: time.hpp:635
Time addNanoseconds(int nanoseconds) const
Returns the result of adding nanoseconds to this time as a new Time object.
Definition: time.hpp:306
Time(std::time_t scalarStdTime)
Constructs a Time object from the standard library std::time_t object scalarStdTime.
Definition: time.hpp:83
static int hoursBetween(const Time &from, const Time &to)
Returns the number of hours between from and to.
Definition: time.hpp:617
Time operator-(const Duration &duration) const
Returns the result of subtracting duration from this time as a new Time object.
Definition: time.hpp:224
static long secondsBetween(const Time &from, const Time &to)
Returns the number of seconds between from and to.
Definition: time.hpp:605
Time addDuration(const Duration &duration) const
Returns the result of adding duration to this time as a new Time object.
Definition: time.hpp:378
Time addMinutes(int minutes) const
Returns the result of adding minutes to this time as a new Time object.
Definition: time.hpp:354
~Time()=default
Default destructor.
bool operator==(const Time &other) const
Returns whether this time is equal to other.
Definition: time.hpp:199
Time subtractMicroseconds(int microseconds) const
Returns the result of subtracting microseconds from this time as a new Time object.
Definition: time.hpp:324
bool isValid() const
Returns whether this time object represents a valid time.
Definition: time.hpp:257
long long toNanosecondsSinceMidnight() const
Returns the elapsed nanoseconds since midnight.
Definition: time.hpp:397
Time subtractHours(int hours) const
Returns the result of subtracting hours from this time as a new Time object.
Definition: time.hpp:372
std::chrono::nanoseconds Nanoseconds
Nanosecond duration.
Definition: time.hpp:56
Nanoseconds toStdDurationSinceMidnight() const
Returns the elapsed time since midnight as a Nanoseconds duration.
Definition: time.hpp:433
bool operator<=(const Time &other) const
Returns whether this time is earlier than other or equal to it.
Definition: time.hpp:181
std::chrono::seconds Seconds
Second duration.
Definition: time.hpp:59
int toHoursSinceMidnight() const
Returns the elapsed hours since midnight. If this time is invalid, the returned value may exceed 23.
Definition: time.hpp:427
long toMillisecondsSinceMidnight() const
Returns the elapsed milliseconds since midnight.
Definition: time.hpp:409
std::chrono::hours Hours
Hour duration.
Definition: time.hpp:61
static Time midnight()
Returns a Time object set to midnight (i.e., "00:00:00").
Definition: time.hpp:535
int second() const
Returns the second of minute (0, 59).
Definition: time.hpp:281
int toMinutesSinceMidnight() const
Returns the elapsed minutes since midnight.
Definition: time.hpp:421