tree data UPDATE portable timezone fix
diff --git a/CMakeModules/UseCompat.cmake b/CMakeModules/UseCompat.cmake
index 4cc1873..c8daa2d 100644
--- a/CMakeModules/UseCompat.cmake
+++ b/CMakeModules/UseCompat.cmake
@@ -48,10 +48,6 @@
 
     check_include_file("stdatomic.h" HAVE_STDATOMIC)
 
-    include(CheckStructHasMember)
-    check_struct_has_member("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF)
-    check_symbol_exists(timezone time.h HAVE_TIME_H_TIMEZONE)
-
     check_symbol_exists(realpath "stdlib.h" HAVE_REALPATH)
     check_symbol_exists(localtime_r "time.h" HAVE_LOCALTIME_R)
     check_symbol_exists(gmtime_r "time.h" HAVE_GMTIME_R)
diff --git a/compat/compat.h.in b/compat/compat.h.in
index 6df7a7b..0fa2fe7 100644
--- a/compat/compat.h.in
+++ b/compat/compat.h.in
@@ -3,7 +3,7 @@
  * @author Michal Vasko <mvasko@cesnet.cz>
  * @brief compatibility functions header
  *
- * Copyright (c) 2021 CESNET, z.s.p.o.
+ * Copyright (c) 2021 - 2023 CESNET, z.s.p.o.
  *
  * This source code is licensed under BSD 3-Clause License (the "License").
  * You may not use this file except in compliance with the License.
@@ -65,8 +65,6 @@
 #cmakedefine HAVE_STRCHRNUL
 #cmakedefine HAVE_GET_CURRENT_DIR_NAME
 #cmakedefine HAVE_PTHREAD_MUTEX_TIMEDLOCK
-#cmakedefine HAVE_TM_GMTOFF
-#cmakedefine HAVE_TIME_H_TIMEZONE
 #cmakedefine HAVE_REALPATH
 #cmakedefine HAVE_LOCALTIME_R
 #cmakedefine HAVE_GMTIME_R
@@ -189,6 +187,10 @@
 struct tm *localtime_r(const time_t *timep, struct tm *result);
 #endif
 
+#ifndef HAVE_GMTIME_R
+struct tm *gmtime_r(const time_t *timep, struct tm *result);
+#endif
+
 #ifndef HAVE_TIMEGM
 # if defined (_WIN32)
 #  define timegm _mkgmtime
diff --git a/src/plugins_types/date_and_time.c b/src/plugins_types/date_and_time.c
index 5ccb86d..65132c0 100644
--- a/src/plugins_types/date_and_time.c
+++ b/src/plugins_types/date_and_time.c
@@ -115,11 +115,9 @@
     ret = ly_time_str2time(value, &val->time, &val->fractions_s);
     LY_CHECK_GOTO(ret, cleanup);
 
-#ifdef HAVE_TIME_H_TIMEZONE
     if (!strncmp(((char *)value + value_len) - 6, "-00:00", 6)) {
         /* unknown timezone, move the timestamp to UTC */
-        tzset();
-        val->time += (time_t)timezone;
+        val->time -= ly_time_tz_offset();
         val->unknown_tz = 1;
 
         /* DST may apply, adjust accordingly */
@@ -135,10 +133,6 @@
             val->time -= 3600;
         }
     }
