123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- // 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 >= 0 && 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(s[1]) || !isdigit(s[2]) || s[3] != ':' ||
- !isdigit(s[4]) || !isdigit(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;
- }
|