KSSUtil  v14.2.0
C++ general utility library
timeutil.hpp
Go to the documentation of this file.
1 //
2 // timeutil.hpp
3 // kssutil
4 //
5 // Created by Steven W. Klassen on 2013-01-04.
6 // Copyright (c) 2013 Klassen Software Solutions. All rights reserved.
7 // Licensing follows the MIT License.
8 //
9 
15 #ifndef kssutil_timeutil_hpp
16 #define kssutil_timeutil_hpp
17 
18 #include <chrono>
19 #include <cmath>
20 #include <cstring>
21 #include <functional>
22 #include <istream>
23 #include <locale>
24 #include <ostream>
25 #include <stdexcept>
26 #include <string>
27 #include <type_traits>
28 
29 #include <kss/contract/all.h>
30 
31 namespace kss { namespace util { namespace time {
32 
43  template <class ToDuration, class Rep, class Period>
44  ToDuration checkedDurationCast(const std::chrono::duration<Rep, Period>& dtn) {
45  using namespace std::chrono;
46  using S = duration<long double, typename ToDuration::period>;
47  constexpr S minimumAllowed = ToDuration::min();
48  constexpr S maximumAllowed = ToDuration::max();
49  const S s = dtn;
50  if (s < minimumAllowed || s > maximumAllowed) {
51  throw std::overflow_error("checked_duration_cast");
52  }
53  return duration_cast<ToDuration>(s);
54  }
55 
56  namespace _private {
57  std::string format(const std::string &fmt, const struct tm &tm) noexcept;
58 
59  struct tm& parseIso8601(const std::string& timestr,
60  struct tm& tm,
61  std::chrono::nanoseconds& ns);
62 
63  std::string toIso8601(const struct tm& tm,
64  const std::chrono::nanoseconds& ns);
65 
66  time_t parseLocalized(const std::string& s,
67  const std::locale& loc,
68  const std::string& tzone);
69 
70  std::string toLocalized(time_t t,
71  const std::locale& loc,
72  const std::string& tzone);
73 
74  time_t readFromInputStream(std::istream& strm);
75  time_t tmToTimeT(const struct tm* tm);
76  struct tm* tzTimeR(const time_t* timep, struct tm* result, const char* tzone, char** tzout);
77 
78  template <class Rep, class Period>
79  std::string getDurationSuffix(const std::chrono::duration<Rep, Period>& dtn) noexcept {
80  if (std::ratio_equal<Period, std::chrono::hours::period>::value) {
81  return "h";
82  }
83  if (std::ratio_equal<Period, std::chrono::minutes::period>::value) {
84  return "min";
85  }
86  if (std::ratio_equal<Period, std::chrono::seconds::period>::value) {
87  return "s";
88  }
89  if (std::ratio_equal<Period, std::chrono::milliseconds::period>::value) {
90  return "ms";
91  }
92  if (std::ratio_equal<Period, std::chrono::microseconds::period>::value) {
93  return "us";
94  }
95  if (std::ratio_equal<Period, std::chrono::nanoseconds::period>::value) {
96  return "ns";
97  }
98  return std::string();
99  }
100  }
101 
122  inline std::string formatIso8601(const struct tm& tm) noexcept {
123  return _private::format("%FT%TZ", tm);
124  }
125  inline struct tm& parseIso8601(const std::string& timestr, struct tm& tm) {
126  std::chrono::nanoseconds ns;
127  return _private::parseIso8601(timestr, tm, ns);
128  }
129 
130  template <class Duration>
131  inline struct tm& parseIso8601(const std::string& timestr, struct tm& tm, Duration& subseconds) {
132  std::chrono::nanoseconds ns;
133  _private::parseIso8601(timestr, tm, ns);
134  subseconds = checkedDurationCast<Duration>(ns);
135  return tm;
136  }
137 
141  std::chrono::milliseconds timeOfExecution(const std::function<void()>& fn);
142 
143 
144  // MARK: time_point extensions
145 
146  // Note that the TimePoint template type needs to be a type compatible with
147  // std::chrono::time_point<Clock, Duration> for some value of Clock and
148  // Duration. This is checked using the following macro. If you get errors
149  // reported on this macro, check that your argument for TimePoint is
150  // actually a time_point type.
151 # define _KSS_IS_TIMEPOINT(TP) \
152  static_assert(std::is_object<typename TP::clock>::value \
153  && std::is_object<typename TP::duration>::value, \
154  "TP must be a std::chrono::time_point<Clock, Duration>")
155 
164  template <class TimePoint>
165  inline TimePoint now(const TimePoint& = TimePoint()) {
166  _KSS_IS_TIMEPOINT(TimePoint);
167  using Clock = typename TimePoint::clock;
168  using Duration = typename TimePoint::duration;
169  const auto dur = Clock::now().time_since_epoch();
170  return TimePoint(checkedDurationCast<Duration>(dur));
171  }
172 
179  template <class TimePoint>
180  inline TimePoint fromTimeT(time_t t, const TimePoint& = TimePoint()) {
181  _KSS_IS_TIMEPOINT(TimePoint);
182  const auto secs = std::chrono::seconds(t);
183  return TimePoint(checkedDurationCast<typename TimePoint::duration>(secs));
184  }
185 
189  template <class TimePoint>
190  inline TimePoint fromTm(const struct tm& tm, const TimePoint& typeArg = TimePoint()) {
191  _KSS_IS_TIMEPOINT(TimePoint);
192  return fromTimeT(_private::tmToTimeT(&tm), typeArg);
193  }
194 
204  template <class TimePoint>
205  TimePoint fromIso8601String(const std::string& s, const TimePoint& typeArg = TimePoint()) {
206  _KSS_IS_TIMEPOINT(TimePoint);
207  struct tm tm;
208  memset(&tm, 0, sizeof(struct tm));
209  std::chrono::nanoseconds ns(0);
210  parseIso8601(s, tm, ns);
211 
212  auto t = fromTm(tm, typeArg);
213  if (ns.count() != 0) {
214  t += std::chrono::duration_cast<typename TimePoint::duration>(ns);
215  }
216  return t;
217  }
218 
234  template <class TimePoint>
235  inline TimePoint fromLocalizedString(const std::string& s,
236  const std::locale& loc = std::locale(),
237  const std::string& tzone = std::string(),
238  const TimePoint& typeArg = TimePoint())
239  {
240  _KSS_IS_TIMEPOINT(TimePoint);
241  return fromTimeT(_private::parseLocalized(s, loc, tzone), typeArg);
242  }
243 
249  template <class Clock, class Duration = typename Clock::duration>
250  time_t toTimeT(const std::chrono::time_point<Clock, Duration>& tp) {
251  using ttduration = std::chrono::duration<time_t, std::chrono::seconds::period>;
252  const auto d = checkedDurationCast<ttduration>(tp.time_since_epoch());
253  return d.count();
254  }
255 
262  template <class Clock, class Duration = typename Clock::duration>
263  struct tm& toTm(const std::chrono::time_point<Clock, Duration>& tp, struct tm& tm) {
264  const auto tt = toTimeT(tp);
265  _private::tzTimeR(&tt, &tm, nullptr, nullptr);
266  return tm;
267  }
268 
276  template <class Clock, class Duration = typename Clock::duration>
277  std::string toIso8601String(const std::chrono::time_point<Clock, Duration>& tp) {
278  struct tm tm;
279  toTm(tp, tm);
280  const auto secs = checkedDurationCast<std::chrono::seconds>(tp.time_since_epoch());
281  const auto subsecs = tp.time_since_epoch() - secs;
282  std::chrono::nanoseconds ns(0);
283  if (subsecs.count() > 0) {
284  typename Duration::period p;
285  const auto subSecsAsSeconds = (long double)subsecs.count() * p.num / p.den;
286  kss::contract::conditions({
287  KSS_EXPR(subSecsAsSeconds <= 1) // should be a fraction of a second
288  });
289  const auto subSecsRoundedToNs = std::round(subSecsAsSeconds * 1000000000);
290  const auto rep = std::chrono::nanoseconds::rep(subSecsRoundedToNs);
291  ns = std::chrono::nanoseconds(rep);
292  }
293  return _private::toIso8601(tm, ns);
294  }
295 
307  template <class Clock, class Duration = typename Clock::duration>
308  inline std::string toLocalizedString(const std::chrono::time_point<Clock, Duration>& tp,
309  const std::locale& loc = std::locale(),
310  const std::string& tzone = std::string())
311  {
312  return _private::toLocalized(toTimeT(tp), loc, tzone);
313  }
314 
315 }}}
316 
317 namespace std {
322  template <class Clock, class Duration = typename Clock::duration>
323  ostream& operator<<(ostream& strm, const chrono::time_point<Clock, Duration>& tp) {
324  const auto loc = strm.getloc();
325  strm << kss::util::time::toLocalizedString(tp, loc);
326  return strm;
327  }
328 
333  template <class Clock, class Duration = typename Clock::duration>
334  inline istream& operator>>(istream& strm, chrono::time_point<Clock, Duration>& tp) {
335  tp = kss::util::time::fromTimeT(kss::util::time::_private::readFromInputStream(strm), tp);
336  return strm;
337  }
338 
344  template <class Rep, class Period>
345  ostream& operator<<(ostream& strm, const chrono::duration<Rep, Period>& dtn) {
346  const auto suffix = kss::util::time::_private::getDurationSuffix(dtn);
347  if (suffix.empty()) {
348  long double count = static_cast<long double>(dtn.count());
349  count *= static_cast<long double>(Period::num) / static_cast<long double>(Period::den);
350  strm << count << "s(approx)";
351  }
352  else {
353  strm << dtn.count() << suffix;
354  }
355  return strm;
356  }
357 
361  template <class Rep, class Period>
362  istream& operator>>(istream& strm, chrono::duration<Rep, Period>& dtn) {
363  const auto suffix = kss::util::time::_private::getDurationSuffix(dtn);
364  if (suffix.empty()) {
365  strm.setstate(istream::failbit);
366  }
367  else {
368  char ch;
369  Rep count(0);
370  strm >> count;
371  for (const auto sch : suffix) {
372  strm >> ch;
373  if (sch != ch) {
374  strm.setstate(istream::failbit);
375  }
376  }
377  dtn = chrono::duration<Rep, Period>(count);
378  }
379  return strm;
380  }
381 }
382 #endif
kss::util::time::toIso8601String
std::string toIso8601String(const std::chrono::time_point< Clock, Duration > &tp)
Definition: timeutil.hpp:277
kss::util::time::parseIso8601
struct tm & parseIso8601(const std::string &timestr, struct tm &tm)
Definition: timeutil.hpp:125
std::operator>>
istream & operator>>(istream &strm, chrono::duration< Rep, Period > &dtn)
Definition: timeutil.hpp:362
kss::util::time::toTimeT
time_t toTimeT(const std::chrono::time_point< Clock, Duration > &tp)
Definition: timeutil.hpp:250
kss::util::time::fromTimeT
TimePoint fromTimeT(time_t t, const TimePoint &=TimePoint())
Definition: timeutil.hpp:180
kss::util::time::formatIso8601
std::string formatIso8601(const struct tm &tm) noexcept
Definition: timeutil.hpp:122
kss::util::time::fromLocalizedString
TimePoint fromLocalizedString(const std::string &s, const std::locale &loc=std::locale(), const std::string &tzone=std::string(), const TimePoint &typeArg=TimePoint())
Definition: timeutil.hpp:235
kss::util::time::timeOfExecution
std::chrono::milliseconds timeOfExecution(const std::function< void()> &fn)
kss::util::time::fromTm
TimePoint fromTm(const struct tm &tm, const TimePoint &typeArg=TimePoint())
Definition: timeutil.hpp:190
kss::util::time::checkedDurationCast
ToDuration checkedDurationCast(const std::chrono::duration< Rep, Period > &dtn)
Definition: timeutil.hpp:44
std
Definition: iterator.hpp:528
kss::util::time::parseIso8601
struct tm & parseIso8601(const std::string &timestr, struct tm &tm, Duration &subseconds)
Definition: timeutil.hpp:131
kss::util::time::now
TimePoint now(const TimePoint &=TimePoint())
Definition: timeutil.hpp:165
kss::util::time::toLocalizedString
std::string toLocalizedString(const std::chrono::time_point< Clock, Duration > &tp, const std::locale &loc=std::locale(), const std::string &tzone=std::string())
Definition: timeutil.hpp:308
kss::util::time::toTm
struct tm & toTm(const std::chrono::time_point< Clock, Duration > &tp, struct tm &tm)
Definition: timeutil.hpp:263
kss
All Klassen Software Solutions libraries begin with this namespace.
Definition: add_rel_ops.hpp:19
kss::util::time::fromIso8601String
TimePoint fromIso8601String(const std::string &s, const TimePoint &typeArg=TimePoint())
Definition: timeutil.hpp:205
std::operator<<
ostream & operator<<(ostream &strm, const chrono::duration< Rep, Period > &dtn)
Definition: timeutil.hpp:345