From 6f55cb4b7c4538ac910920fe992392f5e7d9228d Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Fri, 15 Sep 2023 20:24:59 +0200 Subject: [PATCH] MDEV-31684 post-review changes --- mysql-test/main/date_formats.result | 16 ++--- mysql-test/main/date_formats.test | 19 ++---- mysql-test/main/timezone.result | 11 +++ mysql-test/main/timezone.test | 9 +++ sql/item_timefunc.cc | 63 +++++++----------- sql/tztime.cc | 100 ++++++++++++---------------- sql/tztime.h | 21 +++--- 7 files changed, 105 insertions(+), 134 deletions(-) diff --git a/mysql-test/main/date_formats.result b/mysql-test/main/date_formats.result index d26c07d7c96..277ba946e2b 100644 --- a/mysql-test/main/date_formats.result +++ b/mysql-test/main/date_formats.result @@ -556,9 +556,10 @@ select time_format('2001-01-01 02:02:02', '%T'); time_format('2001-01-01 02:02:02', '%T') 02:02:02 # -# Beginning of 11.3 test +# End of 10.2 test # -# MDEV-31684: Add timezone information to DATE_FORMAT +# +# MDEV-31684 Add timezone information to DATE_FORMAT # SET @old_timezone= @@time_zone; # Using named timezones @@ -590,14 +591,7 @@ SET TIME_ZONE='UTC'; SELECT DATE_FORMAT('2009-10-04 22:23:00', '%z %Z') AS current_timezone; current_timezone +0000 UTC -# using system time -# -# output depends on system time information. Hence replacing -# to avoid result diff test failures. -# -SET @@time_zone= default; -SELECT DATE_FORMAT('2009-10+|-HH:MM ABC22:23:00', '%z %Z') AS current_timezone; -current_timezone -+|-HH:MM ABC SET @@time_zone= @old_timezone; +# # End of 11.3 test +# diff --git a/mysql-test/main/date_formats.test b/mysql-test/main/date_formats.test index 08a1fa493ce..0ded67e985f 100644 --- a/mysql-test/main/date_formats.test +++ b/mysql-test/main/date_formats.test @@ -258,9 +258,11 @@ select time_format('01 02:02:02', '%T'); select time_format('2001-01-01 02:02:02', '%T'); --echo # ---echo # Beginning of 11.3 test +--echo # End of 10.2 test --echo # ---echo # MDEV-31684: Add timezone information to DATE_FORMAT + +--echo # +--echo # MDEV-31684 Add timezone information to DATE_FORMAT --echo # SET @old_timezone= @@time_zone; @@ -290,17 +292,8 @@ SELECT DATE_FORMAT('2009-10-04 22:23:00', '%z %Z') AS current_timezone; SET TIME_ZONE='UTC'; SELECT DATE_FORMAT('2009-10-04 22:23:00', '%z %Z') AS current_timezone; ---echo # using system time ---echo # ---echo # output depends on system time information. Hence replacing ---echo # to avoid result diff test failures. ---echo # - -SET @@time_zone= default; - ---replace_regex /[+-][0-9]* [A-Z]*/+|-HH:MM ABC/ -SELECT DATE_FORMAT('2009-10-04 22:23:00', '%z %Z') AS current_timezone; - SET @@time_zone= @old_timezone; +--echo # --echo # End of 11.3 test +--echo # diff --git a/mysql-test/main/timezone.result b/mysql-test/main/timezone.result index 2ba1e3fb1d5..2a099e90bad 100644 --- a/mysql-test/main/timezone.result +++ b/mysql-test/main/timezone.result @@ -75,3 +75,14 @@ alter table mysql.time_zone_transition_type add primary key (time_zone_id,transi # # End of 10.8 tests # +# +# MDEV-31684 Add timezone information to DATE_FORMAT +# +# using system time +SET @@time_zone= default; +SELECT DATE_FORMAT('2009-11-01 22:23:00', '%z %Z') AS current_timezone; +current_timezone ++0100 MET +SELECT DATE_FORMAT('2008-06-04 02:23:00', '%z %Z') AS current_timezone; +current_timezone ++0200 MEST diff --git a/mysql-test/main/timezone.test b/mysql-test/main/timezone.test index 89c3ab5cfc1..50e062b45f1 100644 --- a/mysql-test/main/timezone.test +++ b/mysql-test/main/timezone.test @@ -74,3 +74,12 @@ alter table mysql.time_zone_transition_type add primary key (time_zone_id,transi --echo # --echo # End of 10.8 tests --echo # + +--echo # +--echo # MDEV-31684 Add timezone information to DATE_FORMAT +--echo # + +--echo # using system time +SET @@time_zone= default; +SELECT DATE_FORMAT('2009-11-01 22:23:00', '%z %Z') AS current_timezone; +SELECT DATE_FORMAT('2008-06-04 02:23:00', '%z %Z') AS current_timezone; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 7c2b7df697e..757c0a5c5bd 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -482,15 +482,8 @@ static bool make_date_time(THD *thd, const String *format, uint weekday; ulong length; const char *ptr, *end; - int diff_hr=0, diff_min=0; - char abbrevation[8]; - struct tz tmp; - tmp.is_inited= false; - - Time_zone* curr_timezone= my_tz_find(thd, - thd->variables.time_zone->get_name()); - memset(abbrevation, 0, sizeof(abbrevation)); - + struct tz curr_tz; + Time_zone* curr_timezone= 0; str->length(0); @@ -710,41 +703,29 @@ static bool make_date_time(THD *thd, const String *format, case 'z': { - if (!tmp.is_inited) + if (!curr_timezone) { - curr_timezone->get_timezone_information(&tmp, l_time); - tmp.is_inited= true; + curr_timezone= thd->variables.time_zone; + curr_timezone->get_timezone_information(&curr_tz, l_time); } - ulonglong seconds= abs(tmp.seconds_offset); - diff_hr= (int)(seconds/3600L); - int temp= (int)(seconds%3600L); - diff_min= temp/60L; + long minutes= labs(curr_tz.seconds_offset)/60, diff_hr, diff_min; + diff_hr= minutes/60; + diff_min= minutes%60; - if (tmp.is_behind) - str->append("-", 1); - else - str->append("+", 1); - - if (diff_hr/10 == 0) - str->append("0", 1); - length= (uint) (int10_to_str(diff_hr, intbuff, 10) - intbuff); - str->append(intbuff, length); - if (diff_min/10 == 0) - str->append("0", 1); - length= (uint) (int10_to_str(diff_min, intbuff, 10) - intbuff); - str->append(intbuff, length); - } + str->append(curr_tz.seconds_offset < 0 ? '-' : '+'); + str->append(static_cast('0' + diff_hr/10)); + str->append(static_cast('0' + diff_hr%10)); + str->append(static_cast('0' + diff_min/10)); + str->append(static_cast('0' + diff_min%10)); break; - - case 'Z': - { - if (!tmp.is_inited) - { - curr_timezone->get_timezone_information(&tmp, l_time); - tmp.is_inited= true; - } - str->append(tmp.abbrevation, strlen(tmp.abbrevation)); } + case 'Z': + if (!curr_timezone) + { + curr_timezone= thd->variables.time_zone; + curr_timezone->get_timezone_information(&curr_tz, l_time); + } + str->append(curr_tz.abbrevation, strlen(curr_tz.abbrevation)); break; default: str->append(*ptr); @@ -1874,6 +1855,7 @@ uint Item_func_date_format::format_length(const String *format) case 'X': /* Year, used with 'v, where week starts with Monday' */ size += 4; break; + case 'Z': /* time zone abbreviation */ case 'a': /* locale's abbreviated weekday name (Sun..Sat) */ case 'b': /* locale's abbreviated month name (Jan.Dec) */ size += 32; /* large for UTF8 locale data */ @@ -1912,6 +1894,9 @@ uint Item_func_date_format::format_length(const String *format) case 'f': /* microseconds */ size += 6; break; + case 'z': /* time zone offset */ + size += 5; + break; case 'w': /* day (of the week), numeric */ case '%': default: diff --git a/sql/tztime.cc b/sql/tztime.cc index b18457ff5c0..9a1f8c6fc52 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -52,23 +52,12 @@ #include #include "lock.h" // MYSQL_LOCK_IGNORE_FLUSH, // MYSQL_LOCK_IGNORE_TIMEOUT - -/* - Now we don't use abbreviations in server but we will do this in future. - Edit: Started needing abbrevation in server as part of task MDEV-31684. -*/ -#ifndef ABBR_ARE_USED -#define ABBR_ARE_USED -#endif /* defined(TZINFO2SQL) || defined(TESTTIME) */ - /* Structure describing local time type (e.g. Moscow summer time (MSD)) */ typedef struct ttinfo { long tt_gmtoff; // Offset from UTC in seconds uint tt_isdst; // Is daylight saving time or not. Used to set tm_isdst -#ifdef ABBR_ARE_USED uint tt_abbrind; // Index of start of abbreviation for this time type. -#endif /* We don't use tt_ttisstd and tt_ttisgmt members of original elsie-code struct since we don't support POSIX-style TZ descriptions in variables. @@ -115,10 +104,8 @@ typedef struct st_time_zone_info my_time_t *ats; // Times of transitions between time types uchar *types; // Local time types for transitions TRAN_TYPE_INFO *ttis; // Local time types descriptions -#ifdef ABBR_ARE_USED /* Storage for local time types abbreviations. They are stored as ASCIIZ */ char *chars; -#endif /* Leap seconds corrections descriptions, this array is shared by all time zones who use leap seconds. @@ -174,9 +161,7 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage) struct tzhead tzhead; uchar buf[sizeof(struct tzhead) + sizeof(my_time_t) * TZ_MAX_TIMES + TZ_MAX_TIMES + sizeof(TRAN_TYPE_INFO) * TZ_MAX_TYPES + -#ifdef ABBR_ARE_USED MY_MAX(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1))) + -#endif sizeof(LS_INFO) * TZ_MAX_LEAPS]; } u; uint ttisstdcnt; @@ -221,9 +206,7 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage) ALIGN_SIZE(sp->timecnt) + ALIGN_SIZE(sp->typecnt * sizeof(TRAN_TYPE_INFO)) + -#ifdef ABBR_ARE_USED ALIGN_SIZE(sp->charcnt+1) + -#endif sp->leapcnt * sizeof(LS_INFO)))) return 1; @@ -233,10 +216,8 @@ tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage) tzinfo_buf+= ALIGN_SIZE(sp->timecnt); sp->ttis= (TRAN_TYPE_INFO *)tzinfo_buf; tzinfo_buf+= ALIGN_SIZE(sp->typecnt * sizeof(TRAN_TYPE_INFO)); -#ifdef ABBR_ARE_USED sp->chars= tzinfo_buf; tzinfo_buf+= ALIGN_SIZE(sp->charcnt+1); -#endif sp->lsis= (LS_INFO *)tzinfo_buf; for (i= 0; i < sp->timecnt; i++, p+= 4) @@ -1096,20 +1077,45 @@ Time_zone_system::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const void Time_zone_system::get_timezone_information(struct tz* curr_tz, const MYSQL_TIME *local_TIME) const { +#ifndef _WIN32 uint error; struct tm tm_local_time; - - time_t time_sec= this->TIME_to_gmt_sec(local_TIME, &error); - + time_t time_sec= TIME_to_gmt_sec(local_TIME, &error); localtime_r(&time_sec, &tm_local_time); -# ifdef __USE_MISC - int len= strlen((tm_local_time.tm_zone)); - strmake(curr_tz->abbrevation, tm_local_time.tm_zone, sizeof(tm_local_time.tm_zone)-1); - curr_tz->abbrevation[len]= '\0'; -# endif - curr_tz->seconds_offset= tm_local_time.tm_gmtoff; - curr_tz->is_behind= tm_local_time.tm_gmtoff < 0; - + strmake_buf(curr_tz->abbrevation, tm_local_time.tm_zone); + curr_tz->seconds_offset= tm_local_time.tm_gmtoff; +#else +#define mdHMS(mon,day,h,m,s) (((((mon)*100+(day))*100+(h))*100+(m))*100+(s)) + TIME_ZONE_INFORMATION tzi; + bool use_dst= false; + GetTimeZoneInformationForYear(local_TIME->year, NULL, &tzi); + if (tzi.StandardDate.wMonth) + { + ulonglong std= mdHMS(tzi.StandardDate.wMonth, tzi.StandardDate.wDay, + tzi.StandardDate.wHour, tzi.StandardDate.wMinute, + tzi.StandardDate.wSecond); + ulonglong dst= mdHMS(tzi.DaylightDate.wMonth, tzi.DaylightDate.wDay, + tzi.DaylightDate.wHour, tzi.DaylightDate.wMinute, + tzi.DaylightDate.wSecond); + ulonglong cur= mdHMS(local_TIME->month, local_TIME->day, local_TIME->hour, + local_TIME->minute, local_TIME->second); + use_dst= std < dst ? cur < std || cur >= dst : cur >= dst && cur < std; + } + if (use_dst) + { + size_t len; + curr_tz->seconds_offset= -60 * (tzi.Bias + tzi.DaylightBias); + wcstombs_s(&len, curr_tz->abbrevation, sizeof(curr_tz->abbrevation), + tzi.DaylightName, _TRUNCATE); + } + else + { + size_t len; + curr_tz->seconds_offset= -60 * (tzi.Bias + tzi.StandardBias); + wcstombs_s(&len, curr_tz->abbrevation, sizeof(curr_tz->abbrevation), + tzi.StandardName, _TRUNCATE); + } +#endif } @@ -1194,17 +1200,8 @@ Time_zone_utc::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const void Time_zone_utc::get_timezone_information(struct tz* curr_tz, const MYSQL_TIME *local_TIME) const { - uint error; - struct tm tm_local_time; - - time_t time_sec= this->TIME_to_gmt_sec(local_TIME, &error); - - localtime_r(&time_sec, &tm_local_time); -# ifdef __USE_MISC - strmake(curr_tz->abbrevation, "UTC", 3); -# endif - curr_tz->seconds_offset= tm_local_time.tm_gmtoff; - curr_tz->is_behind= tm_local_time.tm_gmtoff < 0; + strmake_buf(curr_tz->abbrevation, "UTC"); + curr_tz->seconds_offset= 0; } @@ -1321,14 +1318,12 @@ Time_zone_db::get_timezone_information(struct tz* curr_tz, const MYSQL_TIME *loc const TRAN_TYPE_INFO *ttisp; /* Get seconds since epoch. */ - sec_in_utc= this->TIME_to_gmt_sec(local_TIME, &error); + sec_in_utc= TIME_to_gmt_sec(local_TIME, &error); /* Get local timezone information. */ ttisp= find_transition_type(sec_in_utc, tz_info); curr_tz->seconds_offset= ttisp->tt_gmtoff; - curr_tz->is_behind= tz_info->revtis->rt_offset < 0 ? true : false; - - strmake(curr_tz->abbrevation, &(tz_info->chars[ttisp->tt_abbrind]), tz_info->charcnt-1); + strmake_buf(curr_tz->abbrevation, &(tz_info->chars[ttisp->tt_abbrind])); } @@ -1491,8 +1486,7 @@ Time_zone_offset::get_timezone_information(struct tz* curr_tz, const MYSQL_TIME { curr_tz->seconds_offset= offset; const char *name= get_name()->ptr(); - curr_tz->is_behind= name[0] == '+' ? false : true; - strmake(curr_tz->abbrevation, name, sizeof(curr_tz->abbrevation)-1); + strmake_buf(curr_tz->abbrevation, name); } @@ -1933,9 +1927,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) my_time_t ats[TZ_MAX_TIMES]; uchar types[TZ_MAX_TIMES]; TRAN_TYPE_INFO ttis[TZ_MAX_TYPES]; -#ifdef ABBR_ARE_USED char chars[MY_MAX(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))]; -#endif /* Used as a temporary tz_info until we decide that we actually want to allocate and keep the tz info and tz name in tz_storage. @@ -2041,7 +2033,6 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) ttis[ttid].tt_gmtoff= (long)table->field[2]->val_int(); ttis[ttid].tt_isdst= (table->field[3]->val_int() > 0); -#ifdef ABBR_ARE_USED // FIXME should we do something with duplicates here ? table->field[4]->val_str(&abbr, &abbr); if (tmp_tz_info.charcnt + abbr.length() + 1 > sizeof(chars)) @@ -2061,11 +2052,6 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld " "abbr='%s' tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff, chars + ttis[ttid].tt_abbrind, ttis[ttid].tt_isdst)); -#else - DBUG_PRINT("info", - ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld " - "tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff, ttis[ttid].tt_isdst)); -#endif /* ttid is increasing because we are reading using index */ if (ttid < tmp_tz_info.typecnt) @@ -2181,9 +2167,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) ALIGN_SIZE(sizeof(my_time_t) * tz_info->timecnt) + ALIGN_SIZE(tz_info->timecnt) + -#ifdef ABBR_ARE_USED ALIGN_SIZE(tz_info->charcnt) + -#endif sizeof(TRAN_TYPE_INFO) * tz_info->typecnt))) { @@ -2197,11 +2181,9 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) tz_info->types= (uchar *)alloc_buff; memcpy(tz_info->types, types, tz_info->timecnt); alloc_buff+= ALIGN_SIZE(tz_info->timecnt); -#ifdef ABBR_ARE_USED tz_info->chars= alloc_buff; memcpy(tz_info->chars, chars, tz_info->charcnt); alloc_buff+= ALIGN_SIZE(tz_info->charcnt); -#endif tz_info->ttis= (TRAN_TYPE_INFO *)alloc_buff; memcpy(tz_info->ttis, ttis, tz_info->typecnt * sizeof(TRAN_TYPE_INFO)); diff --git a/sql/tztime.h b/sql/tztime.h index 75f8a7b3ef2..3dabbe8d118 100644 --- a/sql/tztime.h +++ b/sql/tztime.h @@ -32,25 +32,22 @@ class THD; class THD; -/** - This class represents abstract time zone and provides - basic interface for MYSQL_TIME <-> my_time_t conversion. - Actual time zones which are specified by DB, or via offset - or use system functions are its descendants. -*/ - /* - Has only offset from UTC, bool value to denote if it is - ahead (+), behind(-) of UTC and abbrevation. + Has only offset from UTC and abbrevation. */ struct tz { long seconds_offset; - bool is_behind; - char abbrevation[8]; - bool is_inited; + char abbrevation[64]; }; +/** + This class represents abstract time zone and provides + basic interface for MYSQL_TIME <-> my_time_t conversion. + Actual time zones which are specified by DB, or via offset + or use system functions are its descendants. +*/ + class Time_zone: public Sql_alloc { public: