rfc3339.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "../libnetdata.h"
  3. #include "rfc3339.h"
  4. size_t rfc3339_datetime_ut(char *buffer, size_t len, usec_t now_ut, size_t fractional_digits, bool utc) {
  5. if (!buffer || len == 0)
  6. return 0;
  7. time_t t = (time_t)(now_ut / USEC_PER_SEC);
  8. struct tm *tmp, tmbuf;
  9. if (utc)
  10. tmp = gmtime_r(&t, &tmbuf);
  11. else
  12. tmp = localtime_r(&t, &tmbuf);
  13. if (!tmp) {
  14. buffer[0] = '\0';
  15. return 0;
  16. }
  17. size_t used_length = strftime(buffer, len, "%Y-%m-%dT%H:%M:%S", tmp);
  18. if (used_length == 0) {
  19. buffer[0] = '\0';
  20. return 0;
  21. }
  22. if (fractional_digits >= 0 && fractional_digits <= 9) {
  23. int fractional_part = (int)(now_ut % USEC_PER_SEC);
  24. if (fractional_part && len - used_length > fractional_digits + 1) {
  25. char format[] = ".%01d";
  26. format[3] = (char)('0' + fractional_digits);
  27. // Adjust fractional part
  28. fractional_part /= (int)pow(10, 6 - fractional_digits);
  29. used_length += snprintf(buffer + used_length, len - used_length,
  30. format, fractional_part);
  31. }
  32. }
  33. if (utc) {
  34. if (used_length + 1 < len) {
  35. buffer[used_length++] = 'Z';
  36. buffer[used_length] = '\0';
  37. }
  38. }
  39. else {
  40. long offset = tmbuf.tm_gmtoff;
  41. int hours = (int)(offset / 3600);
  42. int minutes = abs((int)((offset % 3600) / 60));
  43. if (used_length + 7 < len) { // Space for "+HH:MM\0"
  44. used_length += snprintf(buffer + used_length, len - used_length, "%+03d:%02d", hours, minutes);
  45. }
  46. }
  47. return used_length;
  48. }
  49. usec_t rfc3339_parse_ut(const char *rfc3339, char **endptr) {
  50. struct tm tm = { 0 };
  51. int tz_hours = 0, tz_mins = 0;
  52. char *s;
  53. usec_t timestamp, usec = 0;
  54. // Use strptime to parse up to seconds
  55. s = strptime(rfc3339, "%Y-%m-%dT%H:%M:%S", &tm);
  56. if (!s)
  57. return 0; // Parsing error
  58. // Parse fractional seconds if present
  59. if (*s == '.') {
  60. char *next;
  61. usec = strtoul(s + 1, &next, 10);
  62. int digits_parsed = (int)(next - (s + 1));
  63. if (digits_parsed < 1 || digits_parsed > 9)
  64. return 0; // parsing error
  65. static const usec_t fix_usec[] = {
  66. 1000000, // 0 digits (not used)
  67. 100000, // 1 digit
  68. 10000, // 2 digits
  69. 1000, // 3 digits
  70. 100, // 4 digits
  71. 10, // 5 digits
  72. 1, // 6 digits
  73. 10, // 7 digits
  74. 100, // 8 digits
  75. 1000, // 9 digits
  76. };
  77. usec = digits_parsed <= 6 ? usec * fix_usec[digits_parsed] : usec / fix_usec[digits_parsed];
  78. s = next;
  79. }
  80. // Check and parse timezone if present
  81. int tz_offset = 0;
  82. if (*s == '+' || *s == '-') {
  83. // Parse the hours:mins part of the timezone
  84. if (!isdigit(s[1]) || !isdigit(s[2]) || s[3] != ':' ||
  85. !isdigit(s[4]) || !isdigit(s[5]))
  86. return 0; // Parsing error
  87. char tz_sign = *s;
  88. tz_hours = (s[1] - '0') * 10 + (s[2] - '0');
  89. tz_mins = (s[4] - '0') * 10 + (s[5] - '0');
  90. tz_offset = tz_hours * 3600 + tz_mins * 60;
  91. tz_offset *= (tz_sign == '+' ? 1 : -1);
  92. s += 6; // Move past the timezone part
  93. }
  94. else if (*s == 'Z')
  95. s++;
  96. else
  97. return 0; // Invalid RFC 3339 format
  98. // Convert to time_t (assuming local time, then adjusting for timezone later)
  99. time_t epoch_s = mktime(&tm);
  100. if (epoch_s == -1)
  101. return 0; // Error in time conversion
  102. timestamp = (usec_t)epoch_s * USEC_PER_SEC + usec;
  103. timestamp -= tz_offset * USEC_PER_SEC;
  104. if(endptr)
  105. *endptr = s;
  106. return timestamp;
  107. }