From 2c3c01cfff5216463d09d76240304c611f06e3d2 Mon Sep 17 00:00:00 2001 From: wangmm0220 Date: Wed, 27 Nov 2024 22:57:48 +0800 Subject: [PATCH] feat:[TD-32642] add charset for connection support --- contrib/CMakeLists.txt | 2 +- include/os/osString.h | 2 +- source/client/test/CMakeLists.txt | 21 +- source/client/test/clientTests.cpp | 525 -------------------------- source/client/test/timezoneTest.cpp | 563 ++++++++++++++++++++++++++++ source/os/src/timezone/localtime.c | 2 +- source/os/src/timezone/private.h | 2 +- 7 files changed, 586 insertions(+), 531 deletions(-) create mode 100644 source/client/test/timezoneTest.cpp diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index c368a92429..6253d258a8 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -654,7 +654,7 @@ if(${TD_LINUX} AND ${BUILD_WITH_S3}) endif() execute_process( - COMMAND make TZDIR=${TZ_OUTPUT_PATH}/ clean all posix_only + COMMAND make TZDIR=${TZ_OUTPUT_PATH}/ clean zic posix_only WORKING_DIRECTORY "${TD_CONTRIB_DIR}/tz" ) diff --git a/include/os/osString.h b/include/os/osString.h index 810c59131e..1fe83b7e3b 100644 --- a/include/os/osString.h +++ b/include/os/osString.h @@ -22,7 +22,7 @@ extern "C" { typedef wchar_t TdWchar; typedef int32_t TdUcs4; -#if !defined(DISALLOW_NCHAR_WITHOUT_ICONV) && defined(DARWIN) +#if !defined(DISALLOW_NCHAR_WITHOUT_ICONV) #include "iconv.h" #else typedef void *iconv_t; diff --git a/source/client/test/CMakeLists.txt b/source/client/test/CMakeLists.txt index 8f0bc81d6b..f869bedef6 100644 --- a/source/client/test/CMakeLists.txt +++ b/source/client/test/CMakeLists.txt @@ -11,6 +11,12 @@ TARGET_LINK_LIBRARIES( os util common transport parser catalog scheduler gtest ${TAOS_LIB_STATIC} qcom executor function ) +ADD_EXECUTABLE(timezoneTest timezoneTest.cpp) +TARGET_LINK_LIBRARIES( + timezoneTest + os util common transport parser catalog scheduler gtest ${TAOS_LIB_STATIC} qcom executor function +) + ADD_EXECUTABLE(tmqTest tmqTest.cpp) TARGET_LINK_LIBRARIES( tmqTest @@ -41,12 +47,18 @@ TARGET_INCLUDE_DIRECTORIES( PRIVATE "${TD_SOURCE_DIR}/source/client/inc" ) -#IF(${TD_LINUX}) +TARGET_INCLUDE_DIRECTORIES( + timezoneTest + PUBLIC "${TD_SOURCE_DIR}/include/client/" + PRIVATE "${TD_SOURCE_DIR}/source/client/inc" +) + +IF(${TD_LINUX}) add_test( NAME clientTest COMMAND clientTest ) -#ENDIF () +ENDIF () TARGET_INCLUDE_DIRECTORIES( tmqTest @@ -80,3 +92,8 @@ add_test( NAME userOperTest COMMAND userOperTest ) + +add_test( + NAME timezoneTest + COMMAND timezoneTest +) diff --git a/source/client/test/clientTests.cpp b/source/client/test/clientTests.cpp index 1e4faa3eae..f4317f07f7 100644 --- a/source/client/test/clientTests.cpp +++ b/source/client/test/clientTests.cpp @@ -1489,529 +1489,4 @@ TEST(clientCase, sub_tb_mt_test) { } } -TAOS* getConnWithGlobalOption(const char *tz){ - int code = taos_options(TSDB_OPTION_TIMEZONE, tz); - ASSERT(code == 0); - TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); - ASSERT(pConn != nullptr); - return pConn; -} - -TAOS* getConnWithOption(const char *tz){ - TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); - ASSERT(pConn != nullptr); - if (tz != NULL){ - int code = taos_options_connection(pConn, TSDB_OPTION_CONNECTION_TIMEZONE, tz); - ASSERT(code == 0); - } - return pConn; -} - -void execQuery(TAOS* pConn, const char *sql){ - TAOS_RES* pRes = taos_query(pConn, sql); - ASSERT(taos_errno(pRes) == TSDB_CODE_SUCCESS); - taos_free_result(pRes); -} - -void execQueryFail(TAOS* pConn, const char *sql){ - TAOS_RES* pRes = taos_query(pConn, sql); - ASSERT(taos_errno(pRes) != TSDB_CODE_SUCCESS); - taos_free_result(pRes); -} - -void checkRows(TAOS* pConn, const char *sql, int32_t expectedRows){ - TAOS_RES* pRes = taos_query(pConn, sql); - ASSERT(taos_errno(pRes) == TSDB_CODE_SUCCESS); - TAOS_ROW pRow = NULL; - int rows = 0; - while ((pRow = taos_fetch_row(pRes)) != NULL) { - rows++; - } - ASSERT(rows == expectedRows); - taos_free_result(pRes); -} - -void check_timezone(TAOS* pConn, const char *sql, const char* tz){ - TAOS_RES *pRes = taos_query(pConn, sql); - ASSERT(taos_errno(pRes) == 0); - TAOS_ROW row = NULL; - while ((row = taos_fetch_row(pRes)) != NULL) { - if (strcmp((const char*)row[0], "timezone") == 0){ - ASSERT(strstr((const char*)row[1], tz) != NULL); - } - } - taos_free_result(pRes); -} - -void check_sql_result_partial(TAOS* pConn, const char *sql, const char* result){ - TAOS_RES *pRes = taos_query(pConn, sql); - ASSERT(taos_errno(pRes) == 0); - TAOS_ROW row = NULL; - while ((row = taos_fetch_row(pRes)) != NULL) { - ASSERT(strstr((const char*)row[0], result) != NULL); - } - taos_free_result(pRes); -} - -int64_t get_sql_result(TAOS* pConn, const char *sql){ - int64_t ts = 0; - TAOS_RES *pRes = taos_query(pConn, sql); - ASSERT(taos_errno(pRes) == 0); - TAOS_ROW row = NULL; - while ((row = taos_fetch_row(pRes)) != NULL) { - ts = *(int64_t*)row[0]; - } - taos_free_result(pRes); - return ts; -} - -void check_sql_result(TAOS* pConn, const char *sql, const char* result){ - TAOS_RES *pRes = taos_query(pConn, sql); - ASSERT(taos_errno(pRes) == 0); - TAOS_ROW row = NULL; - while ((row = taos_fetch_row(pRes)) != NULL) { - ASSERT (strcmp((const char*)row[0], result) == 0); - } - taos_free_result(pRes); -} - -void check_sql_result_integer(TAOS* pConn, const char *sql, int64_t result){ - TAOS_RES *pRes = taos_query(pConn, sql); - ASSERT(taos_errno(pRes) == 0); - TAOS_ROW row = NULL; - while ((row = taos_fetch_row(pRes)) != NULL) { - ASSERT (*(int64_t*)row[0] == result); - } - taos_free_result(pRes); -} - -void check_set_timezone(TAOS* optionFunc(const char *tz)){ - { - TAOS* pConn = optionFunc("UTC-8"); - check_timezone(pConn, "show local variables", "UTC-8"); - - execQuery(pConn, "drop database if exists db1"); - execQuery(pConn, "create database db1"); - execQuery(pConn, "create table db1.t1 (ts timestamp, v int)"); - - execQuery(pConn, "insert into db1.t1 values('2023-09-16 17:00:00', 1)"); - checkRows(pConn, "select * from db1.t1 where ts == '2023-09-16 17:00:00'", 1); - - taos_close(pConn); - } - - { - TAOS* pConn = optionFunc("UTC+8"); - check_timezone(pConn, "show local variables", "UTC+8"); - checkRows(pConn, "select * from db1.t1 where ts == '2023-09-16 01:00:00'", 1); - execQuery(pConn, "insert into db1.t1 values('2023-09-16 17:00:01', 1)"); - - taos_close(pConn); - } - - { - TAOS* pConn = optionFunc("UTC+0"); - check_timezone(pConn, "show local variables", "UTC+0"); - checkRows(pConn, "select * from db1.t1 where ts == '2023-09-16 09:00:00'", 1); - checkRows(pConn, "select * from db1.t1 where ts == '2023-09-17 01:00:01'", 1); - - taos_close(pConn); - } -} - -TEST(clientCase, set_timezone_Test) { - check_set_timezone(getConnWithGlobalOption); - check_set_timezone(getConnWithOption); -} - -TEST(clientCase, alter_timezone_Test) { - TAOS* pConn = getConnWithGlobalOption("UTC-8"); - check_timezone(pConn, "show local variables", "UTC-8"); - - execQuery(pConn, "alter local 'timezone Asia/Kolkata'"); - check_timezone(pConn, "show local variables", "Asia/Kolkata"); - - execQueryFail(pConn, "alter dnode 1 'timezone Asia/Kolkata'"); -} - -struct insert_params -{ - const char *tz; - const char *tbname; - const char *t1; - const char *t2; -}; - -struct insert_params params1[] = { - {"UTC", "ntb", "2023-09-16 17:00:00", "2023-09-16 17:00:00+08:00"}, - {"UTC", "ctb1", "2023-09-16 17:00:00", "2023-09-16 17:00:00+08:00"}, -}; - -struct insert_params params2[] = { - {"UTC+9", "ntb", "2023-09-16 08:00:00", "2023-09-16 08:00:00-01:00"}, - {"UTC+9", "ctb1", "2023-09-16 08:00:00", "2023-09-16 11:00:00+02:00"}, -}; - -void do_insert(struct insert_params params){ - TAOS* pConn = getConnWithOption(params.tz); - char sql[1024] = {0}; - (void)snprintf(sql, sizeof(sql), "insert into db1.%s values('%s', '%s', 1)", params.tbname, params.t1, params.t2); - execQuery(pConn, sql); - taos_close(pConn); -} - -void do_select(struct insert_params params){ - TAOS* pConn = getConnWithOption(params.tz); - char sql[1024] = {0}; - (void)snprintf(sql, sizeof(sql), "select * from db1.%s where ts == '%s' and c1 == '%s'", params.tbname, params.t1, params.t2); - checkRows(pConn, sql, 1); - taos_close(pConn); -} - -// test insert string and integer to timestamp both normal table and child table(and tag) -TEST(clientCase, insert_with_timezone_Test) { - /* - * 1. prepare data, create db and tables - */ - TAOS* pConn1 = getConnWithOption("UTC+2"); - execQuery(pConn1, "drop database if exists db1"); - execQuery(pConn1, "create database db1"); - execQuery(pConn1, "create table db1.ntb (ts timestamp, c1 timestamp, c2 int)"); - execQuery(pConn1, "create table db1.stb (ts timestamp, c1 timestamp, c2 int) tags(t1 timestamp, t2 timestamp, t3 int)"); - execQuery(pConn1, "create table db1.ctb1 using db1.stb tags(\"2023-09-16 17:00:00+05:00\", \"2023-09-16 17:00:00\", 1)"); - execQuery(pConn1, "create table db1.ctb2 using db1.stb tags(1732178775000, 1732178775000, 1)"); - execQuery(pConn1, "insert into db1.ntb values(1732178775133, 1732178775133, 1)"); - execQuery(pConn1, "insert into db1.ctb1 values(1732178775133, 1732178775133, 1)"); - execQuery(pConn1, "insert into db1.ctb2 values(1732178775133, 1732178775133, 1)"); - - /* - * 2. test tag and timestamp with integer format - */ - TAOS* pConn2 = getConnWithOption("UTC-2"); - checkRows(pConn2, "select * from db1.stb where t1 == '2023-09-16 17:00:00+05:00' and t2 == '2023-09-16 21:00:00'", 1); - checkRows(pConn2, "select * from db1.stb where t1 == '2024-11-21 16:46:15+08:00' and t2 == '2024-11-21 09:46:15+01:00'", 1); - checkRows(pConn2, "select * from db1.ntb where ts == '2024-11-21 09:46:15.133+01:00' and c1 == '2024-11-21 10:46:15.133'", 1); - checkRows(pConn2, "select * from db1.ctb1 where ts == '2024-11-21 09:46:15.133+01:00' and c1 == '2024-11-21 10:46:15.133'", 1); - - check_sql_result(pConn2, "select TO_ISO8601(ts) from db1.ctb1", "2024-11-21T10:46:15.133+0200"); // 2024-01-01 23:00:00+0200 - - - /* - * 3. test timestamp with string format - */ - for (unsigned int i = 0; i < sizeof (params1) / sizeof (params1[0]); ++i){ - do_insert(params1[i]); - do_select(params1[i]); - do_select(params2[i]); - } - - /* - * 4. test NULL timezone, use default timezone UTC-8 - */ - TAOS* pConn3 = getConnWithOption(NULL); - checkRows(pConn3, "select * from db1.stb where t1 == '2023-09-16 20:00:00' and t2 == '2023-09-17 03:00:00'", 2); - checkRows(pConn3, "select * from db1.stb where t1 == 1732178775000 and t2 == 1732178775000", 1); - checkRows(pConn3, "select * from db1.ntb where ts == '2024-11-21 16:46:15.133' and c1 == '2024-11-21 16:46:15.133'", 1); - checkRows(pConn3, "select * from db1.ctb1 where ts == '2023-09-17 01:00:00' and c1 == '2023-09-16 17:00:00'", 1); - - /* - * 5. test multi connection with different timezone - */ - checkRows(pConn2, "select * from db1.ctb1 where ts == '2024-11-21 09:46:15.133+01:00' and c1 == '2024-11-21 10:46:15.133'", 1); - checkRows(pConn1, "select * from db1.ctb1 where ts == '2024-11-21 09:46:15.133+01:00' and c1 == '2024-11-21 06:46:15.133'", 1); - - taos_close(pConn1); - taos_close(pConn2); - taos_close(pConn3); -} - -TEST(clientCase, func_timezone_Test) { - TAOS* pConn = getConnWithGlobalOption("UTC+8"); - check_sql_result(pConn, "select timezone()", "UTC+8 (UTC, -0800)"); - taos_close(pConn); - - pConn = getConnWithOption("UTC-2"); - - execQuery(pConn, "drop database if exists db1"); - execQuery(pConn, "create database db1"); - execQuery(pConn, "create table db1.ntb (ts timestamp, c1 binary(32), c2 int)"); - execQuery(pConn, "insert into db1.ntb values(1704142800000, '2024-01-01 23:00:00', 1)"); // 2024-01-01 23:00:00+0200 - - // test timezone - check_sql_result(pConn, "select timezone()", "UTC-test (UTC, +0200)"); - - // test timetruncate - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 23:00:00', 1d, 0))", "2024-01-01T02:00:00.000+0200"); - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 01:00:00', 1d, 0))", "2023-12-31T02:00:00.000+0200"); - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 01:00:00+0300', 1d, 0))", "2023-12-31T02:00:00.000+0200"); - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 01:00:00-0300', 1d, 0))", "2024-01-01T02:00:00.000+0200"); - - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 23:00:00', 1w, 0))", "2024-01-04T02:00:00.000+0200"); - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 01:00:00', 1w, 0))", "2023-12-28T02:00:00.000+0200"); - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 01:00:00+0300', 1w, 0))", "2023-12-28T02:00:00.000+0200"); - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 01:00:00-0300', 1w, 0))", "2024-01-04T02:00:00.000+0200"); - - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 23:00:00', 1d, 1))", "2024-01-01T00:00:00.000+0200"); - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 01:00:00', 1d, 1))", "2024-01-01T00:00:00.000+0200"); - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 01:00:00+0500', 1d, 1))", "2023-12-31T00:00:00.000+0200"); - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 01:00:00-0300', 1d, 1))", "2024-01-01T00:00:00.000+0200"); - - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 23:00:00', 1w, 1))", "2024-01-04T00:00:00.000+0200"); - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 01:00:00', 1w, 1))", "2024-01-04T00:00:00.000+0200"); - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 01:00:00+0500', 1w, 1))", "2023-12-28T00:00:00.000+0200"); - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 01:00:00-0300', 1w, 1))", "2024-01-04T00:00:00.000+0200"); - - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE(1704142800000, 1d, 0))", "2024-01-01T02:00:00.000+0200"); // 2024-01-01 23:00:00+0200 - check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE(ts, 1w, 1)) from db1.ntb", "2023-12-28T00:00:00.000+0200"); // 2024-01-01 23:00:00+0200 - - // TODAY - check_sql_result_partial(pConn, "select TO_ISO8601(today())", "T00:00:00.000+0200"); - - // NOW - check_sql_result_partial(pConn, "select TO_ISO8601(now())", "+0200"); - - // WEEKDAY - check_sql_result_integer(pConn, "select WEEKDAY('2024-01-01')", 0); - check_sql_result_integer(pConn, "select WEEKDAY('2024-01-01 03:00:00')", 0); - check_sql_result_integer(pConn, "select WEEKDAY('2024-01-01 23:00:00+0200')", 0); - check_sql_result_integer(pConn, "select WEEKDAY('2024-01-01 23:00:00-1100')", 1); - check_sql_result_integer(pConn, "select WEEKDAY(1704142800000)", 0); - check_sql_result_integer(pConn, "select WEEKDAY(ts) from db1.ntb", 1); - - // DAYOFWEEK - check_sql_result_integer(pConn, "select DAYOFWEEK('2024-01-01')", 2); - check_sql_result_integer(pConn, "select DAYOFWEEK('2024-01-01 03:00:00')", 2); - check_sql_result_integer(pConn, "select DAYOFWEEK('2024-01-01 23:00:00+0200')", 2); - check_sql_result_integer(pConn, "select DAYOFWEEK('2024-01-01 23:00:00-1100')", 3); - check_sql_result_integer(pConn, "select DAYOFWEEK(1704142800000)", 2); - check_sql_result_integer(pConn, "select DAYOFWEEK(ts) from db1.ntb", 3); - - // WEEK - check_sql_result_integer(pConn, "select WEEK('2024-01-07')", 1); - check_sql_result_integer(pConn, "select WEEK('2024-01-07 02:00:00')", 1); - check_sql_result_integer(pConn, "select WEEK('2024-01-07 02:00:00+0200')", 1); - check_sql_result_integer(pConn, "select WEEK('2024-01-07 02:00:00+1100')", 0); - check_sql_result_integer(pConn, "select WEEK(1704142800000)", 0); // 2024-01-01 23:00:00+0200 - check_sql_result_integer(pConn, "select WEEK(ts) from db1.ntb", 0); // 2024-01-01 23:00:00+0200 - - check_sql_result_integer(pConn, "select WEEK('2024-01-07', 3)", 1); - check_sql_result_integer(pConn, "select WEEK('2024-01-07 02:00:00', 3)", 1); - check_sql_result_integer(pConn, "select WEEK('2024-01-07 02:00:00+0200', 3)", 1); - check_sql_result_integer(pConn, "select WEEK('2024-01-01 02:00:00+1100', 3)", 52); - check_sql_result_integer(pConn, "select WEEK(1704142800000, 3)", 1); // 2024-01-01 23:00:00+0200 - check_sql_result_integer(pConn, "select WEEK(ts, 3) from db1.ntb", 1); // 2024-01-01 23:00:00+0200 - - // WEEKOFYEAR - check_sql_result_integer(pConn, "select WEEKOFYEAR('2024-01-07')", 1); - check_sql_result_integer(pConn, "select WEEKOFYEAR('2024-01-07 02:00:00')", 1); - check_sql_result_integer(pConn, "select WEEKOFYEAR('2024-01-07 02:00:00+0200')", 1); - check_sql_result_integer(pConn, "select WEEKOFYEAR('2024-01-01 02:00:00+1100')", 52); - check_sql_result_integer(pConn, "select WEEKOFYEAR(1704142800000)", 1); // 2024-01-01 23:00:00+0200 - check_sql_result_integer(pConn, "select WEEKOFYEAR(ts) from db1.ntb", 1); // 2024-01-01 23:00:00+0200 - - // TO_ISO8601 - check_sql_result(pConn, "select TO_ISO8601(ts) from db1.ntb", "2024-01-01T23:00:00.000+0200"); - check_sql_result(pConn, "select TO_ISO8601(ts,'-08') from db1.ntb", "2024-01-01T13:00:00.000-08"); - check_sql_result(pConn, "select TO_ISO8601(1)", "1970-01-01T02:00:00.001+0200"); - check_sql_result(pConn, "select TO_ISO8601(1,'+0800')", "1970-01-01T08:00:00.001+0800"); - - // TO_UNIXTIMESTAMP - check_sql_result_integer(pConn, "select TO_UNIXTIMESTAMP(c1) from db1.ntb", 1704121200000); // use timezone in server UTC-8 - check_sql_result_integer(pConn, "select TO_UNIXTIMESTAMP('2024-01-01T23:00:00.000+0200')", 1704142800000); - check_sql_result_integer(pConn, "select TO_UNIXTIMESTAMP('2024-01-01T13:00:00.000-08')", 1704142800000); - check_sql_result_integer(pConn, "select TO_UNIXTIMESTAMP('2024-01-01T23:00:00.001')", 1704142800001); - - // TO_TIMESTAMP - check_sql_result_integer(pConn, "select TO_TIMESTAMP(c1,'yyyy-mm-dd hh24:mi:ss') from db1.ntb", 1704121200000); // use timezone in server UTC-8 - check_sql_result_integer(pConn, "select TO_TIMESTAMP('2024-01-01 23:00:00+02:00', 'yyyy-mm-dd hh24:mi:ss tzh')", 1704142800000); - check_sql_result_integer(pConn, "select TO_TIMESTAMP('2024-01-01T13:00:00-08', 'yyyy-mm-ddThh24:mi:ss tzh')", 1704142800000); - check_sql_result_integer(pConn, "select TO_TIMESTAMP('2024/01/01 23:00:00', 'yyyy/mm/dd hh24:mi:ss')", 1704142800000); - - // TO_CHAR - check_sql_result(pConn, "select TO_CHAR(ts,'yyyy-mm-dd hh24:mi:ss') from db1.ntb", "2024-01-02 05:00:00"); // use timezone in server UTC-8 - check_sql_result(pConn, "select TO_CHAR(cast(1704142800000 as timestamp), 'yyyy-mm-dd hh24:mi:ss tzh')", "2024-01-01 23:00:00 +02"); - check_sql_result(pConn, "select TO_CHAR(cast(1704142800000 as timestamp), 'yyyy-mm-dd hh24:mi:ss')", "2024-01-01 23:00:00"); - - // TIMEDIFF - check_sql_result_integer(pConn, "select TIMEDIFF(c1, '2024-01-01T23:00:00.001+02') from db1.ntb", -21600001); // use timezone in server UTC-8 - check_sql_result_integer(pConn, "select TIMEDIFF(c1, '2024-01-01T23:00:00.001') from db1.ntb", -1); // use timezone in server UTC-8 - check_sql_result_integer(pConn, "select TIMEDIFF('2024-01-01T23:00:00.001', '2024-01-01T13:00:00.000-08')", 1); - - // CAST - check_sql_result_integer(pConn, "select CAST(c1 as timestamp) from db1.ntb", 1704121200000); - check_sql_result_integer(pConn, "select CAST('2024-01-01T23:00:00.000+02' as timestamp)", 1704142800000); - check_sql_result_integer(pConn, "select CAST('2024-01-01T23:00:00.000' as timestamp)", 1704142800000); - - taos_close(pConn); - - // hash join - pConn = getConnWithOption("UTC+1"); - - execQuery(pConn, "drop database if exists db1"); - execQuery(pConn, "create database db1"); - execQuery(pConn, "create table db1.ntb (ts timestamp, c1 binary(32), c2 int)"); - execQuery(pConn, "create table db1.ntb1 (ts timestamp, c1 binary(32), c2 int)"); - execQuery(pConn, "insert into db1.ntb values(1703987400000, '2023-12-31 00:50:00', 1)"); // 2023-12-31 00:50:00-0100 - execQuery(pConn, "insert into db1.ntb1 values(1704070200000, '2023-12-31 23:50:00', 11)"); // 2023-12-31 23:50:00-0100 - checkRows(pConn, "select a.ts,b.ts from db1.ntb a join db1.ntb1 b on timetruncate(a.ts, 1d) = timetruncate(b.ts, 1d)", 1); - - taos_close(pConn); - -} - -time_t time_winter = 1731323281; // 2024-11-11 19:08:01+0800 -time_t time_summer = 1731323281 - 120 * 24 * 60 * 60; - -struct test_times -{ - const char *name; - time_t t; - const char *timezone; -} test_tz[] = { - {"", time_winter, " (UTC, +0000)"}, - {"America/New_York", time_winter, "America/New_York (EST, -0500)"}, // 2024-11-11 19:08:01+0800 - {"America/New_York", time_summer, "America/New_York (EDT, -0400)"}, - {"Asia/Kolkata", time_winter, "Asia/Kolkata (IST, +0530)"}, - {"Asia/Shanghai", time_winter, "Asia/Shanghai (CST, +0800)"}, - {"Europe/London", time_winter, "Europe/London (GMT, +0000)"}, - {"Europe/London", time_summer, "Europe/London (BST, +0100)"} -}; - -void timezone_str_test(const char* tz, time_t t, const char* tzStr) { - int code = setenv("TZ", tz, 1); - ASSERT(-1 != code); - tzset(); - - char str1[TD_TIMEZONE_LEN] = {0}; - ASSERT(taosFormatTimezoneStr(t, tz, NULL, str1) == 0); - ASSERT_STREQ(str1, tzStr); -} - -void timezone_rz_str_test(const char* tz, time_t t, const char* tzStr) { - timezone_t sp = tzalloc(tz); - ASSERT(sp); - - char str1[TD_TIMEZONE_LEN] = {0}; - ASSERT(taosFormatTimezoneStr(t, tz, sp, str1) == 0); - ASSERT_STREQ(str1, tzStr); - tzfree(sp); -} - -TEST(clientCase, format_timezone_Test) { - for (unsigned int i = 0; i < sizeof (test_tz) / sizeof (test_tz[0]); ++i){ - timezone_str_test(test_tz[i].name, test_tz[i].t, test_tz[i].timezone); - timezone_str_test(test_tz[i].name, test_tz[i].t, test_tz[i].timezone); - } -} - -TEST(clientCase, get_tz_Test) { - { - char tz[TD_TIMEZONE_LEN] = {0}; - getTimezoneStr(tz); - ASSERT_STREQ(tz, "Asia/Shanghai"); - -// getTimezoneStr(tz); -// ASSERT_STREQ(tz, "Asia/Shanghai"); -// -// getTimezoneStr(tz); -// ASSERT_STREQ(tz, "n/a"); - } -} - -struct { - const char * env; - time_t expected; -} test_mk[] = { - {"MST", 832935315}, - {"", 832910115}, - {":UTC", 832910115}, - {"UTC", 832910115}, - {"UTC0", 832910115} -}; - - -TEST(clientCase, mktime_Test){ - struct tm tm; - time_t t; - - memset (&tm, 0, sizeof (tm)); - tm.tm_isdst = 0; - tm.tm_year = 96; /* years since 1900 */ - tm.tm_mon = 4; - tm.tm_mday = 24; - tm.tm_hour = 3; - tm.tm_min = 55; - tm.tm_sec = 15; - - for (unsigned int i = 0; i < sizeof (test_mk) / sizeof (test_mk[0]); ++i) - { - setenv ("TZ", test_mk[i].env, 1); - t = taosMktime (&tm, NULL); - ASSERT (t == test_mk[i].expected); - } -} - -TEST(clientCase, mktime_rz_Test){ - struct tm tm; - time_t t; - - memset (&tm, 0, sizeof (tm)); - tm.tm_isdst = 0; - tm.tm_year = 96; /* years since 1900 */ - tm.tm_mon = 4; - tm.tm_mday = 24; - tm.tm_hour = 3; - tm.tm_min = 55; - tm.tm_sec = 15; - - for (unsigned int i = 0; i < sizeof (test_mk) / sizeof (test_mk[0]); ++i) - { - timezone_t tz = tzalloc(test_mk[i].env); - ASSERT(tz); - t = taosMktime(&tm, tz); - ASSERT (t == test_mk[i].expected); - tzfree(tz); - } -} - -TEST(testCase, localtime_performance_Test) { - timezone_t sp = tzalloc("Asia/Shanghai"); - ASSERT(sp); - - int cnt = 10000000; - int times = 10; - int64_t time_localtime = 0; - int64_t time_localtime_rz = 0; -// int cnt = 1000000; - for (int i = 0; i < times; ++i) { - int64_t t1 = taosGetTimestampNs(); - for (int j = 0; j < cnt; ++j) { - time_t t = time_winter - j; - struct tm tm1; - ASSERT (taosLocalTime(&t, &tm1, NULL, 0, NULL)); - } - int64_t tmp = taosGetTimestampNs() - t1; - printf("localtime cost:%" PRId64 " ns, run %" PRId64 " times", tmp, cnt); - time_localtime += tmp/cnt; - - printf("\n"); - - - - int64_t t2 = taosGetTimestampNs(); - for (int j = 0; j < cnt; ++j) { - time_t t = time_winter - j; - struct tm tm1; - ASSERT (taosLocalTime(&t, &tm1, NULL, 0, sp)); - } - tmp = taosGetTimestampNs() - t2; - printf("localtime_rz cost:%" PRId64 " ns, run %" PRId64 " times", tmp, cnt); - time_localtime_rz += tmp/cnt; - printf("\n\n"); - } - printf("average: localtime cost:%" PRId64 " ns, localtime_rz cost:%" PRId64 " ns", time_localtime/times, time_localtime_rz/times); - tzfree(sp); -} - - #pragma GCC diagnostic pop diff --git a/source/client/test/timezoneTest.cpp b/source/client/test/timezoneTest.cpp new file mode 100644 index 0000000000..ff555540bd --- /dev/null +++ b/source/client/test/timezoneTest.cpp @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2019 TAOS Data, Inc. + * + * This program is free software: you can use, redistribute, and/or modify + * it under the terms of the GNU Affero General Public License, version 3 + * or later ("AGPL"), as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "taoserror.h" +#include "tglobal.h" +#include "thash.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wwrite-strings" +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wsign-compare" + +#include "executor.h" +#include "taos.h" + + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} + +TAOS* getConnWithGlobalOption(const char *tz){ + int code = taos_options(TSDB_OPTION_TIMEZONE, tz); + ASSERT(code == 0); + TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); + ASSERT(pConn != nullptr); + return pConn; +} + +TAOS* getConnWithOption(const char *tz){ + TAOS* pConn = taos_connect("localhost", "root", "taosdata", NULL, 0); + ASSERT(pConn != nullptr); + if (tz != NULL){ + int code = taos_options_connection(pConn, TSDB_OPTION_CONNECTION_TIMEZONE, tz); + ASSERT(code == 0); + } + return pConn; +} + +void execQuery(TAOS* pConn, const char *sql){ + TAOS_RES* pRes = taos_query(pConn, sql); + ASSERT(taos_errno(pRes) == TSDB_CODE_SUCCESS); + taos_free_result(pRes); +} + +void execQueryFail(TAOS* pConn, const char *sql){ + TAOS_RES* pRes = taos_query(pConn, sql); + ASSERT(taos_errno(pRes) != TSDB_CODE_SUCCESS); + taos_free_result(pRes); +} + +void checkRows(TAOS* pConn, const char *sql, int32_t expectedRows){ + TAOS_RES* pRes = taos_query(pConn, sql); + ASSERT(taos_errno(pRes) == TSDB_CODE_SUCCESS); + TAOS_ROW pRow = NULL; + int rows = 0; + while ((pRow = taos_fetch_row(pRes)) != NULL) { + rows++; + } + ASSERT(rows == expectedRows); + taos_free_result(pRes); +} + +void check_timezone(TAOS* pConn, const char *sql, const char* tz){ + TAOS_RES *pRes = taos_query(pConn, sql); + ASSERT(taos_errno(pRes) == 0); + TAOS_ROW row = NULL; + while ((row = taos_fetch_row(pRes)) != NULL) { + if (strcmp((const char*)row[0], "timezone") == 0){ + ASSERT(strstr((const char*)row[1], tz) != NULL); + } + } + taos_free_result(pRes); +} + +void check_sql_result_partial(TAOS* pConn, const char *sql, const char* result){ + TAOS_RES *pRes = taos_query(pConn, sql); + ASSERT(taos_errno(pRes) == 0); + TAOS_ROW row = NULL; + while ((row = taos_fetch_row(pRes)) != NULL) { + ASSERT(strstr((const char*)row[0], result) != NULL); + } + taos_free_result(pRes); +} + +int64_t get_sql_result(TAOS* pConn, const char *sql){ + int64_t ts = 0; + TAOS_RES *pRes = taos_query(pConn, sql); + ASSERT(taos_errno(pRes) == 0); + TAOS_ROW row = NULL; + while ((row = taos_fetch_row(pRes)) != NULL) { + ts = *(int64_t*)row[0]; + } + taos_free_result(pRes); + return ts; +} + +void check_sql_result(TAOS* pConn, const char *sql, const char* result){ + TAOS_RES *pRes = taos_query(pConn, sql); + ASSERT(taos_errno(pRes) == 0); + TAOS_ROW row = NULL; + while ((row = taos_fetch_row(pRes)) != NULL) { + ASSERT (strcmp((const char*)row[0], result) == 0); + } + taos_free_result(pRes); +} + +void check_sql_result_integer(TAOS* pConn, const char *sql, int64_t result){ + TAOS_RES *pRes = taos_query(pConn, sql); + ASSERT(taos_errno(pRes) == 0); + TAOS_ROW row = NULL; + while ((row = taos_fetch_row(pRes)) != NULL) { + ASSERT (*(int64_t*)row[0] == result); + } + taos_free_result(pRes); +} + +void check_set_timezone(TAOS* optionFunc(const char *tz)){ + { + TAOS* pConn = optionFunc("UTC-8"); + check_timezone(pConn, "show local variables", "UTC-8"); + + execQuery(pConn, "drop database if exists db1"); + execQuery(pConn, "create database db1"); + execQuery(pConn, "create table db1.t1 (ts timestamp, v int)"); + + execQuery(pConn, "insert into db1.t1 values('2023-09-16 17:00:00', 1)"); + checkRows(pConn, "select * from db1.t1 where ts == '2023-09-16 17:00:00'", 1); + + taos_close(pConn); + } + + { + TAOS* pConn = optionFunc("UTC+8"); + check_timezone(pConn, "show local variables", "UTC+8"); + checkRows(pConn, "select * from db1.t1 where ts == '2023-09-16 01:00:00'", 1); + execQuery(pConn, "insert into db1.t1 values('2023-09-16 17:00:01', 1)"); + + taos_close(pConn); + } + + { + TAOS* pConn = optionFunc("UTC+0"); + check_timezone(pConn, "show local variables", "UTC+0"); + checkRows(pConn, "select * from db1.t1 where ts == '2023-09-16 09:00:00'", 1); + checkRows(pConn, "select * from db1.t1 where ts == '2023-09-17 01:00:01'", 1); + + taos_close(pConn); + } +} + +TEST(timezoneCase, set_timezone_Test) { + check_set_timezone(getConnWithGlobalOption); + check_set_timezone(getConnWithOption); +} + +TEST(timezoneCase, alter_timezone_Test) { + TAOS* pConn = getConnWithGlobalOption("UTC-8"); + check_timezone(pConn, "show local variables", "UTC-8"); + + execQuery(pConn, "alter local 'timezone Asia/Kolkata'"); + check_timezone(pConn, "show local variables", "Asia/Kolkata"); + + execQueryFail(pConn, "alter dnode 1 'timezone Asia/Kolkata'"); +} + +struct insert_params +{ + const char *tz; + const char *tbname; + const char *t1; + const char *t2; +}; + +struct insert_params params1[] = { + {"UTC", "ntb", "2023-09-16 17:00:00", "2023-09-16 17:00:00+08:00"}, + {"UTC", "ctb1", "2023-09-16 17:00:00", "2023-09-16 17:00:00+08:00"}, +}; + +struct insert_params params2[] = { + {"UTC+9", "ntb", "2023-09-16 08:00:00", "2023-09-16 08:00:00-01:00"}, + {"UTC+9", "ctb1", "2023-09-16 08:00:00", "2023-09-16 11:00:00+02:00"}, +}; + +void do_insert(struct insert_params params){ + TAOS* pConn = getConnWithOption(params.tz); + char sql[1024] = {0}; + (void)snprintf(sql, sizeof(sql), "insert into db1.%s values('%s', '%s', 1)", params.tbname, params.t1, params.t2); + execQuery(pConn, sql); + taos_close(pConn); +} + +void do_select(struct insert_params params){ + TAOS* pConn = getConnWithOption(params.tz); + char sql[1024] = {0}; + (void)snprintf(sql, sizeof(sql), "select * from db1.%s where ts == '%s' and c1 == '%s'", params.tbname, params.t1, params.t2); + checkRows(pConn, sql, 1); + taos_close(pConn); +} + +// test insert string and integer to timestamp both normal table and child table(and tag) +TEST(timezoneCase, insert_with_timezone_Test) { + /* + * 1. prepare data, create db and tables + */ + TAOS* pConn1 = getConnWithOption("UTC+2"); + execQuery(pConn1, "drop database if exists db1"); + execQuery(pConn1, "create database db1"); + execQuery(pConn1, "create table db1.ntb (ts timestamp, c1 timestamp, c2 int)"); + execQuery(pConn1, "create table db1.stb (ts timestamp, c1 timestamp, c2 int) tags(t1 timestamp, t2 timestamp, t3 int)"); + execQuery(pConn1, "create table db1.ctb1 using db1.stb tags(\"2023-09-16 17:00:00+05:00\", \"2023-09-16 17:00:00\", 1)"); + execQuery(pConn1, "create table db1.ctb2 using db1.stb tags(1732178775000, 1732178775000, 1)"); + execQuery(pConn1, "insert into db1.ntb values(1732178775133, 1732178775133, 1)"); + execQuery(pConn1, "insert into db1.ctb1 values(1732178775133, 1732178775133, 1)"); + execQuery(pConn1, "insert into db1.ctb2 values(1732178775133, 1732178775133, 1)"); + + /* + * 2. test tag and timestamp with integer format + */ + TAOS* pConn2 = getConnWithOption("UTC-2"); + checkRows(pConn2, "select * from db1.stb where t1 == '2023-09-16 17:00:00+05:00' and t2 == '2023-09-16 21:00:00'", 1); + checkRows(pConn2, "select * from db1.stb where t1 == '2024-11-21 16:46:15+08:00' and t2 == '2024-11-21 09:46:15+01:00'", 1); + checkRows(pConn2, "select * from db1.ntb where ts == '2024-11-21 09:46:15.133+01:00' and c1 == '2024-11-21 10:46:15.133'", 1); + checkRows(pConn2, "select * from db1.ctb1 where ts == '2024-11-21 09:46:15.133+01:00' and c1 == '2024-11-21 10:46:15.133'", 1); + + check_sql_result(pConn2, "select TO_ISO8601(ts) from db1.ctb1", "2024-11-21T10:46:15.133+0200"); // 2024-01-01 23:00:00+0200 + + + /* + * 3. test timestamp with string format + */ + for (unsigned int i = 0; i < sizeof (params1) / sizeof (params1[0]); ++i){ + do_insert(params1[i]); + do_select(params1[i]); + do_select(params2[i]); + } + + /* + * 4. test NULL timezone, use default timezone UTC-8 + */ + TAOS* pConn3 = getConnWithOption(NULL); + checkRows(pConn3, "select * from db1.stb where t1 == '2023-09-16 20:00:00' and t2 == '2023-09-17 03:00:00'", 2); + checkRows(pConn3, "select * from db1.stb where t1 == 1732178775000 and t2 == 1732178775000", 1); + checkRows(pConn3, "select * from db1.ntb where ts == '2024-11-21 16:46:15.133' and c1 == '2024-11-21 16:46:15.133'", 1); + checkRows(pConn3, "select * from db1.ctb1 where ts == '2023-09-17 01:00:00' and c1 == '2023-09-16 17:00:00'", 1); + + /* + * 5. test multi connection with different timezone + */ + checkRows(pConn2, "select * from db1.ctb1 where ts == '2024-11-21 09:46:15.133+01:00' and c1 == '2024-11-21 10:46:15.133'", 1); + checkRows(pConn1, "select * from db1.ctb1 where ts == '2024-11-21 09:46:15.133+01:00' and c1 == '2024-11-21 06:46:15.133'", 1); + + taos_close(pConn1); + taos_close(pConn2); + taos_close(pConn3); +} + +TEST(timezoneCase, func_timezone_Test) { + TAOS* pConn = getConnWithGlobalOption("UTC+8"); + check_sql_result(pConn, "select timezone()", "UTC+8 (UTC, -0800)"); + taos_close(pConn); + + pConn = getConnWithOption("UTC-2"); + + execQuery(pConn, "drop database if exists db1"); + execQuery(pConn, "create database db1"); + execQuery(pConn, "create table db1.ntb (ts timestamp, c1 binary(32), c2 int)"); + execQuery(pConn, "insert into db1.ntb values(1704142800000, '2024-01-01 23:00:00', 1)"); // 2024-01-01 23:00:00+0200 + + // test timezone + check_sql_result(pConn, "select timezone()", "UTC-test (UTC, +0200)"); + + // test timetruncate + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 23:00:00', 1d, 0))", "2024-01-01T02:00:00.000+0200"); + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 01:00:00', 1d, 0))", "2023-12-31T02:00:00.000+0200"); + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 01:00:00+0300', 1d, 0))", "2023-12-31T02:00:00.000+0200"); + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 01:00:00-0300', 1d, 0))", "2024-01-01T02:00:00.000+0200"); + + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 23:00:00', 1w, 0))", "2024-01-04T02:00:00.000+0200"); + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 01:00:00', 1w, 0))", "2023-12-28T02:00:00.000+0200"); + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 01:00:00+0300', 1w, 0))", "2023-12-28T02:00:00.000+0200"); + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 01:00:00-0300', 1w, 0))", "2024-01-04T02:00:00.000+0200"); + + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 23:00:00', 1d, 1))", "2024-01-01T00:00:00.000+0200"); + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 01:00:00', 1d, 1))", "2024-01-01T00:00:00.000+0200"); + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 01:00:00+0500', 1d, 1))", "2023-12-31T00:00:00.000+0200"); + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-01 01:00:00-0300', 1d, 1))", "2024-01-01T00:00:00.000+0200"); + + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 23:00:00', 1w, 1))", "2024-01-04T00:00:00.000+0200"); + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 01:00:00', 1w, 1))", "2024-01-04T00:00:00.000+0200"); + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 01:00:00+0500', 1w, 1))", "2023-12-28T00:00:00.000+0200"); + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE('2024-01-04 01:00:00-0300', 1w, 1))", "2024-01-04T00:00:00.000+0200"); + + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE(1704142800000, 1d, 0))", "2024-01-01T02:00:00.000+0200"); // 2024-01-01 23:00:00+0200 + check_sql_result(pConn, "select TO_ISO8601(TIMETRUNCATE(ts, 1w, 1)) from db1.ntb", "2023-12-28T00:00:00.000+0200"); // 2024-01-01 23:00:00+0200 + + // TODAY + check_sql_result_partial(pConn, "select TO_ISO8601(today())", "T00:00:00.000+0200"); + + // NOW + check_sql_result_partial(pConn, "select TO_ISO8601(now())", "+0200"); + + // WEEKDAY + check_sql_result_integer(pConn, "select WEEKDAY('2024-01-01')", 0); + check_sql_result_integer(pConn, "select WEEKDAY('2024-01-01 03:00:00')", 0); + check_sql_result_integer(pConn, "select WEEKDAY('2024-01-01 23:00:00+0200')", 0); + check_sql_result_integer(pConn, "select WEEKDAY('2024-01-01 23:00:00-1100')", 1); + check_sql_result_integer(pConn, "select WEEKDAY(1704142800000)", 0); + check_sql_result_integer(pConn, "select WEEKDAY(ts) from db1.ntb", 1); + + // DAYOFWEEK + check_sql_result_integer(pConn, "select DAYOFWEEK('2024-01-01')", 2); + check_sql_result_integer(pConn, "select DAYOFWEEK('2024-01-01 03:00:00')", 2); + check_sql_result_integer(pConn, "select DAYOFWEEK('2024-01-01 23:00:00+0200')", 2); + check_sql_result_integer(pConn, "select DAYOFWEEK('2024-01-01 23:00:00-1100')", 3); + check_sql_result_integer(pConn, "select DAYOFWEEK(1704142800000)", 2); + check_sql_result_integer(pConn, "select DAYOFWEEK(ts) from db1.ntb", 3); + + // WEEK + check_sql_result_integer(pConn, "select WEEK('2024-01-07')", 1); + check_sql_result_integer(pConn, "select WEEK('2024-01-07 02:00:00')", 1); + check_sql_result_integer(pConn, "select WEEK('2024-01-07 02:00:00+0200')", 1); + check_sql_result_integer(pConn, "select WEEK('2024-01-07 02:00:00+1100')", 0); + check_sql_result_integer(pConn, "select WEEK(1704142800000)", 0); // 2024-01-01 23:00:00+0200 + check_sql_result_integer(pConn, "select WEEK(ts) from db1.ntb", 0); // 2024-01-01 23:00:00+0200 + + check_sql_result_integer(pConn, "select WEEK('2024-01-07', 3)", 1); + check_sql_result_integer(pConn, "select WEEK('2024-01-07 02:00:00', 3)", 1); + check_sql_result_integer(pConn, "select WEEK('2024-01-07 02:00:00+0200', 3)", 1); + check_sql_result_integer(pConn, "select WEEK('2024-01-01 02:00:00+1100', 3)", 52); + check_sql_result_integer(pConn, "select WEEK(1704142800000, 3)", 1); // 2024-01-01 23:00:00+0200 + check_sql_result_integer(pConn, "select WEEK(ts, 3) from db1.ntb", 1); // 2024-01-01 23:00:00+0200 + + // WEEKOFYEAR + check_sql_result_integer(pConn, "select WEEKOFYEAR('2024-01-07')", 1); + check_sql_result_integer(pConn, "select WEEKOFYEAR('2024-01-07 02:00:00')", 1); + check_sql_result_integer(pConn, "select WEEKOFYEAR('2024-01-07 02:00:00+0200')", 1); + check_sql_result_integer(pConn, "select WEEKOFYEAR('2024-01-01 02:00:00+1100')", 52); + check_sql_result_integer(pConn, "select WEEKOFYEAR(1704142800000)", 1); // 2024-01-01 23:00:00+0200 + check_sql_result_integer(pConn, "select WEEKOFYEAR(ts) from db1.ntb", 1); // 2024-01-01 23:00:00+0200 + + // TO_ISO8601 + check_sql_result(pConn, "select TO_ISO8601(ts) from db1.ntb", "2024-01-01T23:00:00.000+0200"); + check_sql_result(pConn, "select TO_ISO8601(ts,'-08') from db1.ntb", "2024-01-01T13:00:00.000-08"); + check_sql_result(pConn, "select TO_ISO8601(1)", "1970-01-01T02:00:00.001+0200"); + check_sql_result(pConn, "select TO_ISO8601(1,'+0800')", "1970-01-01T08:00:00.001+0800"); + + // TO_UNIXTIMESTAMP + check_sql_result_integer(pConn, "select TO_UNIXTIMESTAMP(c1) from db1.ntb", 1704121200000); // use timezone in server UTC-8 + check_sql_result_integer(pConn, "select TO_UNIXTIMESTAMP('2024-01-01T23:00:00.000+0200')", 1704142800000); + check_sql_result_integer(pConn, "select TO_UNIXTIMESTAMP('2024-01-01T13:00:00.000-08')", 1704142800000); + check_sql_result_integer(pConn, "select TO_UNIXTIMESTAMP('2024-01-01T23:00:00.001')", 1704142800001); + + // TO_TIMESTAMP + check_sql_result_integer(pConn, "select TO_TIMESTAMP(c1,'yyyy-mm-dd hh24:mi:ss') from db1.ntb", 1704121200000); // use timezone in server UTC-8 + check_sql_result_integer(pConn, "select TO_TIMESTAMP('2024-01-01 23:00:00+02:00', 'yyyy-mm-dd hh24:mi:ss tzh')", 1704142800000); + check_sql_result_integer(pConn, "select TO_TIMESTAMP('2024-01-01T13:00:00-08', 'yyyy-mm-ddThh24:mi:ss tzh')", 1704142800000); + check_sql_result_integer(pConn, "select TO_TIMESTAMP('2024/01/01 23:00:00', 'yyyy/mm/dd hh24:mi:ss')", 1704142800000); + + // TO_CHAR + check_sql_result(pConn, "select TO_CHAR(ts,'yyyy-mm-dd hh24:mi:ss') from db1.ntb", "2024-01-02 05:00:00"); // use timezone in server UTC-8 + check_sql_result(pConn, "select TO_CHAR(cast(1704142800000 as timestamp), 'yyyy-mm-dd hh24:mi:ss tzh')", "2024-01-01 23:00:00 +02"); + check_sql_result(pConn, "select TO_CHAR(cast(1704142800000 as timestamp), 'yyyy-mm-dd hh24:mi:ss')", "2024-01-01 23:00:00"); + + // TIMEDIFF + check_sql_result_integer(pConn, "select TIMEDIFF(c1, '2024-01-01T23:00:00.001+02') from db1.ntb", -21600001); // use timezone in server UTC-8 + check_sql_result_integer(pConn, "select TIMEDIFF(c1, '2024-01-01T23:00:00.001') from db1.ntb", -1); // use timezone in server UTC-8 + check_sql_result_integer(pConn, "select TIMEDIFF('2024-01-01T23:00:00.001', '2024-01-01T13:00:00.000-08')", 1); + + // CAST + check_sql_result_integer(pConn, "select CAST(c1 as timestamp) from db1.ntb", 1704121200000); + check_sql_result_integer(pConn, "select CAST('2024-01-01T23:00:00.000+02' as timestamp)", 1704142800000); + check_sql_result_integer(pConn, "select CAST('2024-01-01T23:00:00.000' as timestamp)", 1704142800000); + + taos_close(pConn); + + // hash join + pConn = getConnWithOption("UTC+1"); + + execQuery(pConn, "drop database if exists db1"); + execQuery(pConn, "create database db1"); + execQuery(pConn, "create table db1.ntb (ts timestamp, c1 binary(32), c2 int)"); + execQuery(pConn, "create table db1.ntb1 (ts timestamp, c1 binary(32), c2 int)"); + execQuery(pConn, "insert into db1.ntb values(1703987400000, '2023-12-31 00:50:00', 1)"); // 2023-12-31 00:50:00-0100 + execQuery(pConn, "insert into db1.ntb1 values(1704070200000, '2023-12-31 23:50:00', 11)"); // 2023-12-31 23:50:00-0100 + checkRows(pConn, "select a.ts,b.ts from db1.ntb a join db1.ntb1 b on timetruncate(a.ts, 1d) = timetruncate(b.ts, 1d)", 1); + + taos_close(pConn); + +} + +time_t time_winter = 1731323281; // 2024-11-11 19:08:01+0800 +time_t time_summer = 1731323281 - 120 * 24 * 60 * 60; + +struct test_times +{ + const char *name; + time_t t; + const char *timezone; +} test_tz[] = { + {"", time_winter, " (UTC, +0000)"}, + {"America/New_York", time_winter, "America/New_York (EST, -0500)"}, // 2024-11-11 19:08:01+0800 + {"America/New_York", time_summer, "America/New_York (EDT, -0400)"}, + {"Asia/Kolkata", time_winter, "Asia/Kolkata (IST, +0530)"}, + {"Asia/Shanghai", time_winter, "Asia/Shanghai (CST, +0800)"}, + {"Europe/London", time_winter, "Europe/London (GMT, +0000)"}, + {"Europe/London", time_summer, "Europe/London (BST, +0100)"} +}; + +void timezone_str_test(const char* tz, time_t t, const char* tzStr) { + int code = setenv("TZ", tz, 1); + ASSERT(-1 != code); + tzset(); + + char str1[TD_TIMEZONE_LEN] = {0}; + ASSERT(taosFormatTimezoneStr(t, tz, NULL, str1) == 0); + ASSERT_STREQ(str1, tzStr); +} + +void timezone_rz_str_test(const char* tz, time_t t, const char* tzStr) { + timezone_t sp = tzalloc(tz); + ASSERT(sp); + + char str1[TD_TIMEZONE_LEN] = {0}; + ASSERT(taosFormatTimezoneStr(t, tz, sp, str1) == 0); + ASSERT_STREQ(str1, tzStr); + tzfree(sp); +} + +TEST(timezoneCase, format_timezone_Test) { + for (unsigned int i = 0; i < sizeof (test_tz) / sizeof (test_tz[0]); ++i){ + timezone_str_test(test_tz[i].name, test_tz[i].t, test_tz[i].timezone); + timezone_str_test(test_tz[i].name, test_tz[i].t, test_tz[i].timezone); + } +} + +TEST(timezoneCase, get_tz_Test) { + { + char tz[TD_TIMEZONE_LEN] = {0}; + getTimezoneStr(tz); + ASSERT_STREQ(tz, "Asia/Shanghai"); + +// getTimezoneStr(tz); +// ASSERT_STREQ(tz, "Asia/Shanghai"); +// +// getTimezoneStr(tz); +// ASSERT_STREQ(tz, "n/a"); + } +} + +struct { + const char * env; + time_t expected; +} test_mk[] = { + {"MST", 832935315}, + {"", 832910115}, + {":UTC", 832910115}, + {"UTC", 832910115}, + {"UTC0", 832910115} +}; + + +TEST(timezoneCase, mktime_Test){ + struct tm tm; + time_t t; + + memset (&tm, 0, sizeof (tm)); + tm.tm_isdst = 0; + tm.tm_year = 96; /* years since 1900 */ + tm.tm_mon = 4; + tm.tm_mday = 24; + tm.tm_hour = 3; + tm.tm_min = 55; + tm.tm_sec = 15; + + for (unsigned int i = 0; i < sizeof (test_mk) / sizeof (test_mk[0]); ++i) + { + setenv ("TZ", test_mk[i].env, 1); + t = taosMktime (&tm, NULL); + ASSERT (t == test_mk[i].expected); + } +} + +TEST(timezoneCase, mktime_rz_Test){ + struct tm tm; + time_t t; + + memset (&tm, 0, sizeof (tm)); + tm.tm_isdst = 0; + tm.tm_year = 96; /* years since 1900 */ + tm.tm_mon = 4; + tm.tm_mday = 24; + tm.tm_hour = 3; + tm.tm_min = 55; + tm.tm_sec = 15; + + for (unsigned int i = 0; i < sizeof (test_mk) / sizeof (test_mk[0]); ++i) + { + timezone_t tz = tzalloc(test_mk[i].env); + ASSERT(tz); + t = taosMktime(&tm, tz); + ASSERT (t == test_mk[i].expected); + tzfree(tz); + } +} + +TEST(timezoneCase, localtime_performance_Test) { + timezone_t sp = tzalloc("Asia/Shanghai"); + ASSERT(sp); + + int cnt = 10000000; + int times = 10; + int64_t time_localtime = 0; + int64_t time_localtime_rz = 0; +// int cnt = 1000000; + for (int i = 0; i < times; ++i) { + int64_t t1 = taosGetTimestampNs(); + for (int j = 0; j < cnt; ++j) { + time_t t = time_winter - j; + struct tm tm1; + ASSERT (taosLocalTime(&t, &tm1, NULL, 0, NULL)); + } + int64_t tmp = taosGetTimestampNs() - t1; + printf("localtime cost:%" PRId64 " ns, run %d times", tmp, cnt); + time_localtime += tmp/cnt; + + printf("\n"); + + + + int64_t t2 = taosGetTimestampNs(); + for (int j = 0; j < cnt; ++j) { + time_t t = time_winter - j; + struct tm tm1; + ASSERT (taosLocalTime(&t, &tm1, NULL, 0, sp)); + } + tmp = taosGetTimestampNs() - t2; + printf("localtime_rz cost:%" PRId64 " ns, run %d times", tmp, cnt); + time_localtime_rz += tmp/cnt; + printf("\n\n"); + } + printf("average: localtime cost:%" PRId64 " ns, localtime_rz cost:%" PRId64 " ns", time_localtime/times, time_localtime_rz/times); + tzfree(sp); +} + + +#pragma GCC diagnostic pop diff --git a/source/os/src/timezone/localtime.c b/source/os/src/timezone/localtime.c index ea2a6fb8a9..e6d79d05d9 100644 --- a/source/os/src/timezone/localtime.c +++ b/source/os/src/timezone/localtime.c @@ -1770,7 +1770,7 @@ timesub(const time_t *timep, int_fast32_t offset, dayoff = offset / SECSPERDAY - corr / SECSPERDAY + rem / SECSPERDAY - 3; rem %= SECSPERDAY; /* y = (EPOCH_YEAR - + floor((tdays + dayoff) / DAYSPERREPEAT) * YEARSPERREPEAT), + + floor((tdays + dayoff) / DAYSPERREPEAT) * YEARSPERREPEAT), sans overflow. But calculate against 1570 (EPOCH_YEAR - YEARSPERREPEAT) instead of against 1970 so that things work for localtime values before 1970 when time_t is unsigned. */ diff --git a/source/os/src/timezone/private.h b/source/os/src/timezone/private.h index bdd0001395..fc0b875545 100644 --- a/source/os/src/timezone/private.h +++ b/source/os/src/timezone/private.h @@ -897,7 +897,7 @@ ATTRIBUTE_PURE time_t time2posix_z(timezone_t, time_t); default: TIME_T_MAX_NO_PADDING) \ : (time_t) -1) enum { SIGNED_PADDING_CHECK_NEEDED - = _Generic((time_t) 0, + = _Generic((time_t) 0, signed char: false, short: false, int: false, long: false, long long: false, default: true) };