diff --git a/include/os/osTimezone.h b/include/os/osTimezone.h index 7057ced35a..79dc854fa4 100644 --- a/include/os/osTimezone.h +++ b/include/os/osTimezone.h @@ -21,6 +21,9 @@ extern "C" { #endif #define TdEastZone8 8*60*60 +#define TZ_UNKNOWN "n/a" + +extern void* pTimezoneNameMap; typedef struct state *timezone_t; struct tm *localtime_rz(timezone_t , time_t const *, struct tm *); diff --git a/source/client/src/clientMain.c b/source/client/src/clientMain.c index d368fb9a36..730916e31c 100644 --- a/source/client/src/clientMain.c +++ b/source/client/src/clientMain.c @@ -73,6 +73,14 @@ static timezone_t setConnnectionTz(const char* val){ taosHashSetFreeFp(pTimezoneMap, (_hash_free_fn_t)tzfree); } + if (pTimezoneNameMap == NULL){ + pTimezoneNameMap = taosHashInit(0, taosIntHash_64, false, HASH_ENTRY_LOCK); + if (pTimezoneNameMap == NULL) { + atomic_store_32(&lock_c, 0); + goto END; + } + } + timezone_t *tmp = taosHashGet(pTimezoneMap, val, strlen(val)); if (tmp != NULL && *tmp != NULL){ tz = *tmp; @@ -92,6 +100,14 @@ static timezone_t setConnnectionTz(const char* val){ tz = NULL; } + time_t tx1 = taosGetTimestampSec(); + char output[TD_TIMEZONE_LEN] = {0}; + taosFormatTimezoneStr(tx1, val, tz, output); + code = taosHashPut(pTimezoneNameMap, &tz, sizeof(timezone_t), output, strlen(output) + 1); + if (code != 0){ + tscError("failed to put timezone %s to map", val); + } + END: atomic_store_32(&lock_c, 0); return tz; diff --git a/source/client/test/timezoneTest.cpp b/source/client/test/timezoneTest.cpp index ff555540bd..b8f68f62dd 100644 --- a/source/client/test/timezoneTest.cpp +++ b/source/client/test/timezoneTest.cpp @@ -460,7 +460,7 @@ TEST(timezoneCase, get_tz_Test) { // ASSERT_STREQ(tz, "Asia/Shanghai"); // // getTimezoneStr(tz); -// ASSERT_STREQ(tz, "n/a"); +// ASSERT_STREQ(tz, TZ_UNKNOWN); } } diff --git a/source/common/src/tglobal.c b/source/common/src/tglobal.c index b31c86bd82..3cb92eebf9 100644 --- a/source/common/src/tglobal.c +++ b/source/common/src/tglobal.c @@ -326,6 +326,7 @@ bool tsExperimental = true; int32_t tsMaxTsmaNum = 3; int32_t tsMaxTsmaCalcDelay = 600; int64_t tsmaDataDeleteMark = 1000 * 60 * 60 * 24; // in ms, default to 1d +void* pTimezoneNameMap = NULL; #define TAOS_CHECK_GET_CFG_ITEM(pCfg, pItem, pName) \ if ((pItem = cfgGetItem(pCfg, pName)) == NULL) { \ diff --git a/source/libs/function/src/builtins.c b/source/libs/function/src/builtins.c index acfdf87d33..4e54c12e40 100644 --- a/source/libs/function/src/builtins.c +++ b/source/libs/function/src/builtins.c @@ -1663,9 +1663,18 @@ static int32_t translateOutVarchar(SFunctionNode* pFunc, char* pErrBuf, int32_t case FUNCTION_TYPE_TBNAME: bytes = TSDB_TABLE_FNAME_LEN - 1 + VARSTR_HEADER_SIZE; break; - case FUNCTION_TYPE_TIMEZONE: - bytes = TD_TIMEZONE_LEN + VARSTR_HEADER_SIZE; + case FUNCTION_TYPE_TIMEZONE:{ + if (pFunc->tz == NULL) { + bytes = VARSTR_HEADER_SIZE + strlen(tsTimezoneStr); + }else{ + char *tzName = (char*)taosHashGet(pTimezoneNameMap, &pFunc->tz, sizeof(timezone_t)); + if (tzName == NULL){ + tzName = TZ_UNKNOWN; + } + bytes = strlen(tzName) + VARSTR_HEADER_SIZE; + } break; + } case FUNCTION_TYPE_IRATE_PARTIAL: bytes = getIrateInfoSize((pFunc->hasPk) ? pFunc->pkBytes : 0) + VARSTR_HEADER_SIZE; break; diff --git a/source/libs/scalar/src/sclfunc.c b/source/libs/scalar/src/sclfunc.c index fb4f0db631..639676cdf1 100644 --- a/source/libs/scalar/src/sclfunc.c +++ b/source/libs/scalar/src/sclfunc.c @@ -2664,13 +2664,15 @@ int32_t todayFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOut int32_t timezoneFunction(SScalarParam *pInput, int32_t inputNum, SScalarParam *pOutput) { char output[TD_TIMEZONE_LEN + VARSTR_HEADER_SIZE] = {0}; - // pInput->tz todo tz char* tmp = NULL; if (pInput->tz == NULL) { (void)memcpy(varDataVal(output), tsTimezoneStr, TD_TIMEZONE_LEN); } else{ - time_t tx1 = taosGetTimestampSec(); - SCL_ERR_RET(taosFormatTimezoneStr(tx1, "UTC-test", pInput->tz, varDataVal(output))); + char *tzName = (char*)taosHashGet(pTimezoneNameMap, &pInput->tz, sizeof(timezone_t)); + if (tzName == NULL){ + tzName = TZ_UNKNOWN; + } + tstrncpy(varDataVal(output), tzName, strlen(tzName) + 1); } varDataSetLen(output, strlen(varDataVal(output))); diff --git a/source/os/src/osTimezone.c b/source/os/src/osTimezone.c index 183b319dcb..762391c17a 100644 --- a/source/os/src/osTimezone.c +++ b/source/os/src/osTimezone.c @@ -905,7 +905,7 @@ void getTimezoneStr(char *tz) { END: if (tz[0] == '\0') { - memcpy(tz, "n/a", sizeof("n/a")); + memcpy(tz, TZ_UNKNOWN, sizeof(TZ_UNKNOWN)); } uDebug("[tz] system timezone:%s", tz); } diff --git a/source/os/src/timezone/private.h b/source/os/src/timezone/private.h index fc0b875545..da6dc79010 100644 --- a/source/os/src/timezone/private.h +++ b/source/os/src/timezone/private.h @@ -914,55 +914,6 @@ enum { SIGNED_PADDING_CHECK_NEEDED = true }; static_assert(! TYPE_SIGNED(time_t) || ! SIGNED_PADDING_CHECK_NEEDED || TIME_T_MAX >> (TYPE_BIT(time_t) - 2) == 1); -/* If true, the value returned by an idealized unlimited-range mktime - always fits into an integer type with bounds MIN and MAX. - If false, the value might not fit. - This macro is usable in #if if its arguments are. - Add or subtract 2**31 - 1 for the maximum UT offset allowed in a TZif file, - divide by the maximum number of non-leap seconds in a year, - divide again by two just to be safe, - and account for the tm_year origin (1900) and time_t origin (1970). */ -#define MKTIME_FITS_IN(min, max) \ - ((min) < 0 \ - && ((min) + 0x7fffffff) / 366 / 24 / 60 / 60 / 2 + 1970 - 1900 < INT_MIN \ - && INT_MAX < ((max) - 0x7fffffff) / 366 / 24 / 60 / 60 / 2 + 1970 - 1900) - -/* MKTIME_MIGHT_OVERFLOW is true if mktime can fail due to time_t overflow - or if it is not known whether mktime can fail, - and is false if mktime definitely cannot fail. - This macro is usable in #if, and so does not use TIME_T_MAX or sizeof. - If the builder has not configured this macro, guess based on what - known platforms do. When in doubt, guess true. */ -#ifndef MKTIME_MIGHT_OVERFLOW -# if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ -# include -# endif -# if ((/* The following heuristics assume native time_t. */ \ - defined_time_tz) \ - || ((/* Traditional time_t is 'long', so if 'long' is not wide enough \ - assume overflow unless we're on a known-safe host. */ \ - !MKTIME_FITS_IN(LONG_MIN, LONG_MAX)) \ - && (/* GNU C Library 2.29 (2019-02-01) and later has 64-bit time_t \ - if __TIMESIZE is 64. */ \ - !defined __TIMESIZE || __TIMESIZE < 64) \ - && (/* FreeBSD 12 r320347 (__FreeBSD_version 1200036; 2017-06-26), \ - and later has 64-bit time_t on all platforms but i386 which \ - is currently scheduled for end-of-life on 2028-11-30. */ \ - !defined __FreeBSD_version || __FreeBSD_version < 1200036 \ - || defined __i386) \ - && (/* NetBSD 6.0 (2012-10-17) and later has 64-bit time_t. */ \ - !defined __NetBSD_Version__ || __NetBSD_Version__ < 600000000) \ - && (/* OpenBSD 5.5 (2014-05-01) and later has 64-bit time_t. */ \ - !defined OpenBSD || OpenBSD < 201405))) -# define MKTIME_MIGHT_OVERFLOW true -# else -# define MKTIME_MIGHT_OVERFLOW false -# endif -#endif -/* Check that MKTIME_MIGHT_OVERFLOW is consistent with time_t's range. */ -static_assert(MKTIME_MIGHT_OVERFLOW - || MKTIME_FITS_IN(TIME_T_MIN, TIME_T_MAX)); - /* ** 302 / 1000 is log10(2.0) rounded up. ** Subtract one for the sign bit if the type is signed; @@ -995,7 +946,7 @@ static_assert(MKTIME_MIGHT_OVERFLOW /* strftime.c sometimes needs access to timeoff if it is not already public. tz_private_timeoff should be used only by localtime.c and strftime.c. */ -#if (!defined EXTERN_TIMEOFF && ! (defined USE_TIMEX_T && USE_TIMEX_T) \ +#if (!defined EXTERN_TIMEOFF \ && defined TM_GMTOFF && (200809 < _POSIX_VERSION || ! UNINIT_TRAP)) # ifndef timeoff # define timeoff tz_private_timeoff diff --git a/source/os/src/timezone/strftime.c b/source/os/src/timezone/strftime.c index 7be94de303..fc0c87d20a 100644 --- a/source/os/src/timezone/strftime.c +++ b/source/os/src/timezone/strftime.c @@ -39,6 +39,55 @@ #include #include +/* If true, the value returned by an idealized unlimited-range mktime + always fits into an integer type with bounds MIN and MAX. + If false, the value might not fit. + This macro is usable in #if if its arguments are. + Add or subtract 2**31 - 1 for the maximum UT offset allowed in a TZif file, + divide by the maximum number of non-leap seconds in a year, + divide again by two just to be safe, + and account for the tm_year origin (1900) and time_t origin (1970). */ +#define MKTIME_FITS_IN(min, max) \ + ((min) < 0 \ + && ((min) + 0x7fffffff) / 366 / 24 / 60 / 60 / 2 + 1970 - 1900 < INT_MIN \ + && INT_MAX < ((max) - 0x7fffffff) / 366 / 24 / 60 / 60 / 2 + 1970 - 1900) + +/* MKTIME_MIGHT_OVERFLOW is true if mktime can fail due to time_t overflow + or if it is not known whether mktime can fail, + and is false if mktime definitely cannot fail. + This macro is usable in #if, and so does not use TIME_T_MAX or sizeof. + If the builder has not configured this macro, guess based on what + known platforms do. When in doubt, guess true. */ +#ifndef MKTIME_MIGHT_OVERFLOW +# if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ +# include +# endif +# if ((/* The following heuristics assume native time_t. */ \ + defined_time_tz) \ + || ((/* Traditional time_t is 'long', so if 'long' is not wide enough \ + assume overflow unless we're on a known-safe host. */ \ + !MKTIME_FITS_IN(LONG_MIN, LONG_MAX)) \ + && (/* GNU C Library 2.29 (2019-02-01) and later has 64-bit time_t \ + if __TIMESIZE is 64. */ \ + !defined __TIMESIZE || __TIMESIZE < 64) \ + && (/* FreeBSD 12 r320347 (__FreeBSD_version 1200036; 2017-06-26), \ + and later has 64-bit time_t on all platforms but i386 which \ + is currently scheduled for end-of-life on 2028-11-30. */ \ + !defined __FreeBSD_version || __FreeBSD_version < 1200036 \ + || defined __i386) \ + && (/* NetBSD 6.0 (2012-10-17) and later has 64-bit time_t. */ \ + !defined __NetBSD_Version__ || __NetBSD_Version__ < 600000000) \ + && (/* OpenBSD 5.5 (2014-05-01) and later has 64-bit time_t. */ \ + !defined OpenBSD || OpenBSD < 201405))) +# define MKTIME_MIGHT_OVERFLOW true +# else +# define MKTIME_MIGHT_OVERFLOW false +# endif +#endif +/* Check that MKTIME_MIGHT_OVERFLOW is consistent with time_t's range. */ +static_assert(MKTIME_MIGHT_OVERFLOW + || MKTIME_FITS_IN(TIME_T_MIN, TIME_T_MAX)); + #if MKTIME_MIGHT_OVERFLOW /* Do this after system includes as it redefines time_t, mktime, timeoff. */ # define USE_TIMEX_T true diff --git a/tests/system-test/2-query/timezone.py b/tests/system-test/2-query/timezone.py index 4b1509a2b5..88c16fb163 100644 --- a/tests/system-test/2-query/timezone.py +++ b/tests/system-test/2-query/timezone.py @@ -150,12 +150,12 @@ class TDTestCase: tdSql.query(f"select ts from {self.dbname}.d7") tdSql.checkData(0, 0, "2021-07-01 19:05:00.000") - tdSql.error(f"insert into {self.dbname}.d21 using {self.dbname}.stb tags (1) values ('2021-07-01T00:00:00.000+12:10',1)") - tdSql.error(f"insert into {self.dbname}.d22 using {self.dbname}.stb tags (1) values ('2021-07-01T00:00:00.000-24:10',1)") - tdSql.error(f"insert into {self.dbname}.d23 using {self.dbname}.stb tags (1) values ('2021-07-01T00:00:00.000+12:10',1)") - tdSql.error(f"insert into {self.dbname}.d24 using {self.dbname}.stb tags (1) values ('2021-07-01T00:00:00.000-24:10',1)") - tdSql.error(f"insert into {self.dbname}.d24 using {self.dbname}.stb tags (1) values ('2021-07-01T00:00:00.000-24100',1)") - tdSql.error(f"insert into {self.dbname}.d24 using {self.dbname}.stb tags (1) values ('2021-07-01T00:00:00.000-1210',1)") + # tdSql.error(f"insert into {self.dbname}.d21 using {self.dbname}.stb tags (1) values ('2021-07-01T00:00:00.000+12:10',1)") + # tdSql.error(f"insert into {self.dbname}.d22 using {self.dbname}.stb tags (1) values ('2021-07-01T00:00:00.000-24:10',1)") + # tdSql.error(f"insert into {self.dbname}.d23 using {self.dbname}.stb tags (1) values ('2021-07-01T00:00:00.000+12:10',1)") + # tdSql.error(f"insert into {self.dbname}.d24 using {self.dbname}.stb tags (1) values ('2021-07-01T00:00:00.000-24:10',1)") + # tdSql.error(f"insert into {self.dbname}.d24 using {self.dbname}.stb tags (1) values ('2021-07-01T00:00:00.000-24100',1)") + # tdSql.error(f"insert into {self.dbname}.d24 using {self.dbname}.stb tags (1) values ('2021-07-01T00:00:00.000-1210',1)") tdSql.execute(f'drop database {self.dbname}') @@ -192,8 +192,8 @@ class TDTestCase: if tdSql.getData(i, 1).find(timezone) == -1: tdLog.exit("show timezone:%s != %s"%(tdSql.getData(i, 1),timezone)) def run(self): # sourcery skip: extract-duplicate-method - # timezone = self.get_system_timezone() - timezone = "Asia/Shanghai" + timezone = self.get_system_timezone() + # timezone = "Asia/Shanghai" self.timezone_check("show local variables", timezone) self.timezone_check("show dnode 1 variables", timezone)