diff options
Diffstat (limited to 'unittest/mysys')
-rw-r--r-- | unittest/mysys/CMakeLists.txt | 2 | ||||
-rw-r--r-- | unittest/mysys/my_tzinfo-t.c | 197 |
2 files changed, 198 insertions, 1 deletions
diff --git a/unittest/mysys/CMakeLists.txt b/unittest/mysys/CMakeLists.txt index 9dd5e5db..03c92e8b 100644 --- a/unittest/mysys/CMakeLists.txt +++ b/unittest/mysys/CMakeLists.txt @@ -14,7 +14,7 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA MY_ADD_TESTS(bitmap base64 my_atomic my_rdtsc lf my_malloc my_getopt dynstring - byte_order + byte_order my_tzinfo queues stacktrace crc32 LINK_LIBRARIES mysys) MY_ADD_TESTS(my_vsnprintf LINK_LIBRARIES strings mysys) MY_ADD_TESTS(aes LINK_LIBRARIES mysys mysys_ssl) diff --git a/unittest/mysys/my_tzinfo-t.c b/unittest/mysys/my_tzinfo-t.c new file mode 100644 index 00000000..b38ebd37 --- /dev/null +++ b/unittest/mysys/my_tzinfo-t.c @@ -0,0 +1,197 @@ +/* + Copyright (c) 2023, MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + 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. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + */ + +/* + This file tests my_tzset/my_get_tzinfo APIs +*/ +#include <my_global.h> +#include <my_sys.h> +#include "tap.h" +#include <stdlib.h> + +/** + Seconds since epoch used for "summer" timestamp + Corresponds to Jul 22 2023 04:26:40 GMT + Used to test timezone daylight savings UTC offset and DST abbreviation +*/ +#define SUMMER_TIMESTAMP 1690000000 + +/** + Seconds since epoch used for "winter" timestamp + Corresponds to Nov 14 2023 22:13:20 GMT+ + Used to test standard (no daylight savings) UTC offset and abbreviation +*/ +#define WINTER_TIMESTAMP 1700000000 + +#ifdef _WIN32 +#define timegm _mkgmtime +#endif +/** + Check expected offset from UTC, corresponding to specific + timestamp. + + On Windows, it is possible that my_get_tzinfo() is using + ICU to calculate offset.This function rechecks that value is the + same when using C runtime's _mkgmtime(). + + Elsewhere, my_get_tzinfo is taking this value from non-standard glibc + extension struct tm::tm_gmtoff. This function rechecks that the value + is the same if calculated with timegm(). +*/ +static void check_utc_offset(time_t t, long expected, const char *comment) +{ +#if defined _WIN32 || defined __linux__ + struct tm local_time; + long offset; + localtime_r(&t, &local_time); + offset= (long) (timegm(&local_time) - t); + ok(offset == expected, "%s: Offset for timestamp %lld is %ld/%ld", comment, + (long long) t,expected, offset); +#else + skip(1, "no utc offset check"); +#endif +} + +/** + Test my_tzset/my_get_tzinfo functions for a single named timezone. + + @param[in] tz_env. Timezone name, as used for TZ env.variable + + @param[in] expected_tznames. Expected "sytem_timezone" names. + For example, expected names for timezone PST8PDT can + be PST8PDT, PST or PDT + + @param[in] summer_gmt_off. Expected UTC offset, for SUMMER_TIMESTAMP + For timezones with DST savings on northern hemisphere + it is the expected DST offset from UTC + + @param[in] summer_time_abbr . Expected standard abbreviation + corresponding to SUMMER_TIMESTAMP. For example, it is + "PDT" for timezone PST8PDT + + @param[in] winter_gmt_off. Expected UTC offset, for WINTER_TIMESTAMP + + @param[in] winter_time_abbr . Expected standard abbreviation + corresponding to WINTER_TIMESTAMP. For example, it is + "PST" for timezone PST8PDT. +*/ +void test_timezone(const char *tz_env, const char **expected_tznames, + long summer_gmt_off, const char *summer_time_abbr, + long winter_gmt_off, const char *winter_time_abbr) +{ + char timezone_name[64]; + int found; + struct my_tz tz; + + setenv("TZ", tz_env, 1); + my_tzset(); + my_tzname(timezone_name, sizeof(timezone_name)); + + /* Check expected timezone names. */ + found= 0; + for (int i= 0; expected_tznames[i]; i++) + { + if (!strcmp(expected_tznames[i], timezone_name)) + { + found= 1; + break; + } + } + ok(found, "%s: timezone_name = %s", tz_env, timezone_name); + my_tzinfo(SUMMER_TIMESTAMP, &tz); + ok(summer_gmt_off == tz.seconds_offset, "%s: Summer GMT offset %ld", tz_env, tz.seconds_offset); + check_utc_offset(SUMMER_TIMESTAMP,tz.seconds_offset, tz_env); + + ok(!strcmp(summer_time_abbr, tz.abbreviation), "%s: Summer time abbreviation %s", + tz_env, tz.abbreviation); + my_tzinfo(WINTER_TIMESTAMP, &tz); + ok(winter_gmt_off == tz.seconds_offset, "%s: Winter GMT offset %ld", tz_env, tz.seconds_offset); + check_utc_offset(WINTER_TIMESTAMP, tz.seconds_offset, tz_env); + ok(!strcmp(winter_time_abbr, tz.abbreviation), "%s: Winter time abbreviation %s", + tz_env, tz.abbreviation); +} + +/* Check default timezone.*/ +static void test_default_timezone() +{ + char timezone_name[64]; + + time_t timestamps[]= {SUMMER_TIMESTAMP, WINTER_TIMESTAMP, time(NULL)}; + size_t i; + struct my_tz tz; +#ifdef _WIN32 + (void) putenv("TZ="); +#else + unsetenv("TZ"); +#endif + + my_tzset(); + my_tzname(timezone_name, sizeof(timezone_name)); +#ifdef _WIN32 + /* Expect timezone name like Europe/Berlin */ + ok(strstr(timezone_name, "/") != NULL, "Default timezone name %s", + timezone_name); +#else + skip(1, "no test for default timezone name %s", timezone_name); +#endif + + for (i = 0; i < array_elements(timestamps); i++) + { + my_tzinfo(timestamps[i], &tz); + ok(tz.seconds_offset % 60 == 0, + "GMT offset is whole number of minutes %ld", tz.seconds_offset); + check_utc_offset(timestamps[i], tz.seconds_offset, timezone_name); + ok(strlen(tz.abbreviation) < 8, "tz abbreviation %s", tz.abbreviation); + } +} + +int main(int argc __attribute__((unused)), char *argv[]) +{ + const char *PST8PDT_names[]= {"PST", "PDT", "PST8PDT", NULL}; + const char *GMT_names[]= {"GMT", "Etc/UTC", NULL}; + const char *GST_minus1GDT_names[]= {"GST", "GDT", NULL}; + const char *IST_names[]= {"IST",NULL}; + MY_INIT(argv[0]); + + plan(38); + test_default_timezone(); + + /* + Test PST8PDT timezone + Standard timezone, supported everywhere. Note - this one is supported by + ICU, so it would be using ICU for calculation on Windows + */ + test_timezone("PST8PDT", PST8PDT_names, -25200, "PDT", -28800, "PST"); + + /* + Test GMT. Supported by ICU, would be using ICU for calculations + */ + test_timezone("GMT", GMT_names, 0, "GMT", 0, "GMT"); + + /* + Non-standard "Germany" timezone, taken from Windows tzset() documentation + example. Unsupported by ICU, will be using C runtime on Windows for + abbreviations, and offset calculations. + */ + test_timezone("GST-1GDT", GST_minus1GDT_names, 7200, "GDT", 3600, "GST"); + + /* India */ + test_timezone("IST-5:30", IST_names, 19800, "IST", 19800, "IST"); + + my_end(0); + return exit_status(); +} |