data tree FEATURE public functions for working with date-and-time
diff --git a/src/plugins_types/date_and_time.c b/src/plugins_types/date_and_time.c
index 6611346..0d52dbb 100644
--- a/src/plugins_types/date_and_time.c
+++ b/src/plugins_types/date_and_time.c
@@ -44,126 +44,6 @@
static void lyplg_type_free_date_and_time(const struct ly_ctx *ctx, struct lyd_value *value);
/**
- * @brief Convert date-and-time from string to UNIX timestamp and fractions of a second.
- *
- * @param[in] value Valid string value.
- * @param[out] time UNIX timestamp.
- * @param[out] fractions_s Fractions of a second, set to NULL if none.
- * @return LY_ERR value.
- */
-static LY_ERR
-dat_str2time(const char *value, time_t *time, char **fractions_s)
-{
- struct tm tm = {0};
- uint32_t i, frac_len;
- const char *frac;
- int64_t shift, shift_m;
- time_t t;
-
- tm.tm_year = atoi(&value[0]) - 1900;
- tm.tm_mon = atoi(&value[5]) - 1;
- tm.tm_mday = atoi(&value[8]);
- tm.tm_hour = atoi(&value[11]);
- tm.tm_min = atoi(&value[14]);
- tm.tm_sec = atoi(&value[17]);
-
- t = timegm(&tm);
- i = 19;
-
- /* fractions of a second */
- if (value[i] == '.') {
- ++i;
- frac = &value[i];
- for (frac_len = 0; isdigit(frac[frac_len]); ++frac_len) {}
-
- i += frac_len;
-
- /* skip trailing zeros */
- for ( ; frac_len && (frac[frac_len - 1] == '0'); --frac_len) {}
-
- if (!frac_len) {
- /* only zeros, ignore */
- frac = NULL;
- }
- } else {
- frac = NULL;
- }
-
- /* apply offset */
- if ((value[i] == 'Z') || (value[i] == 'z')) {
- /* zero shift */
- shift = 0;
- } else {
- shift = strtol(&value[i], NULL, 10);
- shift = shift * 60 * 60; /* convert from hours to seconds */
- shift_m = strtol(&value[i + 4], NULL, 10) * 60; /* includes conversion from minutes to seconds */
- /* correct sign */
- if (shift < 0) {
- shift_m *= -1;
- }
- /* connect hours and minutes of the shift */
- shift = shift + shift_m;
- }
-
- /* we have to shift to the opposite way to correct the time */
- t -= shift;
-
- *time = t;
- if (frac) {
- *fractions_s = strndup(frac, frac_len);
- LY_CHECK_RET(!*fractions_s, LY_EMEM);
- } else {
- *fractions_s = NULL;
- }
- return LY_SUCCESS;
-}
-
-/**
- * @brief Convert UNIX timestamp and fractions of a second into canonical date-and-time string value.
- *
- * @param[in] time UNIX timestamp.
- * @param[in] fractions_s Fractions of a second, if any.
- * @param[out] str Canonical string value.
- * @return LY_ERR value.
- */
-static LY_ERR
-dat_time2str(time_t time, const char *fractions_s, char **str)
-{
- struct tm tm;
- char zoneshift[7];
- int32_t zonediff_h, zonediff_m;
-
- /* initialize the local timezone */
- tzset();
-
- /* convert */
- if (!localtime_r(&time, &tm)) {
- return LY_ESYS;
- }
-
- /* get timezone offset */
- if (tm.tm_gmtoff == 0) {
- /* time is Zulu (UTC) */
- zonediff_h = 0;
- zonediff_m = 0;
- } else {
- /* timezone offset */
- zonediff_h = tm.tm_gmtoff / 60 / 60;
- zonediff_m = tm.tm_gmtoff / 60 % 60;
- }
- sprintf(zoneshift, "%+03d:%02d", zonediff_h, zonediff_m);
-
- /* print */
- if (asprintf(str, "%04d-%02d-%02dT%02d:%02d:%02d%s%s%s",
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
- fractions_s ? "." : "", fractions_s ? fractions_s : "", zoneshift) == -1) {
- return LY_EMEM;
- }
-
- return LY_SUCCESS;
-}
-
-/**
* @brief Implementation of ::lyplg_type_store_clb for ietf-yang-types date-and-time type.
*/
static LY_ERR
@@ -228,7 +108,7 @@
LY_CHECK_GOTO(ret, cleanup);
/* pattern validation succeeded, convert to UNIX time and fractions of second */
- ret = dat_str2time(value, &val->time, &val->fractions_s);
+ ret = ly_time_str2time(value, &val->time, &val->fractions_s);
LY_CHECK_GOTO(ret, cleanup);
if (format == LY_VALUE_CANON) {
@@ -318,7 +198,7 @@
/* generate canonical value if not already */
if (!value->_canonical) {
/* get the canonical value */
- if (dat_time2str(val->time, val->fractions_s, &ret)) {
+ if (ly_time_time2str(val->time, val->fractions_s, &ret)) {
return NULL;
}
diff --git a/src/tree_data.h b/src/tree_data.h
index e742b02..89aad8a 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -40,6 +40,7 @@
struct lyd_node;
struct lyd_node_opaq;
struct lyd_node_term;
+struct timespec;
/**
* @page howtoData Data Instances
@@ -2302,6 +2303,44 @@
*/
LY_ERR lyd_find_path(const struct lyd_node *ctx_node, const char *path, ly_bool output, struct lyd_node **match);
+/**
+ * @brief Convert date-and-time from string to UNIX timestamp and fractions of a second.
+ *
+ * @param[in] value Valid string date-and-time value.
+ * @param[out] time UNIX timestamp.
+ * @param[out] fractions_s Optional fractions of a second, set to NULL if none.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_time_str2time(const char *value, time_t *time, char **fractions_s);
+
+/**
+ * @brief Convert UNIX timestamp and fractions of a second into canonical date-and-time string value.
+ *
+ * @param[in] time UNIX timestamp.
+ * @param[in] fractions_s Fractions of a second, if any.
+ * @param[out] str String date-and-time value.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_time_time2str(time_t time, const char *fractions_s, char **str);
+
+/**
+ * @brief Convert date-and-time from string to timespec.
+ *
+ * @param[in] value Valid string date-and-time value.
+ * @param[out] ts Timespec.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_time_str2ts(const char *value, struct timespec *ts);
+
+/**
+ * @brief Convert timespec into date-and-time string value.
+ *
+ * @param[in] ts Timespec.
+ * @param[out] str String date-and-time value.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_time_ts2str(const struct timespec *ts, char **str);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index bb4b734..f26749b 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -11,12 +11,16 @@
*
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _POSIX_C_SOURCE 200809L /* strdup, strndup */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+#include <sys/cdefs.h>
#include <assert.h>
+#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include "common.h"
#include "compat.h"
@@ -704,3 +708,153 @@
return NULL;
}
+
+API LY_ERR
+ly_time_str2time(const char *value, time_t *time, char **fractions_s)
+{
+ struct tm tm = {0};
+ uint32_t i, frac_len;
+ const char *frac;
+ int64_t shift, shift_m;
+ time_t t;
+
+ LY_CHECK_ARG_RET(NULL, value, time, LY_EINVAL);
+
+ tm.tm_year = atoi(&value[0]) - 1900;
+ tm.tm_mon = atoi(&value[5]) - 1;
+ tm.tm_mday = atoi(&value[8]);
+ tm.tm_hour = atoi(&value[11]);
+ tm.tm_min = atoi(&value[14]);
+ tm.tm_sec = atoi(&value[17]);
+
+ t = timegm(&tm);
+ i = 19;
+
+ /* fractions of a second */
+ if (value[i] == '.') {
+ ++i;
+ frac = &value[i];
+ for (frac_len = 0; isdigit(frac[frac_len]); ++frac_len) {}
+
+ i += frac_len;
+
+ /* skip trailing zeros */
+ for ( ; frac_len && (frac[frac_len - 1] == '0'); --frac_len) {}
+
+ if (!frac_len) {
+ /* only zeros, ignore */
+ frac = NULL;
+ }
+ } else {
+ frac = NULL;
+ }
+
+ /* apply offset */
+ if ((value[i] == 'Z') || (value[i] == 'z')) {
+ /* zero shift */
+ shift = 0;
+ } else {
+ shift = strtol(&value[i], NULL, 10);
+ shift = shift * 60 * 60; /* convert from hours to seconds */
+ shift_m = strtol(&value[i + 4], NULL, 10) * 60; /* includes conversion from minutes to seconds */
+ /* correct sign */
+ if (shift < 0) {
+ shift_m *= -1;
+ }
+ /* connect hours and minutes of the shift */
+ shift = shift + shift_m;
+ }
+
+ /* we have to shift to the opposite way to correct the time */
+ t -= shift;
+
+ *time = t;
+ if (fractions_s) {
+ if (frac) {
+ *fractions_s = strndup(frac, frac_len);
+ LY_CHECK_RET(!*fractions_s, LY_EMEM);
+ } else {
+ *fractions_s = NULL;
+ }
+ }
+ return LY_SUCCESS;
+}
+
+API LY_ERR
+ly_time_time2str(time_t time, const char *fractions_s, char **str)
+{
+ struct tm tm;
+ char zoneshift[7];
+ int32_t zonediff_h, zonediff_m;
+
+ LY_CHECK_ARG_RET(NULL, str, LY_EINVAL);
+
+ /* initialize the local timezone */
+ tzset();
+
+ /* convert */
+ if (!localtime_r(&time, &tm)) {
+ return LY_ESYS;
+ }
+
+ /* get timezone offset */
+ if (tm.tm_gmtoff == 0) {
+ /* time is Zulu (UTC) */
+ zonediff_h = 0;
+ zonediff_m = 0;
+ } else {
+ /* timezone offset */
+ zonediff_h = tm.tm_gmtoff / 60 / 60;
+ zonediff_m = tm.tm_gmtoff / 60 % 60;
+ }
+ sprintf(zoneshift, "%+03d:%02d", zonediff_h, zonediff_m);
+
+ /* print */
+ if (asprintf(str, "%04d-%02d-%02dT%02d:%02d:%02d%s%s%s",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
+ fractions_s ? "." : "", fractions_s ? fractions_s : "", zoneshift) == -1) {
+ return LY_EMEM;
+ }
+
+ return LY_SUCCESS;
+}
+
+API LY_ERR
+ly_time_str2ts(const char *value, struct timespec *ts)
+{
+ LY_ERR rc;
+ char *fractions_s, frac_buf[10] = {'0'};
+ int frac_len;
+
+ LY_CHECK_ARG_RET(NULL, value, ts, LY_EINVAL);
+
+ rc = ly_time_str2time(value, &ts->tv_sec, &fractions_s);
+ LY_CHECK_RET(rc);
+
+ /* convert fractions of a second to nanoseconds */
+ if (fractions_s) {
+ frac_len = strlen(fractions_s);
+ memcpy(frac_buf, fractions_s, frac_len > 9 ? 9 : frac_len);
+ ts->tv_nsec = atol(frac_buf);
+ free(fractions_s);
+ } else {
+ ts->tv_nsec = 0;
+ }
+
+ return LY_SUCCESS;
+}
+
+API LY_ERR
+ly_time_ts2str(const struct timespec *ts, char **str)
+{
+ char frac_buf[10];
+
+ LY_CHECK_ARG_RET(NULL, ts, str, LY_EINVAL);
+
+ /* convert nanoseconds to fractions of a second */
+ if (ts->tv_nsec) {
+ sprintf(frac_buf, "%09ld", ts->tv_nsec);
+ }
+
+ return ly_time_time2str(ts->tv_sec, ts->tv_nsec ? frac_buf : NULL, str);
+}