-#else
-    (void)tm;
-    val->unknown_tz = 1;
-#endif
 
     if (format == LY_VALUE_CANON) {
         /* store canonical value */
diff --git a/src/tree_data.h b/src/tree_data.h
index ff98f60..0531f46 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -2557,6 +2557,13 @@
 LIBYANG_API_DECL LY_ERR lyd_find_target(const struct ly_path *path, const struct lyd_node *tree, struct lyd_node **match);
 
 /**
+ * @brief Get current timezone UTC (GMT) time offset in seconds.
+ *
+ * @return Timezone shift in seconds.
+ */
+LIBYANG_API_DECL int ly_time_tz_offset(void);
+
+/**
  * @brief Convert date-and-time from string to UNIX timestamp and fractions of a second.
  *
  * @param[in] value Valid string date-and-time value.
diff --git a/src/tree_data_common.c b/src/tree_data_common.c
index 5af3c6d..d06d0e8 100644
--- a/src/tree_data_common.c
+++ b/src/tree_data_common.c
@@ -20,6 +20,7 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/time.h>
 #include <time.h>
 
 #include "common.h"
@@ -1508,6 +1509,32 @@
     return NULL;
 }
 
+LIBYANG_API_DEF int
+ly_time_tz_offset(void)
+{
+    const time_t epoch_plus_11h = 60 * 60 * 11;
+    struct tm tm_local, tm_utc;
+    int result = 0;
+
+    /* init timezone */
+    tzset();
+
+    /* get local and UTC time */
+    localtime_r(&epoch_plus_11h, &tm_local);
+    gmtime_r(&epoch_plus_11h, &tm_utc);
+
+    /* hours shift in seconds */
+    result += (tm_local.tm_hour - tm_utc.tm_hour) * 3600;
+
+    /* minutes shift in seconds */
+    result += (tm_local.tm_min - tm_utc.tm_min) * 60;
+
+    /* seconds shift */
+    result += tm_local.tm_sec - tm_utc.tm_sec;
+
+    return result;
+}
+
 LIBYANG_API_DEF LY_ERR
 ly_time_str2time(const char *value, time_t *time, char **fractions_s)
 {
@@ -1575,41 +1602,24 @@
 ly_time_time2str(time_t time, const char *fractions_s, char **str)
 {
     struct tm tm;
-    char zoneshift[8];
-    int32_t zonediff_h, zonediff_m;
+    char zoneshift[12];
+    int zonediff_s, zonediff_h, zonediff_m;
 
     LY_CHECK_ARG_RET(NULL, str, LY_EINVAL);
 
-    /* initialize the local timezone */
+    /* init timezone */
     tzset();
 
-#ifdef HAVE_TM_GMTOFF
     /* 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;
-    }
+    zonediff_s = ly_time_tz_offset();
+    zonediff_h = zonediff_s / 60 / 60;
+    zonediff_m = zonediff_s / 60 % 60;
     sprintf(zoneshift, "%+03d:%02d", zonediff_h, zonediff_m);
-#else
-    /* convert */
-    if (!gmtime_r(&time, &tm)) {
-        return LY_ESYS;
-    }
-
-    (void)zonediff_h;
-    (void)zonediff_m;
-    sprintf(zoneshift, "-00:00");
-#endif
 
     /* print */
     if (asprintf(str, "%04d-%02d-%02dT%02d:%02d:%02d%s%s%s",
diff --git a/tests/utests/types/yang_types.c b/tests/utests/types/yang_types.c
index 7bcb5e6..68e1ec0 100644
--- a/tests/utests/types/yang_types.c
+++ b/tests/utests/types/yang_types.c
@@ -91,7 +91,6 @@
     UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
 
     /* date-and-time */
-#if defined (HAVE_TM_GMTOFF) && defined (HAVE_TIME_H_TIMEZONE)
     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");
@@ -105,23 +104,6 @@
 
     /* fractional hours */
     TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888+04:30", STRING, "2005-05-25T16:45:15.88888-02:00");
-#else
-    /* Tests run with a TZ offset of +02:00, but this platform cannot represent that in time_t,
-     * so libyang always returns unspecified TZ. */
-    TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888Z", STRING, "2005-05-25T23:15:15.88888-00:00");
-    TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-08:59", STRING, "2005-06-01T08:14:15-00:00");
-    TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-23:00", STRING, "2005-06-01T22:15:15-00: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:59-02:00", STRING, "1970-01-01T02:59:59-00:00");
-    TEST_SUCCESS_XML("a", "l", "1969-12-31T23:59:59-02:00", STRING, "1970-01-01T01:59:59-00:00");
-
-    /* canonize */
-    TEST_SUCCESS_XML("a", "l", "2005-02-29T23:15:15-02:00", STRING, "2005-03-02T01:15:15-00:00");
-
-    /* fractional hours */
-    TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888+04:30", STRING, "2005-05-25T18:45:15.88888-00:00");
-#endif
 
     /* unknown timezone -- timezone conversion MUST NOT happen */
     TEST_SUCCESS_XML("a", "l", "2017-02-01T00:00:00-00:00", STRING, "2017-02-01T00:00:00-00:00");