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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
// SPDX-License-Identifier: GPL-3.0-or-later
#include "../libnetdata.h"
#include "rfc3339.h"
size_t rfc3339_datetime_ut(char *buffer, size_t len, usec_t now_ut, size_t fractional_digits, bool utc) {
if (!buffer || len == 0)
return 0;
time_t t = (time_t)(now_ut / USEC_PER_SEC);
struct tm *tmp, tmbuf;
if (utc)
tmp = gmtime_r(&t, &tmbuf);
else
tmp = localtime_r(&t, &tmbuf);
if (!tmp) {
buffer[0] = '\0';
return 0;
}
size_t used_length = strftime(buffer, len, "%Y-%m-%dT%H:%M:%S", tmp);
if (used_length == 0) {
buffer[0] = '\0';
return 0;
}
if (fractional_digits >= 1 && fractional_digits <= 9) {
int fractional_part = (int)(now_ut % USEC_PER_SEC);
if (fractional_part && len - used_length > fractional_digits + 1) {
char format[] = ".%01d";
format[3] = (char)('0' + fractional_digits);
// Adjust fractional part
fractional_part /= (int)pow(10, 6 - fractional_digits);
used_length += snprintf(buffer + used_length, len - used_length,
format, fractional_part);
}
}
if (utc) {
if (used_length + 1 < len) {
buffer[used_length++] = 'Z';
buffer[used_length] = '\0';
}
}
else {
long offset = tmbuf.tm_gmtoff;
int hours = (int)(offset / 3600);
int minutes = abs((int)((offset % 3600) / 60));
if (used_length + 7 < len) { // Space for "+HH:MM\0"
used_length += snprintf(buffer + used_length, len - used_length, "%+03d:%02d", hours, minutes);
}
}
return used_length;
}
usec_t rfc3339_parse_ut(const char *rfc3339, char **endptr) {
struct tm tm = { 0 };
int tz_hours = 0, tz_mins = 0;
char *s;
usec_t timestamp, usec = 0;
// Use strptime to parse up to seconds
s = strptime(rfc3339, "%Y-%m-%dT%H:%M:%S", &tm);
if (!s)
return 0; // Parsing error
// Parse fractional seconds if present
if (*s == '.') {
char *next;
usec = strtoul(s + 1, &next, 10);
int digits_parsed = (int)(next - (s + 1));
if (digits_parsed < 1 || digits_parsed > 9)
return 0; // parsing error
static const usec_t fix_usec[] = {
1000000, // 0 digits (not used)
100000, // 1 digit
10000, // 2 digits
1000, // 3 digits
100, // 4 digits
10, // 5 digits
1, // 6 digits
10, // 7 digits
100, // 8 digits
1000, // 9 digits
};
usec = digits_parsed <= 6 ? usec * fix_usec[digits_parsed] : usec / fix_usec[digits_parsed];
s = next;
}
// Check and parse timezone if present
int tz_offset = 0;
if (*s == '+' || *s == '-') {
// Parse the hours:mins part of the timezone
if (!isdigit((uint8_t)s[1]) || !isdigit((uint8_t)s[2]) || s[3] != ':' ||
!isdigit((uint8_t)s[4]) || !isdigit((uint8_t)s[5]))
return 0; // Parsing error
char tz_sign = *s;
tz_hours = (s[1] - '0') * 10 + (s[2] - '0');
tz_mins = (s[4] - '0') * 10 + (s[5] - '0');
tz_offset = tz_hours * 3600 + tz_mins * 60;
tz_offset *= (tz_sign == '+' ? 1 : -1);
s += 6; // Move past the timezone part
}
else if (*s == 'Z')
s++;
else
return 0; // Invalid RFC 3339 format
// Convert to time_t (assuming local time, then adjusting for timezone later)
time_t epoch_s = mktime(&tm);
if (epoch_s == -1)
return 0; // Error in time conversion
timestamp = (usec_t)epoch_s * USEC_PER_SEC + usec;
timestamp -= tz_offset * USEC_PER_SEC;
if(endptr)
*endptr = s;
return timestamp;
}
|