plugins types FEATURE date-and-time LYB value support
diff --git a/src/plugins_types/date_and_time.c b/src/plugins_types/date_and_time.c
index 24b5034..ec8a25f 100644
--- a/src/plugins_types/date_and_time.c
+++ b/src/plugins_types/date_and_time.c
@@ -12,16 +12,19 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
+#define _GNU_SOURCE /* asprintf, strdup */
+#include <sys/cdefs.h>
#include "plugins_types.h"
#include <arpa/inet.h>
#include <assert.h>
+#include <ctype.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include "libyang.h"
@@ -29,228 +32,404 @@
#include "compat.h"
/**
- * @brief Convert string to a number.
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesDateAndTime date-and-time (ietf-yang-types)
*
- * @param[in,out] str String to convert, the parsed number are skipped.
- * @param[in] len Expected length of the number. 0 to parse at least 1 character.
- * @param[in] full_str Full string for error generation.
- * @param[out] num Converted number.
- * @param[out] err Error information on error.
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 8 | yes | `time_t *` | UNIX timestamp |
+ * | string length | no | `char *` | string with the fraction digits of a second |
+ */
+
+static void lyplg_type_free_date_and_time(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Stored value structure for date-and-time
+ */
+struct lyd_value_date_and_time {
+ time_t time; /**< UNIX timestamp */
+ char *fractions_s; /**< fractions of a second */
+};
+
+/**
+ * @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
-convert_number(const char **str, int len, const char *full_str, int *num, struct ly_err_item **err)
+dat_str2time(const char *value, time_t *time, char **fractions_s)
{
- char *ptr;
+ struct tm tm = {0};
+ uint32_t i, frac_len;
+ const char *frac;
+ int64_t shift, shift_m;
+ time_t t;
- *num = strtoul(*str, &ptr, 10);
- if ((len && (ptr - *str != len)) || (!len && (ptr == *str))) {
- return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid character '%c' in date-and-time value \"%s\", "
- "a digit expected.", ptr[0], full_str);
+ 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;
}
- *str = ptr;
+ /* 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 Check a single character.
+ * @brief Convert UNIX timestamp and fractions of a second into canonical date-and-time string value.
*
- * @param[in,out] str String to check, the parsed character is skipped.
- * @param[in] c Array of possible characters.
- * @param[in] full_str Full string for error generation.
- * @param[out] err Error information on error.
+ * @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
-check_char(const char **str, char c[], const char *full_str, struct ly_err_item **err)
+dat_time2str(time_t time, const char *fractions_s, char **str)
{
- LY_ERR ret;
- uint32_t i;
- char *exp_str;
+ struct tm tm;
+ char zoneshift[7];
+ int32_t zonediff_h, zonediff_m;
- for (i = 0; c[i]; ++i) {
- if ((*str)[0] == c[i]) {
- break;
- }
- }
- if (!c[i]) {
- /* "'c'" + (" or 'c'")* + \0 */
- exp_str = malloc(3 + (i - 1) * 7 + 1);
- sprintf(exp_str, "'%c'", c[0]);
- for (i = 1; c[i]; ++i) {
- sprintf(exp_str + strlen(exp_str), " or '%c'", c[i]);
- }
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid character '%c' in date-and-time value \"%s\", "
- "%s expected.", (*str)[0], full_str, exp_str);
- free(exp_str);
- return ret;
+ /* initialize the local timezone */
+ tzset();
+
+ /* convert */
+ if (!localtime_r(&time, &tm)) {
+ return LY_ESYS;
}
- ++(*str);
+ /* 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 Validate, canonize and store value of the ietf-yang-types date-and-time type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for ietf-yang-types date-and-time type.
*/
static LY_ERR
lyplg_type_store_date_and_time(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
- uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
- struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
{
LY_ERR ret = LY_SUCCESS;
- struct tm tm = {0}, tm2;
- int num;
- const char *val_str;
+ struct lysc_type_str *type_dat = (struct lysc_type_str *)type;
+ struct lyd_value_date_and_time *val;
+ uint32_t i;
+ char c;
- /* store as a string */
- ret = lyplg_type_store_string(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node,
- storage, unres, err);
- LY_CHECK_RET(ret);
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
- /* canonize */
- /* \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[\+\-]\d{2}:\d{2})
- * 2018-03-21T09:11:05(.55785...)(Z|+02:00) */
- val_str = storage->_canonical;
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len < 8) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB date-and-time value size %zu "
+ "(expected at least 8).", value_len);
+ goto cleanup;
+ }
+ for (i = 8; i < value_len; ++i) {
+ c = ((char *)value)[i];
+ if (!isdigit(c)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB date-and-time character '%c' "
+ "(expected a digit).", c);
+ goto cleanup;
+ }
+ }
- /* year */
- ret = convert_number(&val_str, 4, storage->_canonical, &tm.tm_year, err);
- LY_CHECK_GOTO(ret, cleanup);
- tm.tm_year -= 1900;
+ /* allocate the value */
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
- LY_CHECK_GOTO(ret = check_char(&val_str, "-", storage->_canonical, err), cleanup);
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = val;
+ storage->realtype = type;
- /* month */
- ret = convert_number(&val_str, 2, storage->_canonical, &tm.tm_mon, err);
- LY_CHECK_GOTO(ret, cleanup);
- tm.tm_mon -= 1;
+ /* store timestamp */
+ memcpy(&val->time, value, sizeof val->time);
- LY_CHECK_GOTO(ret = check_char(&val_str, "-", storage->_canonical, err), cleanup);
+ /* store fractions of second */
+ if (value_len > 8) {
+ val->fractions_s = strndup(((char *)value) + 8, value_len - 8);
+ LY_CHECK_ERR_GOTO(!val->fractions_s, ret = LY_EMEM, cleanup);
+ }
- /* day */
- ret = convert_number(&val_str, 2, storage->_canonical, &tm.tm_mday, err);
- LY_CHECK_GOTO(ret, cleanup);
-
- LY_CHECK_GOTO(ret = check_char(&val_str, "T", storage->_canonical, err), cleanup);
-
- /* hours */
- ret = convert_number(&val_str, 2, storage->_canonical, &tm.tm_hour, err);
- LY_CHECK_GOTO(ret, cleanup);
-
- LY_CHECK_GOTO(ret = check_char(&val_str, ":", storage->_canonical, err), cleanup);
-
- /* minutes */
- ret = convert_number(&val_str, 2, storage->_canonical, &tm.tm_min, err);
- LY_CHECK_GOTO(ret, cleanup);
-
- LY_CHECK_GOTO(ret = check_char(&val_str, ":", storage->_canonical, err), cleanup);
-
- /* seconds */
- ret = convert_number(&val_str, 2, storage->_canonical, &tm.tm_sec, err);
- LY_CHECK_GOTO(ret, cleanup);
-
- /* do not move the pointer */
- LY_CHECK_GOTO(ret = check_char(&val_str, ".Z+-", storage->_canonical, err), cleanup);
- --val_str;
-
- /* validate using mktime() */
- tm2 = tm;
- errno = 0;
- mktime(&tm);
- /* ENOENT is set when "/etc/localtime" is missing but the function suceeeds */
- if (errno && (errno != ENOENT)) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Checking date-and-time value \"%s\" failed (%s).",
- storage->_canonical, strerror(errno));
- goto cleanup;
- }
- /* we now have correctly filled the remaining values, use them */
- memcpy(((char *)&tm2) + (6 * sizeof(int)), ((char *)&tm) + (6 * sizeof(int)), sizeof(struct tm) - (6 * sizeof(int)));
- /* back it up again */
- tm = tm2;
- /* let mktime() correct date & time with having the other values correct now */
- errno = 0;
- mktime(&tm);
- if (errno && (errno != ENOENT)) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Checking date-and-time value \"%s\" failed (%s).",
- storage->_canonical, strerror(errno));
- goto cleanup;
- }
- /* detect changes in the filled values */
- if (memcmp(&tm, &tm2, 6 * sizeof(int))) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Checking date-and-time value \"%s\" failed, "
- "canonical date and time is \"%04d-%02d-%02dT%02d:%02d:%02d\".", storage->_canonical,
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ /* success */
goto cleanup;
}
- /* tenth of a second */
- if (val_str[0] == '.') {
- ++val_str;
- ret = convert_number(&val_str, 0, storage->_canonical, &num, err);
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction, there can be only ASCII chars */
+ if (type_dat->length) {
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_dat->length, value_len, value, value_len, err);
LY_CHECK_GOTO(ret, cleanup);
}
- switch (val_str[0]) {
- case 'Z':
- /* done */
- ++val_str;
- break;
- case '+':
- case '-':
- /* timezone shift */
- if ((val_str[1] < '0') || (val_str[1] > '2')) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".",
- val_str, storage->_canonical);
- goto cleanup;
- }
- if ((val_str[2] < '0') || ((val_str[1] == '2') && (val_str[2] > '3')) || (val_str[2] > '9')) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".",
- val_str, storage->_canonical);
- goto cleanup;
- }
+ /* date-and-time pattern */
+ ret = lyplg_type_validate_patterns(type_dat->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
- if (val_str[3] != ':') {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".",
- val_str, storage->_canonical);
- goto cleanup;
- }
+ /* allocate the value */
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
- if ((val_str[4] < '0') || (val_str[4] > '5')) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".",
- val_str, storage->_canonical);
- goto cleanup;
- }
- if ((val_str[5] < '0') || (val_str[5] > '9')) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".",
- val_str, storage->_canonical);
- goto cleanup;
- }
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = val;
+ storage->realtype = type;
- val_str += 6;
- break;
- default:
- LY_CHECK_GOTO(ret = check_char(&val_str, "Z+-", storage->_canonical, err), cleanup);
+ /* pattern validation succeeded, convert to UNIX time and fractions of second */
+ ret = dat_str2time(value, &val->time, &val->fractions_s);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (format == LY_VALUE_CANON) {
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
}
- /* no other characters expected */
- if (val_str[0]) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid character '%c' in date-and-time value \"%s\", "
- "no characters expected.", val_str[0], storage->_canonical);
- goto cleanup;
- }
-
- /* validation succeeded and we do not want to change how it is stored */
-
cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
if (ret) {
- type->plugin->free(ctx, storage);
+ lyplg_type_free_date_and_time(ctx, storage);
}
return ret;
}
/**
+ * @brief Implementation of ::lyplg_type_compare_clb for ietf-yang-types date-and-time type.
+ */
+static LY_ERR
+lyplg_type_compare_date_and_time(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_date_and_time *v1 = val1->ptr, *v2 = val2->ptr;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ /* compare timestamp */
+ if (v1->time != v2->time) {
+ return LY_ENOT;
+ }
+
+ /* compare second fractions */
+ if ((!v1->fractions_s && !v2->fractions_s) ||
+ (v1->fractions_s && v2->fractions_s && !strcmp(v1->fractions_s, v2->fractions_s))) {
+ return LY_SUCCESS;
+ }
+ return LY_ENOT;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for ietf-yang-types date-and-time type.
+ */
+static const void *
+lyplg_type_print_date_and_time(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ struct lyd_value_date_and_time *val = value->ptr;
+ char *ret;
+
+ if (format == LY_VALUE_LYB) {
+ if (val->fractions_s) {
+ ret = malloc(8 + strlen(val->fractions_s));
+ LY_CHECK_ERR_RET(!ret, LOGMEM(ctx), NULL);
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = 8 + strlen(val->fractions_s);
+ }
+ memcpy(ret, &val->time, sizeof val->time);
+ memcpy(ret + 8, val->fractions_s, strlen(val->fractions_s));
+ } else {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = 8;
+ }
+ ret = (char *)&val->time;
+ }
+ return ret;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* get the canonical value */
+ if (dat_time2str(val->time, val->fractions_s, &ret)) {
+ return NULL;
+ }
+
+ /* store it */
+ if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+ LOGMEM(ctx);
+ return NULL;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_hash_clb for ietf-yang-types date-and-time type.
+ */
+static const void *
+lyplg_type_hash_date_and_time(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+ struct lyd_value_date_and_time *val = value->ptr;
+
+ if (!val->fractions_s) {
+ /* we can use the timestamp */
+ *dynamic = 0;
+ *key_len = 8;
+ return &val->time;
+ }
+
+ /* simply use the (dynamic) LYB value */
+ return lyplg_type_print_date_and_time(NULL, value, LY_VALUE_LYB, NULL, dynamic, key_len);
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for ietf-yang-types date-and-time type.
+ */
+static LY_ERR
+lyplg_type_dup_date_and_time(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lyd_value_date_and_time *orig_val = original->ptr, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ /* optional canonical value */
+ ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ /* allocate value */
+ dup_val = calloc(1, sizeof *dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+ dup->ptr = dup_val;
+
+ /* copy timestamp */
+ dup_val->time = orig_val->time;
+
+ /* duplicate second fractions */
+ if (orig_val->fractions_s) {
+ dup_val->fractions_s = strdup(orig_val->fractions_s);
+ LY_CHECK_ERR_GOTO(!dup_val->fractions_s, ret = LY_EMEM, error);
+ }
+
+ dup->ptr = dup_val;
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_date_and_time(ctx, dup);
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for ietf-yang-types date-and-time type.
+ */
+static void
+lyplg_type_free_date_and_time(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_date_and_time *val = value->ptr;
+
+ lydict_remove(ctx, value->_canonical);
+ if (val) {
+ free(val->fractions_s);
+ free(val);
+ }
+}
+
+/**
* @brief Plugin information for date-and-time type implementation.
*
* Note that external plugins are supposed to use:
@@ -266,11 +445,11 @@
.plugin.id = "libyang 2 - date-and-time, version 1",
.plugin.store = lyplg_type_store_date_and_time,
.plugin.validate = NULL,
- .plugin.compare = lyplg_type_compare_simple,
- .plugin.print = lyplg_type_print_simple,
- .plugin.hash = lyplg_type_hash_simple,
- .plugin.duplicate = lyplg_type_dup_simple,
- .plugin.free = lyplg_type_free_simple
+ .plugin.compare = lyplg_type_compare_date_and_time,
+ .plugin.print = lyplg_type_print_date_and_time,
+ .plugin.hash = lyplg_type_hash_date_and_time,
+ .plugin.duplicate = lyplg_type_dup_date_and_time,
+ .plugin.free = lyplg_type_free_date_and_time
},
{0}
};
diff --git a/tests/utests/types/yang_types.c b/tests/utests/types/yang_types.c
index 491c22e..4e1aba1 100644
--- a/tests/utests/types/yang_types.c
+++ b/tests/utests/types/yang_types.c
@@ -16,7 +16,8 @@
#define _UTEST_MAIN_
#include "../utests.h"
-/* LOCAL INCLUDE HEADERS */
+#include <stdlib.h>
+
#include "libyang.h"
#define MODULE_CREATE_YIN(MOD_NAME, NODES) \
@@ -58,6 +59,39 @@
assert_null(tree); \
}
+static int
+setup(void **state)
+{
+ char *cur_tz;
+
+ *state = NULL;
+
+ /* backup timezone, if any */
+ cur_tz = getenv("TZ");
+ if (cur_tz) {
+ cur_tz = strdup(cur_tz);
+ *state = cur_tz;
+ }
+
+ /* set CET */
+ setenv("TZ", "CET+02:00", 1);
+
+ return 0;
+}
+
+static int
+teardown(void **state)
+{
+ char *prev_tz = *state;
+
+ if (prev_tz) {
+ /* restore TZ */
+ setenv("TZ", prev_tz, 1);
+ free(prev_tz);
+ }
+ return 0;
+}
+
static void
test_data_xml(void **state)
{
@@ -74,22 +108,21 @@
UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
/* date-and-time */
- TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888Z", STRING, "2005-05-25T23:15:15.88888Z");
- TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-08:59", STRING, "2005-05-31T23:15:15-08:59");
- TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-23:00", STRING, "2005-05-31T23:15:15-23:00");
+ TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888Z", STRING, "2005-05-25T21:15:15.88888-02:00");
+ TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-08:59", STRING, "2005-06-01T06:14:15-02:00");
+ TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-23:00", STRING, "2005-06-01T20:15:15-02:00");
/* test 1 second before epoch (mktime returns -1, but it is a correct value), with and without DST */
- TEST_SUCCESS_XML("a", "l", "1970-01-01T00:59:59Z", STRING, "1970-01-01T00:59:59Z");
- TEST_SUCCESS_XML("a", "l", "1969-12-31T23:59:59Z", STRING, "1969-12-31T23:59:59Z");
+ TEST_SUCCESS_XML("a", "l", "1970-01-01T00:59:59-02:00", STRING, "1970-01-01T00:59:59-02:00");
+ TEST_SUCCESS_XML("a", "l", "1969-12-31T23:59:59-02:00", STRING, "1969-12-31T23:59:59-02:00");
+
+ /* canonize */
+ TEST_SUCCESS_XML("a", "l", "2005-02-29T23:15:15-02:00", STRING, "2005-03-01T23:15:15-02:00");
TEST_ERROR_XML("a", "l", "2005-05-31T23:15:15.-08:00");
CHECK_LOG_CTX("Unsatisfied pattern - \"2005-05-31T23:15:15.-08:00\" does not conform to "
"\"\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[\\+\\-]\\d{2}:\\d{2})\".",
"Schema location /a:l, line number 1.");
- TEST_ERROR_XML("a", "l", "2005-02-29T23:15:15-08:00");
- CHECK_LOG_CTX("Checking date-and-time value \"2005-02-29T23:15:15-08:00\" failed, "
- "canonical date and time is \"2005-03-01T23:15:15\".",
- "Schema location /a:l, line number 1.");
/* phys-address */
TEST_SUCCESS_XML("a", "l2", "aa:bb:cc:dd", STRING, "aa:bb:cc:dd");
@@ -167,5 +200,5 @@
UTEST(test_print),
};
- return cmocka_run_group_tests(tests, NULL, NULL);
+ return cmocka_run_group_tests(tests, setup, teardown);
}