1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-24 22:13:52 +01:00

sys/fmt: add functins for ISO 8601 date and time

This commit is contained in:
Fabian Hüßler 2025-04-08 18:26:12 +02:00
parent 3c649239b6
commit 0f81f50e3a
2 changed files with 140 additions and 0 deletions

View File

@ -24,6 +24,7 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "container.h"
@ -472,6 +473,55 @@ size_t fmt_to_lower(char *out, const char *str)
return len;
}
int fmt_time_tm_iso8601(char out[20], const struct tm *tm, char separator)
{
assert(out);
assert(tm);
/* The lowest year allowed in ISO 8601 is 0000 (year zero), which represents 1 BCE */
if ((tm->tm_year < -1900 || tm->tm_year > 9999 - 1900) ||
(tm->tm_mon < -1 || tm->tm_mon > 99 - 1) ||
(tm->tm_mday < 0 || tm->tm_mday > 99) ||
(tm->tm_hour < 0 || tm->tm_hour > 99) ||
(tm->tm_min < 0 || tm->tm_min > 99) ||
(tm->tm_sec < 0 || tm->tm_sec > 99)) {
return -EINVAL;
}
char *pos = out;
int len;
len = fmt_u16_dec(pos, tm->tm_year + 1900);
fmt_lpad(pos, len, 4, '0');
pos += 4;
*pos++ = '-';
len = fmt_u16_dec(pos, tm->tm_mon + 1);
fmt_lpad(pos, len, 2, '0');
pos += 2;
*pos++ = '-';
len = fmt_u16_dec(pos, tm->tm_mday);
fmt_lpad(pos, len, 2, '0');
pos += 2;
*pos++ = separator;
len = fmt_u16_dec(pos, tm->tm_hour);
fmt_lpad(pos, len, 2, '0');
pos += 2;
*pos++ = ':';
len = fmt_u16_dec(pos, tm->tm_min);
fmt_lpad(pos, len, 2, '0');
pos += 2;
*pos++ = ':';
len = fmt_u16_dec(pos, tm->tm_sec);
fmt_lpad(pos, len, 2, '0');
pos += 2;
*pos = '\0';
return pos - out;
}
uint32_t scn_u32_dec(const char *str, size_t n)
{
uint32_t res = 0;
@ -567,6 +617,54 @@ ssize_t scn_buf_hex(void *_dest, size_t dest_len, const char *hex, size_t hex_le
return len;
}
int scn_time_tm_iso8601(struct tm *tm, const char *str, char separator)
{
assert(tm);
assert(str);
memset(tm, 0, sizeof(*tm));
tm->tm_isdst = -1; /* undefined */
if (!fmt_is_digit(str[0]) || !fmt_is_digit(str[1]) ||
!fmt_is_digit(str[2]) || !fmt_is_digit(str[3]) ||
str[4] != '-' ||
!fmt_is_digit(str[5]) || !fmt_is_digit(str[6]) ||
str[7] != '-' ||
!fmt_is_digit(str[8]) || !fmt_is_digit(str[9])) {
return -EINVAL;
}
uint32_t num = scn_u32_dec(str, 4);
tm->tm_year = num - 1900;
num = scn_u32_dec(str + 5, 2);
tm->tm_mon = num - 1;
num = scn_u32_dec(str + 8, 2);
tm->tm_mday = num;
if (str[10] == '\0') {
/* no time, just date */
return 10;
}
if (str[10] != separator) {
return -EBADF;
}
if (!fmt_is_digit(str[11]) || !fmt_is_digit(str[12]) ||
str[13] != ':' ||
!fmt_is_digit(str[14]) || !fmt_is_digit(str[15]) ||
str[16] != ':' ||
!fmt_is_digit(str[17]) || !fmt_is_digit(str[18])) {
return -EINVAL;
}
num = scn_u32_dec(str + 11, 2);
tm->tm_hour = num;
num = scn_u32_dec(str + 14, 2);
tm->tm_min = num;
num = scn_u32_dec(str + 17, 2);
tm->tm_sec = num;
return 19;
}
/* native gets special treatment as native's stdio code is ... special.
* And when not building for RIOT, there's no `stdio_write()`.
* In those cases, just defer to `printf()`.

View File

@ -43,6 +43,7 @@
#include <stddef.h>
#include <stdint.h>
#include <time.h>
#include <unistd.h>
#ifdef __cplusplus
@ -404,6 +405,25 @@ size_t fmt_str(char *out, const char *str);
*/
size_t fmt_to_lower(char *out, const char *str);
/**
* @brief Format a time structure to an ISO 8601 string
*
* This function does only take care of format validity
* and not of date/time validity.
*
* @param[out] out Pointer to output buffer
* @param[in] tm Pointer to time structure
* @param[in] separator Date and time separator.
* Must be 'T' for ISO 8601 or may be ' '
*
* @return nr of characters written to (or needed in) @p out
*
* @retval -EINVAL if @p tm is specifying a number for
* year, month, day, hour, minute which would yield
* to an invalid date/time format
*/
int fmt_time_tm_iso8601(char out[20], const struct tm *tm, char separator);
/**
* @brief Convert string of decimal digits to uint32
*
@ -455,6 +475,28 @@ uint32_t scn_u32_hex(const char *str, size_t n);
*/
ssize_t scn_buf_hex(void *dest, size_t dest_len, const char *hex, size_t hex_len);
/**
* @brief Convert an ISO 8601 string to time structure
*
* This function parses a string in the format
* YYYY-MM-DD\<separator\>HH:MM:SS or YYYY-MM-DD.
*
* A terminating '\0' is not required.
*
* This function does only take care of format validity
* and not of date/time validity.
*
* @param[out] tm Pointer to time structure
* @param[in] str Pointer to string to read from
* @param[in] separator Date and time separator
*
* @return Number of characters read from @p str on success (19 or 10)
*
* @retval -EINVAL if @p str has an invalid format
* @retval -EBADF if the expected separator does not match
*/
int scn_time_tm_iso8601(struct tm *tm, const char *str, char separator);
/**
* @brief Print string to stdout
*