-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathdateutil.hh
101 lines (85 loc) · 3.1 KB
/
dateutil.hh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/* Date utilities */
#ifndef DATEUTIL_HH
#define DATEUTIL_HH
#include <set>
#include <iostream>
#include <string>
#include <sstream>
using std::cerr; using std::string;
using Day_sec = uint64_t;
/* Hour of Influx backup, e.g. 11 for 11am UTC, 23 for 11pm UTC.
* TODO: can we pull this number from somewhere? "T11" maybe? */
static const unsigned BACKUP_HR = 11;
/**
* Given a set of Unix timestamps (seconds), prints contiguous intervals as nice strings.
* E.g. given timestamps representing {Jan 15, Jan 16, Jan 17, Jan 18, Feb 1, Feb 2, Feb 3},
* print Jan 15 : Jan 18, Feb 1 : Feb 3.
* Useful for (pre)confinterval.
*/
void print_intervals(const std::set<Day_sec> & days) {
struct tm ts_fields{};
char day_str[80] = {-1};
char prev_day_str[80] = {-1};
uint64_t prev_day = -1;
for (const Day_sec & day: days) { // set is ordered
// swap at beginning of loop iter so day_str is the current day at loop exit
std::swap(prev_day_str, day_str);
time_t ts_raw = day;
// gmtime assumes argument is GMT
ts_fields = *gmtime(&ts_raw);
strftime(day_str, sizeof(day_str), "%Y-%m-%d", &ts_fields);
if (difftime(day, prev_day) > 60 * 60 * 24) {
// end of previous interval
if (prev_day != -1UL) {
cerr << prev_day_str << "\n";
}
// start of next interval
cerr << day_str << " : ";
}
prev_day = day;
}
// end of last interval
if (prev_day != -1UL) {
cerr << day_str << "\n";
}
}
/* Parse date string or prefix to
* Unix timestamp (seconds) at Influx backup hour,
* e.g. 2019-11-28 or 2019-11-28T11_2019-11-29T11 => 1574938800 (for 11AM UTC backup) */
std::optional<Day_sec> str2Day_sec(const string & date_str) {
// TODO: check format
// If no T, takes entire string
const auto T_pos = date_str.find('T');
const string & start_day = date_str.substr(0, T_pos);
struct tm day_fields{};
std::ostringstream strptime_str;
strptime_str << start_day << " " << BACKUP_HR << ":00:00";
if (not strptime(strptime_str.str().c_str(), "%Y-%m-%d %H:%M:%S", &day_fields)) {
return {};
}
// set timezone to UTC for mktime
char* tz = getenv("TZ");
setenv("TZ", "UTC", 1);
tzset();
Day_sec start_ts = mktime(&day_fields);
tz ? setenv("TZ", tz, 1) : unsetenv("TZ");
tzset();
return start_ts;
}
/* Round down ts *in seconds* to nearest backup hour. */
Day_sec ts2Day_sec(uint64_t ts) {
if (ts > 9999999999) {
throw(std::logic_error("ts2Day_sec operates on seconds, not nanoseconds"));
}
const unsigned sec_per_hr = 60 * 60;
const unsigned sec_per_day = sec_per_hr * 24;
unsigned day_index = ts / sec_per_day;
const unsigned sec_past_midnight = ts % sec_per_day;
if (sec_past_midnight < BACKUP_HR * sec_per_hr) {
/* If ts is before backup hour, it belongs to the previous day's backup
* (e.g. Jan 2 1:00 am belongs to Jan 1 11:00 am backup) */
day_index--;
}
return day_index * sec_per_day + BACKUP_HR * sec_per_hr;
}
#endif