NumCpp  2.11.0
A Templatized Header Only C++ Implementation of the Python NumPy Library
DateTime/DateTime.hpp
Go to the documentation of this file.
1 #pragma once
29 
30 #ifndef NUMCPP_NO_USE_BOOST
31 
32 #include <chrono>
33 #include <ctime>
34 #include <iostream>
35 #include <regex>
36 #include <sstream>
37 #include <stdexcept>
38 #include <string>
39 #include <string_view>
40 
41 #include "boost/date_time/posix_time/posix_time.hpp"
42 
44 
45 namespace nc
46 {
47  //================================================================================
48  // Class Description:
50  class DateTime
51  {
52  public:
53  static constexpr int MAX_MONTH = 12;
54  static constexpr int MAX_DAY = 31;
55  static constexpr int MAX_HOUR = 23;
56  static constexpr int MAX_MINUTE = 59;
57  static constexpr int MAX_SECOND = 59;
58 
59  //============================================================================
60  // Method Description:
63  DateTime() = default;
64 
65  //============================================================================
66  // Method Description:
71  explicit DateTime(const TimePoint& tp)
72  {
73  auto tpSubSeconds = std::chrono::duration_cast<Duration>(tp.time_since_epoch());
74  auto fractionalSecond = static_cast<double>(tpSubSeconds.count() % Duration::period::den) /
75  static_cast<double>(Duration::period::den);
76  auto time = Clock::to_time_t(std::chrono::time_point_cast<Clock::duration>(tp));
77  std::tm tm{};
78 #ifdef _MSC_VER
79  gmtime_s(&tm, &time);
80 #else
81  gmtime_r(&time, &tm);
82 #endif
83 
84  setYear(tm.tm_year + TM_EPOCH_YEAR);
85  setMonth(tm.tm_mon + 1);
86  setDay(tm.tm_mday);
87  setHour(tm.tm_hour);
88  setMinute(tm.tm_min);
89  setSecond(tm.tm_sec);
91  }
92 
93  //============================================================================
94  // Method Description:
99  explicit DateTime(const std::string& timestamp) :
100  DateTime(strToTimepoint(timestamp))
101  {
102  }
103 
104  //============================================================================
105  // Method Description:
116  DateTime(int year, int month, int day, int hour, int minute, int second, double fractionalSecond = 0.0) noexcept
117  :
118  year_(year),
119  month_(month),
120  day_(day),
121  hour_(hour),
122  minute_(minute),
123  second_(second),
124  fractionalSecond_(fractionalSecond)
125  {
126  }
127 
128  //============================================================================
129  // Method Description:
134  [[nodiscard]] int year() const noexcept
135  {
136  return year_;
137  }
138 
139  //============================================================================
140  // Method Description:
145  void setYear(int year)
146  {
147  if (year < 0)
148  {
149  throw std::invalid_argument("input year must be greater than zero");
150  }
151  year_ = year;
152  }
153 
154  //============================================================================
155  // Method Description:
160  [[nodiscard]] int month() const noexcept
161  {
162  return month_;
163  }
164 
165  //============================================================================
166  // Method Description:
171  void setMonth(int month)
172  {
173  if (month < 1)
174  {
175  throw std::invalid_argument("input month must be greater than one");
176  }
177  if (month > MAX_MONTH)
178  {
179  throw std::invalid_argument("input month must be less than DateTime::MAX_MONTH");
180  }
181  month_ = month;
182  }
183 
184  //============================================================================
185  // Method Description:
190  [[nodiscard]] int day() const noexcept
191  {
192  return day_;
193  }
194 
195  //============================================================================
196  // Method Description:
201  void setDay(int day)
202  {
203  if (day < 1)
204  {
205  throw std::invalid_argument("input day must be greater than one");
206  }
207  if (day > MAX_DAY)
208  {
209  throw std::invalid_argument("input day must be less than DateTime::MAX_DAY");
210  }
211  day_ = day;
212  }
213 
214  //============================================================================
215  // Method Description:
220  [[nodiscard]] int hour() const noexcept
221  {
222  return hour_;
223  }
224 
225  //============================================================================
226  // Method Description:
231  void setHour(int hour)
232  {
233  if (hour < 0)
234  {
235  throw std::invalid_argument("input hour must be greater than zero");
236  }
237  if (hour > MAX_HOUR)
238  {
239  throw std::invalid_argument("input hour must be less than DateTime::MAX_HOUR");
240  }
241  hour_ = hour;
242  }
243 
244  //============================================================================
245  // Method Description:
250  [[nodiscard]] int minute() const noexcept
251  {
252  return minute_;
253  }
254 
255  //============================================================================
256  // Method Description:
261  void setMinute(int minute)
262  {
263  if (minute < 0)
264  {
265  throw std::invalid_argument("input minute must be greater than zero");
266  }
267  if (minute > MAX_MINUTE)
268  {
269  throw std::invalid_argument("input minute must be less than DateTime::MAX_MINUTE");
270  }
271  minute_ = minute;
272  }
273 
274  //============================================================================
275  // Method Description:
280  [[nodiscard]] int second() const noexcept
281  {
282  return second_;
283  }
284 
285  //============================================================================
286  // Method Description:
291  void setSecond(int second)
292  {
293  if (second < 0)
294  {
295  throw std::invalid_argument("input second must be greater than zero");
296  }
297  if (second > MAX_SECOND)
298  {
299  throw std::invalid_argument("input second must be less than DateTime::MAX_SECOND");
300  }
301  second_ = second;
302  }
303 
304  //============================================================================
305  // Method Description:
310  [[nodiscard]] double fractionalSecond() const noexcept
311  {
312  return fractionalSecond_;
313  }
314 
315  //============================================================================
316  // Method Description:
322  {
323  if (fractionalSecond < 0. || fractionalSecond >= 1.)
324  {
325  throw std::invalid_argument("input fractionalSecond must be in the range [0, 1)");
326  }
327  fractionalSecond_ = fractionalSecond;
328  }
329 
330  //============================================================================
331  // Method Description:
336  [[nodiscard]] TimePoint toTimePoint() const
337  {
338  std::tm t{};
339  t.tm_year = year_ - TM_EPOCH_YEAR;
340  t.tm_mon = month_ - 1; // tm is 0 based months
341  t.tm_mday = day_;
342  t.tm_hour = hour_;
343  t.tm_min = minute_;
344  t.tm_sec = second_;
345  auto timePoint = Clock::from_time_t(
346 #ifdef _MSC_VER
347  _mkgmtime
348 #else
349  timegm
350 #endif
351  (&t));
352  return std::chrono::time_point_cast<TimePoint::duration>(timePoint) +
353  std::chrono::nanoseconds(static_cast<int64_t>(fractionalSecond_ * SECONDS_TO_NANOSECONDS));
354  }
355 
356  //============================================================================
357  // Method Description:
362  [[nodiscard]] std::string toStr() const
363  {
364  const auto timePoint = toTimePoint();
365  const auto timeSinceEpoch = timePoint.time_since_epoch().count();
366  time_t secondsFromEpoch = timeSinceEpoch / Duration::period::den;
367  const auto fractionalSeconds = static_cast<double>(timeSinceEpoch % Duration::period::den) /
368  static_cast<double>(Duration::period::den);
369 
370  std::tm tm{};
371 #ifdef _MSC_VER
372  gmtime_s(&tm, &secondsFromEpoch);
373 #else
374  gmtime_r(&secondsFromEpoch, &tm);
375 #endif
376 
377  std::stringstream ss;
378  if (fractionalSeconds > 0)
379  {
380  const auto format = "%Y-%m-%dT%H:%M:%S.%msZ";
381  std::stringstream ssFractionalSecond;
382  ssFractionalSecond.precision(NANO_SECOND_PRECESION);
383  ssFractionalSecond << std::fixed << fractionalSeconds;
384  auto fractionalSecondStr = ssFractionalSecond.str();
385  // strip of the preceding "0." and any trailing zeros
386  fractionalSecondStr = fractionalSecondStr.substr(2, fractionalSecondStr.size());
387  fractionalSecondStr = fractionalSecondStr.substr(0, fractionalSecondStr.find_last_not_of('0') + 1);
388  const auto fractionalSecondsFormat = std::regex_replace(format, std::regex("%ms"), fractionalSecondStr);
389  ss << std::put_time(&tm, fractionalSecondsFormat.c_str());
390  }
391  else
392  {
393  const auto format = "%Y-%m-%dT%H:%M:%SZ";
394  ss << std::put_time(&tm, format);
395  }
396 
397  return ss.str();
398  }
399 
400  //============================================================================
401  // Method Description:
407  [[nodiscard]] static DateTime now() noexcept
408  {
409  return DateTime(Clock::now());
410  }
411 
412  //============================================================================
413  // Method Description:
418  static TimePoint strToTimepoint(const std::string& timestamp)
419  {
420  const std::regex regexIsoTime{ R"(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(.\d+)?Z)" };
421  if (!std::regex_match(timestamp, regexIsoTime))
422  {
423  throw std::invalid_argument("Invalid iso timestamp format");
424  }
425 
426  auto convertedTime = boost::posix_time::ptime{};
427  try
428  {
429  convertedTime = boost::posix_time::from_iso_extended_string(timestamp.substr(0, timestamp.size() - 1));
430  }
431  catch (...)
432  {
433  throw std::invalid_argument("Invalid iso timestamp format");
434  }
435 
436  const auto fromEpoch = convertedTime - POSIX_EPOCH;
437  return TimePoint{ Duration{ fromEpoch.total_nanoseconds() } };
438  }
439 
440  private:
441  static constexpr int TM_EPOCH_YEAR = 1900;
442  static constexpr int POSIX_EPOCH_YEAR = 1970;
443  static inline const std::string POSIX_EPOCH_STR{ "1970-01-01T00:00:00" };
444  static inline const boost::posix_time::ptime POSIX_EPOCH{ boost::posix_time::from_iso_extended_string(
445  POSIX_EPOCH_STR) };
446  static constexpr double SECONDS_TO_NANOSECONDS = 1e9;
447  static constexpr int NANO_SECOND_PRECESION = 9;
448 
450  int year_{ POSIX_EPOCH_YEAR };
452  int month_{ 1 };
454  int day_{ 1 };
456  int hour_{ 0 };
458  int minute_{ 0 };
460  int second_{ 0 };
462  double fractionalSecond_{ 0.0 };
463  };
464 
465  //============================================================================
466  // Method Description:
473  [[nodiscard]] inline bool operator==(const DateTime& lhs, const DateTime& rhs) noexcept
474  {
475  return lhs.toTimePoint() == rhs.toTimePoint();
476  }
477 
478  //============================================================================
479  // Method Description:
486  [[nodiscard]] inline bool operator!=(const DateTime& lhs, const DateTime& rhs) noexcept
487  {
488  return !(lhs == rhs);
489  }
490 
491  //============================================================================
492  // Method Description:
499  [[nodiscard]] inline bool operator<(const DateTime& lhs, const DateTime& rhs) noexcept
500  {
501  return lhs.toTimePoint() < rhs.toTimePoint();
502  }
503 
504  //============================================================================
505  // Method Description:
512  [[nodiscard]] inline bool operator<=(const DateTime& lhs, const DateTime& rhs) noexcept
513  {
514  return lhs.toTimePoint() <= rhs.toTimePoint();
515  }
516 
517  //============================================================================
518  // Method Description:
525  [[nodiscard]] inline bool operator>(const DateTime& lhs, const DateTime& rhs) noexcept
526  {
527  return lhs.toTimePoint() > rhs.toTimePoint();
528  }
529 
530  //============================================================================
531  // Method Description:
538  [[nodiscard]] inline bool operator>=(const DateTime& lhs, const DateTime& rhs) noexcept
539  {
540  return lhs.toTimePoint() >= rhs.toTimePoint();
541  }
542 
543  //============================================================================
544  // Method Description:
551  [[nodiscard]] inline Duration operator-(const DateTime& lhs, const DateTime& rhs) noexcept
552  {
553  return lhs.toTimePoint() - rhs.toTimePoint();
554  }
555 
556  //============================================================================
557  // Method Description:
564  inline std::ostream& operator<<(std::ostream& os, const DateTime& datetime) noexcept
565  {
566  os << "DateTime:\n";
567  os << "\tyear: " << datetime.year() << '\n';
568  os << "\tmonth: " << datetime.month() << '\n';
569  os << "\tday: " << datetime.day() << '\n';
570  os << "\thour: " << datetime.hour() << '\n';
571  os << "\tminute: " << datetime.minute() << '\n';
572  os << "\tsecond: " << datetime.second() << '\n';
573  os << "\tfractionalSecond: " << datetime.fractionalSecond() << '\n';
574  return os;
575  }
576 } // namespace nc
577 
578 #endif
Date Time class for working with iso formatted date times.
Definition: DateTime/DateTime.hpp:51
void setFractionalSecond(double fractionalSecond)
fractionalSecond setter
Definition: DateTime/DateTime.hpp:321
void setHour(int hour)
hour setter
Definition: DateTime/DateTime.hpp:231
static constexpr int MAX_HOUR
Definition: DateTime/DateTime.hpp:55
int hour() const noexcept
hour getter
Definition: DateTime/DateTime.hpp:220
int second() const noexcept
second getter
Definition: DateTime/DateTime.hpp:280
DateTime()=default
TimePoint toTimePoint() const
Converts the struct to a TimePoint.
Definition: DateTime/DateTime.hpp:336
static constexpr int MAX_MINUTE
Definition: DateTime/DateTime.hpp:56
DateTime(int year, int month, int day, int hour, int minute, int second, double fractionalSecond=0.0) noexcept
Definition: DateTime/DateTime.hpp:116
int year() const noexcept
year getter
Definition: DateTime/DateTime.hpp:134
void setSecond(int second)
second setter
Definition: DateTime/DateTime.hpp:291
static constexpr int MAX_DAY
Definition: DateTime/DateTime.hpp:54
int minute() const noexcept
minute getter
Definition: DateTime/DateTime.hpp:250
DateTime(const std::string &timestamp)
Definition: DateTime/DateTime.hpp:99
void setDay(int day)
day setter
Definition: DateTime/DateTime.hpp:201
double fractionalSecond() const noexcept
fractionalSecond getter
Definition: DateTime/DateTime.hpp:310
void setMonth(int month)
month setter
Definition: DateTime/DateTime.hpp:171
static TimePoint strToTimepoint(const std::string &timestamp)
Converts the struct to an iso string.
Definition: DateTime/DateTime.hpp:418
std::string toStr() const
Converts the struct to an iso string.
Definition: DateTime/DateTime.hpp:362
static constexpr int MAX_SECOND
Definition: DateTime/DateTime.hpp:57
static constexpr int MAX_MONTH
Definition: DateTime/DateTime.hpp:53
void setMinute(int minute)
minute setter
Definition: DateTime/DateTime.hpp:261
int month() const noexcept
month getter
Definition: DateTime/DateTime.hpp:160
int day() const noexcept
day getter
Definition: DateTime/DateTime.hpp:190
static DateTime now() noexcept
Factory static method for returning a DateTime object cooresponding to the system clock now.
Definition: DateTime/DateTime.hpp:407
DateTime(const TimePoint &tp)
Definition: DateTime/DateTime.hpp:71
void setYear(int year)
year setter
Definition: DateTime/DateTime.hpp:145
Definition: Cartesian.hpp:40
bool operator==(const DateTime &lhs, const DateTime &rhs) noexcept
Equality operator for DateTime.
Definition: DateTime/DateTime.hpp:473
bool operator>=(const std::complex< T > &lhs, const std::complex< T > &rhs) noexcept
Definition: StdComplexOperators.hpp:98
Duration operator-(const DateTime &lhs, const DateTime &rhs) noexcept
Subtraction operator.
Definition: DateTime/DateTime.hpp:551
bool operator>(const std::complex< T > &lhs, const std::complex< T > &rhs) noexcept
Definition: StdComplexOperators.hpp:84
std::chrono::nanoseconds Duration
Duration Type.
Definition: Clock.hpp:16
bool operator!=(const DateTime &lhs, const DateTime &rhs) noexcept
Non Equality operator for DateTime.
Definition: DateTime/DateTime.hpp:486
bool operator<(const std::complex< T > &lhs, const std::complex< T > &rhs) noexcept
Definition: StdComplexOperators.hpp:46
bool operator<=(const std::complex< T > &lhs, const std::complex< T > &rhs) noexcept
Definition: StdComplexOperators.hpp:65
std::chrono::time_point< Clock, Duration > TimePoint
TimePoint Type.
Definition: Clock.hpp:21
std::ostream & operator<<(std::ostream &os, Duration duration)
Output stream operator for the Duration type.
Definition: Clock.hpp:30