Coverage Report

Created: 2026-09-04 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hobbes/include/hobbes/util/time.H
Line
Count
Source
1
2
#ifndef HOBBES_UTIL_TIME_HPP_INCLUDED
3
#define HOBBES_UTIL_TIME_HPP_INCLUDED
4
5
#include <cstdint>
6
#include <cstring>
7
#include <ctime>
8
#include <hobbes/util/str.H>
9
#include <limits>
10
#include <sstream>
11
#include <string>
12
13
namespace hobbes {
14
15
/*******
16
 * timespans (a number of microseconds)
17
 *
18
 *  e.g.: 5s, 20m, 4day2h3m30s200ms456us, ...
19
 *
20
 * *****/
21
// timespans are accumulated out of non-negative terms, so rather than let an
22
// absurdly large literal (e.g. "9223372036854775807day") wrap around, we
23
// saturate at the largest representable timespan
24
0
inline long saturatingTimespanScale(uint64_t n, long m) {
25
0
  static const uint64_t maxts = static_cast<uint64_t>(std::numeric_limits<long>::max());
26
27
0
  const uint64_t um = static_cast<uint64_t>(m);
28
0
  if (um == 0 || n > (maxts / um)) {
29
0
    return (um == 0) ? 0 : std::numeric_limits<long>::max();
30
0
  }
31
0
  return static_cast<long>(n * um);
32
0
}
33
34
0
inline long saturatingTimespanAdd(long x, long y) {
35
0
  static const long maxts = std::numeric_limits<long>::max();
36
37
0
  if (x > (maxts - y)) {
38
0
    return maxts;
39
0
  }
40
0
  return x + y;
41
0
}
42
43
0
inline long readTimespan(std::string s) {
44
0
  long r = 0;
45
46
0
  while (!s.empty()) {
47
0
    str::pair p = str::readWhile(&str::isDigit,    s);
48
0
    str::pair u = str::readWhile(&str::isNotDigit, p.second);
49
50
0
    long m = 1;
51
0
    if (u.first == "us") {                            // microseconds
52
0
      m = 1;
53
0
    } else if (u.first == "ms") {                     // milliseconds
54
0
      m = 1000;
55
0
    } else if (u.first == "s") {                      // seconds
56
0
      m = 1000000;
57
0
    } else if (u.first == "m" || u.first == "min") {  // minutes
58
0
      m = 1000000*60;
59
0
    } else if (u.first == "h" || u.first == "hour") { // hours
60
0
      m = 1000000L*60*60;
61
0
    } else if (u.first == "d" || u.first == "day") {  // days
62
0
      m = 1000000L*60*60*24;
63
0
    }
64
65
    // an out-of-range digit sequence reads as UINT64_MAX, which saturates below
66
0
    r = saturatingTimespanAdd(r, saturatingTimespanScale(str::to<uint64_t>(p.first), m));
67
0
    s = u.second;
68
0
  }
69
70
0
  return r;
71
0
}
72
73
0
inline long readTimespan(const str::seq& ss) {
74
0
  long r = 0;
75
0
  for (const auto& s : ss) {
76
0
    r = saturatingTimespanAdd(r, readTimespan(s));
77
0
  }
78
0
  return r;
79
0
}
80
81
0
inline std::string showTimespan(long ts) {
82
0
  static const long msus  = 1000;
83
0
  static const long sus   = msus * 1000;
84
0
  static const long mus   = sus * 60;
85
0
  static const long hus   = mus * 60;
86
0
  static const long dayus = hus * 24;
87
0
88
0
  uint64_t x  = labs(ts);
89
0
  uint64_t d  = x / dayus; x -= d*dayus;
90
0
  uint64_t h  = x / hus;   x -= h*hus;
91
0
  uint64_t m  = x / mus;   x -= m*mus;
92
0
  uint64_t s  = x / sus;   x -= s*sus;
93
0
  uint64_t ms = x / msus;  x -= ms*msus;
94
0
  uint64_t us = x;
95
0
96
0
  std::ostringstream ss;
97
0
  if (ts < 0) ss << "-";
98
0
  if (d > 0)        ss << d << "d";
99
0
  if (h > 0)        ss << h << "h";
100
0
  if (m > 0)        ss << m << "m";
101
0
  if (s > 0 || (d == 0 && h == 0 && m == 0 && ms == 0 && us == 0)) {
102
0
    ss << s << "s";
103
0
  }
104
0
  if (ms > 0)       ss << ms << "ms";
105
0
  if (us > 0)       ss << us << "us";
106
0
107
0
  return ss.str();
108
0
}
109
110
/*******
111
 * times (microseconds since a base time)
112
 *
113
 *   e.g.: 01:00:00.000000, 15:34:57.123456, ...
114
 *
115
 *******/
116
0
inline long mkTime(int h, int m, int s, int u) {
117
0
  tm t;
118
0
  memset(&t, 0, sizeof(t));
119
120
0
  t.tm_sec   = s;
121
0
  t.tm_min   = m;
122
0
  t.tm_hour  = h;
123
0
  t.tm_mday  = 1;
124
0
  t.tm_mon   = 0;
125
0
  t.tm_year  = 70;
126
0
  t.tm_isdst = -1;
127
128
0
  return (mktime(&t) * 1000 * 1000) + u;
129
0
}
130
131
0
inline long readTime(const std::string& x) {
132
0
  str::pair h_msu = str::lsplit(x, ":");
133
0
  str::pair m_su  = str::lsplit(h_msu.second, ":");
134
0
  str::pair s_u   = str::lsplit(m_su.second,  ".");
135
136
0
  int h = h_msu.first.empty() ? 0 : str::to<int>(h_msu.first);
137
0
  int m = m_su.first.empty()  ? 0 : str::to<int>(m_su.first);
138
0
  int s = s_u.first.empty()   ? 0 : str::to<int>(s_u.first);
139
0
  int u = s_u.second.empty()  ? 0 : (str::to<int>(s_u.second) * (s_u.second.size()==3?1000:1));
140
141
0
  return mkTime(h,m,s,u);
142
0
}
143
144
0
inline std::string showTime(long x) {
145
0
  int64_t s  = x / (1000 * 1000);
146
0
  int64_t us = x % (1000 * 1000);
147
0
  if (us < 0) {
148
0
    s -= 1;
149
0
    us += 1000L * 1000L;
150
0
  }
151
0
152
0
  static char buf[256];
153
0
  strftime(buf, sizeof(buf), "%H:%M:%S", localtime(reinterpret_cast<time_t*>(&s)));
154
0
155
0
  std::ostringstream uss;
156
0
  uss << us;
157
0
158
0
  std::ostringstream ss;
159
0
  ss << buf;
160
0
  ss << ".";
161
0
  ss << std::string((uss.str().size() < 6) ? (6 - uss.str().size()) : 0, '0') << uss.str();
162
0
163
0
  return ss.str();
164
0
}
165
166
/*******
167
 * datetimes (microseconds since epoch)
168
 *
169
 *   e.g.: 2015-01-01T01:00:00.000000, 1980-05-19T15:34:57.123456, ...
170
 *
171
 *******/
172
0
inline long mkDateTime(int y, int mon, int d, int h, int min, int s, int u) {
173
0
  tm t;
174
0
  memset(&t, 0, sizeof(t));
175
176
0
  t.tm_sec   = s;
177
0
  t.tm_min   = min;
178
0
  t.tm_hour  = h;
179
0
  t.tm_mday  = d;
180
0
  t.tm_mon   = mon - 1;
181
0
  t.tm_year  = y - 1900;
182
0
  t.tm_isdst = -1;
183
184
0
  return (mktime(&t) * 1000L * 1000L) + u;
185
0
}
186
187
0
inline long timeFromDateTime(long x) {
188
0
  int64_t s  = x / (1000L * 1000L);
189
0
  int64_t us = x % (1000L * 1000L);
190
0
  tm*     t  = localtime(reinterpret_cast<time_t*>(&s));
191
0
192
0
  if (t != nullptr) {
193
0
    return mkTime(t->tm_hour, t->tm_min, t->tm_sec, us);
194
0
  } else {
195
0
    return mkTime(0, 0, 0, 0);
196
0
  }
197
0
}
198
199
0
inline long dateFromDateTime(long x) {
200
0
  int64_t s  = x / (1000L * 1000L);
201
0
  tm t;
202
0
  localtime_r(reinterpret_cast<time_t*>(&s), &t);
203
0
  
204
0
  t.tm_sec  = 0;
205
0
  t.tm_min  = 0;
206
0
  t.tm_hour = 0;
207
0
208
0
  return mktime(&t) * 1000L * 1000L;
209
0
}
210
211
0
inline long readDateTime(const std::string& x) {
212
0
  str::pair y_mdThmsu = str::lsplit(x, "-");
213
0
  str::pair m_dThmsu  = str::lsplit(y_mdThmsu.second, "-");
214
0
  str::pair d_Thmsu   = str::lsplit(m_dThmsu.second, "T");
215
0
  str::pair h_msu     = str::lsplit(d_Thmsu.second, ":");
216
0
  str::pair m_su      = str::lsplit(h_msu.second, ":");
217
0
  str::pair s_u       = str::lsplit(m_su.second, ".");
218
219
0
  int y   = y_mdThmsu.first.empty() ? 0 : str::to<int>(y_mdThmsu.first);
220
0
  int mon = m_dThmsu.first.empty()  ? 0 : str::to<int>(m_dThmsu.first);
221
0
  int d   = d_Thmsu.first.empty()   ? 0 : str::to<int>(d_Thmsu.first);
222
223
0
  int h   = h_msu.first.empty() ? 0 : str::to<int>(h_msu.first);
224
0
  int min = m_su.first.empty()  ? 0 : str::to<int>(m_su.first);
225
0
  int s   = s_u.first.empty()   ? 0 : str::to<int>(s_u.first);
226
0
  int u   = s_u.second.empty()  ? 0 : (str::to<int>(s_u.second) * (s_u.second.size()==3?1000:1));
227
228
0
  return mkDateTime(y, mon, d, h, min, s, u);
229
0
}
230
231
0
inline std::string showDateTime(long x) {
232
0
  int64_t s  = x / (1000L * 1000L);
233
0
  int64_t us = x % (1000L * 1000L);
234
0
  if (us < 0) {
235
0
    s -= 1;
236
0
    us += 1000L * 1000L;
237
0
  }
238
0
239
0
  static char buf[256];
240
0
  strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", localtime(reinterpret_cast<time_t*>(&s)));
241
0
242
0
  std::ostringstream uss;
243
0
  uss << us;
244
0
245
0
  std::ostringstream ss;
246
0
  ss << buf;
247
0
  ss << ".";
248
0
  ss << std::string((uss.str().size() < 6) ? (6 - uss.str().size()) : 0, '0') << uss.str();
249
0
250
0
  return ss.str();
251
0
}
252
253
}
254
255
#endif