| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "../libnetdata.h" |
| 4 | |
| 5 | size_t iso8601_datetime_ut(char *buffer, size_t len, usec_t now_ut, ISO8601_OPTIONS options) { |
| 6 | if(unlikely(!buffer || len == 0)) |
| 7 | return 0; |
| 8 | |
| 9 | time_t t = (time_t)(now_ut / USEC_PER_SEC); |
| 10 | struct tm *tmp, tmbuf; |
| 11 | |
| 12 | if(options & ISO8601_UTC) |
| 13 | // Use gmtime_r for UTC time conversion. |
| 14 | tmp = gmtime_r(&t, &tmbuf); |
| 15 | else |
| 16 | // Use localtime_r for local time conversion. |
| 17 | tmp = localtime_r(&t, &tmbuf); |
| 18 | |
| 19 | if (unlikely(!tmp)) { |
| 20 | buffer[0] = '\0'; |
| 21 | return 0; |
| 22 | } |
| 23 | |
| 24 | // Format the date and time according to the ISO 8601 format. |
| 25 | size_t used_length = strftime(buffer, len, "%Y-%m-%dT%H:%M:%S", tmp); |
| 26 | if (unlikely(used_length == 0)) { |
| 27 | buffer[0] = '\0'; |
| 28 | return 0; |
| 29 | } |
| 30 | |
| 31 | if(options & ISO8601_MILLISECONDS) { |
| 32 | // Calculate the remaining microseconds |
| 33 | int milliseconds = (int) ((now_ut % USEC_PER_SEC) / USEC_PER_MS); |
| 34 | if(milliseconds && len - used_length > 4) |
| 35 | used_length += snprintfz(buffer + used_length, len - used_length, ".%03d", milliseconds); |
| 36 | } |
| 37 | else if(options & ISO8601_MICROSECONDS) { |
| 38 | // Calculate the remaining microseconds |
| 39 | int microseconds = (int) (now_ut % USEC_PER_SEC); |
| 40 | if(microseconds && len - used_length > 7) |
| 41 | used_length += snprintfz(buffer + used_length, len - used_length, ".%06d", microseconds); |
| 42 | } |
| 43 | |
| 44 | if(options & ISO8601_UTC) { |
| 45 | if(used_length + 1 < len) { |
| 46 | buffer[used_length++] = 'Z'; |
| 47 | buffer[used_length] = '\0'; // null-terminate the string. |
| 48 | } |
| 49 | } |
| 50 | else { |
| 51 | // Calculate the timezone offset in hours and minutes from UTC. |
| 52 | long offset = tmbuf.tm_gmtoff; |
| 53 | int hours = (int) (offset / 3600); // Convert offset seconds to hours. |
| 54 | int minutes = (int) ((offset % 3600) / 60); // Convert remainder to minutes (keep the sign for minutes). |
| 55 | |
| 56 | // Check if timezone is UTC. |
| 57 | if(hours == 0 && minutes == 0) { |
| 58 | // For UTC, append 'Z' to the timestamp. |
| 59 | if(used_length + 1 < len) { |
| 60 | buffer[used_length++] = 'Z'; |
| 61 | buffer[used_length] = '\0'; // null-terminate the string. |
| 62 | } |
| 63 | } |
| 64 | else { |
| 65 | // For non-UTC, format the timezone offset. Omit minutes if they are zero. |
| 66 | if(minutes == 0) { |
| 67 | // Check enough space is available for the timezone offset string. |
| 68 | if(used_length + 3 < len) // "+hh\0" |
| 69 | used_length += snprintfz(buffer + used_length, len - used_length, "%+03d", hours); |
| 70 | } |
| 71 | else { |
| 72 | // Check enough space is available for the timezone offset string. |
| 73 | if(used_length + 6 < len) // "+hh:mm\0" |
| 74 | used_length += snprintfz(buffer + used_length, len - used_length, |
| 75 | "%+03d:%02d", hours, abs(minutes)); |
| 76 | } |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | return used_length; |
| 81 | } |