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:
parent
3c649239b6
commit
0f81f50e3a
@ -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()`.
|
||||
|
||||
@ -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
|
||||
*
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user