Commit 94eb8192 authored by Rucha Deodhar's avatar Rucha Deodhar

MDEV-31684 Add timezone information to DATE_FORMAT

Before starting to go over the format string, prepare the current time
zone information incase '%z' or '%Z' is encountered.
This information can be obtained as given below:

A) If timezone is not set ( meaning we are working with system timezone):
Get the MYSQL_TIME representation for current time and GMT time using
current thread variable for timezone and timezone variable for UTC
respectively. This MYSQL_TIME variable will be used to calculate time
difference. Also convert current time in second to tm structure to
get system timezone information.

B) If timezone is set as offset:
Get timezone information using current timezone information and store
in appropriate variable.

C) If timezone is set as some place (example: Europe/Berlin)
Get timezone information by searching the timezone. During internal
timezone search, information like timeoffset from UTC and abbrevation
is stored in another relevant structure. Hence use the same information.
parent 5fc19e71
...@@ -555,3 +555,49 @@ time_format('01 02:02:02', '%T') ...@@ -555,3 +555,49 @@ time_format('01 02:02:02', '%T')
select time_format('2001-01-01 02:02:02', '%T'); select time_format('2001-01-01 02:02:02', '%T');
time_format('2001-01-01 02:02:02', '%T') time_format('2001-01-01 02:02:02', '%T')
02:02:02 02:02:02
#
# Beginning of 11.3 test
#
# MDEV-31684: Add timezone information to DATE_FORMAT
#
SET @old_timezone= @@time_zone;
# Using named timezones
SET TIME_ZONE='Japan';
SELECT DATE_FORMAT('2009-10-04 22:23:00', '%z %Z') AS current_timezone;
current_timezone
+0900 JST
SET @@time_zone='Europe/Moscow';
SELECT DATE_FORMAT('1965-02-17 22:23:00', '%z %Z') AS current_timezone;
current_timezone
+0300 MSK
SELECT DATE_FORMAT('1965-12-31 22:23:00', '%z %Z') AS current_timezone;
current_timezone
+0300 MSK
SELECT DATE_FORMAT('1985-06-01', '%z %Z');
DATE_FORMAT('1985-06-01', '%z %Z')
+0400 MSD
# Using positive and negative offset
SET TIME_ZONE= '-05:30';
SELECT DATE_FORMAT('2009-10-04 22:23:00', '%z %Z') AS current_timezone;
current_timezone
-0530 -05:30
SET TIME_ZONE= '+04:30';
SELECT DATE_FORMAT('2009-10-04 22:23:00', '%z %Z') AS current_timezone;
current_timezone
+0430 +04:30
# Using UTC
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
...@@ -256,3 +256,51 @@ select time_format('2001-01-01 02:02:02', '%d %T'); ...@@ -256,3 +256,51 @@ select time_format('2001-01-01 02:02:02', '%d %T');
select time_format('01 02:02:02', '%d %T'); select time_format('01 02:02:02', '%d %T');
select time_format('01 02:02:02', '%T'); select time_format('01 02:02:02', '%T');
select time_format('2001-01-01 02:02:02', '%T'); select time_format('2001-01-01 02:02:02', '%T');
--echo #
--echo # Beginning of 11.3 test
--echo #
--echo # MDEV-31684: Add timezone information to DATE_FORMAT
--echo #
SET @old_timezone= @@time_zone;
--echo # Using named timezones
SET TIME_ZONE='Japan';
SELECT DATE_FORMAT('2009-10-04 22:23:00', '%z %Z') AS current_timezone;
SET @@time_zone='Europe/Moscow';
SELECT DATE_FORMAT('1965-02-17 22:23:00', '%z %Z') AS current_timezone;
SELECT DATE_FORMAT('1965-12-31 22:23:00', '%z %Z') AS current_timezone;
SELECT DATE_FORMAT('1985-06-01', '%z %Z');
--echo # Using positive and negative offset
SET TIME_ZONE= '-05:30';
SELECT DATE_FORMAT('2009-10-04 22:23:00', '%z %Z') AS current_timezone;
SET TIME_ZONE= '+04:30';
SELECT DATE_FORMAT('2009-10-04 22:23:00', '%z %Z') AS current_timezone;
--echo # Using UTC
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 # End of 11.3 test
...@@ -3,24 +3,24 @@ ...@@ -3,24 +3,24 @@
@@ -449,9 +449,9 @@ @@ -449,9 +449,9 @@
Table Checksum Table Checksum
mysql.roles_mapping 2510045525 mysql.roles_mapping 2510045525
mysql.time_zone_transition 3895294076 mysql.time_zone_transition 3719776009
-mysql.plugin 1587119305 -mysql.plugin 1587119305
+mysql.plugin 2184891911 +mysql.plugin 2184891911
mysql.servers 2079085450 mysql.servers 2079085450
-mysql.func 3241572444 -mysql.func 3241572444
+mysql.func 310494789 +mysql.func 310494789
mysql.innodb_table_stats 347867921 mysql.innodb_table_stats 1285726777
mysql.table_stats 664320059 mysql.table_stats 2836905944
# Opps.... # Opps....
@@ -484,9 +484,9 @@ @@ -484,9 +484,9 @@
Table Checksum Table Checksum
mysql.roles_mapping 2510045525 mysql.roles_mapping 2510045525
mysql.time_zone_transition 3895294076 mysql.time_zone_transition 3719776009
-mysql.plugin 1587119305 -mysql.plugin 1587119305
+mysql.plugin 2184891911 +mysql.plugin 2184891911
mysql.servers 2079085450 mysql.servers 2079085450
-mysql.func 3241572444 -mysql.func 3241572444
+mysql.func 310494789 +mysql.func 310494789
mysql.innodb_table_stats 347867921 mysql.innodb_table_stats 1285726777
mysql.table_stats 664320059 mysql.table_stats 2836905944
DROP FUNCTION IF EXISTS metaphon; DROP FUNCTION IF EXISTS metaphon;
This diff is collapsed.
...@@ -623,7 +623,7 @@ DATA_LENGTH #DL# ...@@ -623,7 +623,7 @@ DATA_LENGTH #DL#
MAX_DATA_LENGTH #MDL# MAX_DATA_LENGTH #MDL#
INDEX_LENGTH #IL# INDEX_LENGTH #IL#
DATA_FREE #DF# DATA_FREE #DF#
AUTO_INCREMENT 6 AUTO_INCREMENT 7
CREATE_TIME #CRT# CREATE_TIME #CRT#
UPDATE_TIME #UT# UPDATE_TIME #UT#
CHECK_TIME #CT# CHECK_TIME #CT#
......
...@@ -623,7 +623,7 @@ DATA_LENGTH #DL# ...@@ -623,7 +623,7 @@ DATA_LENGTH #DL#
MAX_DATA_LENGTH #MDL# MAX_DATA_LENGTH #MDL#
INDEX_LENGTH #IL# INDEX_LENGTH #IL#
DATA_FREE #DF# DATA_FREE #DF#
AUTO_INCREMENT 6 AUTO_INCREMENT 7
CREATE_TIME #CRT# CREATE_TIME #CRT#
UPDATE_TIME #UT# UPDATE_TIME #UT#
CHECK_TIME #CT# CHECK_TIME #CT#
...@@ -1412,7 +1412,7 @@ DATA_LENGTH #DL# ...@@ -1412,7 +1412,7 @@ DATA_LENGTH #DL#
MAX_DATA_LENGTH #MDL# MAX_DATA_LENGTH #MDL#
INDEX_LENGTH #IL# INDEX_LENGTH #IL#
DATA_FREE #DF# DATA_FREE #DF#
AUTO_INCREMENT 6 AUTO_INCREMENT 7
CREATE_TIME #CRT# CREATE_TIME #CRT#
UPDATE_TIME #UT# UPDATE_TIME #UT#
CHECK_TIME #CT# CHECK_TIME #CT#
......
This diff is collapsed.
...@@ -473,15 +473,24 @@ static bool extract_date_time(THD *thd, DATE_TIME_FORMAT *format, ...@@ -473,15 +473,24 @@ static bool extract_date_time(THD *thd, DATE_TIME_FORMAT *format,
Create a formatted date/time value in a string. Create a formatted date/time value in a string.
*/ */
static bool make_date_time(const String *format, const MYSQL_TIME *l_time, static bool make_date_time(THD *thd, const String *format,
timestamp_type type, const MY_LOCALE *locale, const MYSQL_TIME *l_time, timestamp_type type,
String *str) const MY_LOCALE *locale, String *str)
{ {
char intbuff[15]; char intbuff[15];
uint hours_i; uint hours_i;
uint weekday; uint weekday;
ulong length; ulong length;
const char *ptr, *end; 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));
str->length(0); str->length(0);
...@@ -699,6 +708,44 @@ static bool make_date_time(const String *format, const MYSQL_TIME *l_time, ...@@ -699,6 +708,44 @@ static bool make_date_time(const String *format, const MYSQL_TIME *l_time,
str->append_with_prefill(intbuff, length, 1, '0'); str->append_with_prefill(intbuff, length, 1, '0');
break; break;
case 'z':
{
if (!tmp.is_inited)
{
curr_timezone->get_timezone_information(&tmp, l_time);
tmp.is_inited= true;
}
ulonglong seconds= abs(tmp.seconds_offset);
diff_hr= (int)(seconds/3600L);
int temp= (int)(seconds%3600L);
diff_min= temp/60L;
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);
}
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));
}
break;
default: default:
str->append(*ptr); str->append(*ptr);
break; break;
...@@ -1912,7 +1959,7 @@ String *Item_func_date_format::val_str(String *str) ...@@ -1912,7 +1959,7 @@ String *Item_func_date_format::val_str(String *str)
/* Create the result string */ /* Create the result string */
str->set_charset(collation.collation); str->set_charset(collation.collation);
if (!make_date_time(format, &l_time, if (!make_date_time(thd, format, &l_time,
is_time_format ? MYSQL_TIMESTAMP_TIME : is_time_format ? MYSQL_TIMESTAMP_TIME :
MYSQL_TIMESTAMP_DATE, MYSQL_TIMESTAMP_DATE,
lc, str)) lc, str))
......
...@@ -55,15 +55,10 @@ ...@@ -55,15 +55,10 @@
/* /*
Now we don't use abbreviations in server but we will do this in future. 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.
*/ */
#if defined(TZINFO2SQL) || defined(TESTTIME) #ifndef ABBR_ARE_USED
#define ABBR_ARE_USED
#else
#if !defined(DBUG_OFF)
/* Let use abbreviations for debug purposes */
#undef ABBR_ARE_USED
#define ABBR_ARE_USED #define ABBR_ARE_USED
#endif /* !defined(DBUG_OFF) */
#endif /* defined(TZINFO2SQL) || defined(TESTTIME) */ #endif /* defined(TZINFO2SQL) || defined(TESTTIME) */
/* Structure describing local time type (e.g. Moscow summer time (MSD)) */ /* Structure describing local time type (e.g. Moscow summer time (MSD)) */
...@@ -1031,6 +1026,7 @@ class Time_zone_system : public Time_zone ...@@ -1031,6 +1026,7 @@ class Time_zone_system : public Time_zone
virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t, uint *error_code) const; virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t, uint *error_code) const;
virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const; virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
virtual const String * get_name() const; virtual const String * get_name() const;
virtual void get_timezone_information(struct tz* curr_tz, const MYSQL_TIME *local_TIME) const;
}; };
...@@ -1097,6 +1093,25 @@ Time_zone_system::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const ...@@ -1097,6 +1093,25 @@ Time_zone_system::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
adjust_leap_second(tmp); adjust_leap_second(tmp);
} }
void
Time_zone_system::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
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;
}
/* /*
Get name of time zone Get name of time zone
...@@ -1128,6 +1143,7 @@ class Time_zone_utc : public Time_zone ...@@ -1128,6 +1143,7 @@ class Time_zone_utc : public Time_zone
uint *error_code) const; uint *error_code) const;
virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const; virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
virtual const String * get_name() const; virtual const String * get_name() const;
virtual void get_timezone_information(struct tz* curr_tz, const MYSQL_TIME *local_TIME) const;
}; };
...@@ -1175,6 +1191,22 @@ Time_zone_utc::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const ...@@ -1175,6 +1191,22 @@ Time_zone_utc::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
adjust_leap_second(tmp); adjust_leap_second(tmp);
} }
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;
}
/* /*
Get name of time zone Get name of time zone
...@@ -1210,6 +1242,7 @@ class Time_zone_db : public Time_zone ...@@ -1210,6 +1242,7 @@ class Time_zone_db : public Time_zone
virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t, uint *error_code) const; virtual my_time_t TIME_to_gmt_sec(const MYSQL_TIME *t, uint *error_code) const;
virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const; virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
virtual const String * get_name() const; virtual const String * get_name() const;
virtual void get_timezone_information(struct tz* curr_tz, const MYSQL_TIME *local_TIME) const;
private: private:
TIME_ZONE_INFO *tz_info; TIME_ZONE_INFO *tz_info;
const String *tz_name; const String *tz_name;
...@@ -1280,6 +1313,24 @@ Time_zone_db::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const ...@@ -1280,6 +1313,24 @@ Time_zone_db::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const
adjust_leap_second(tmp); adjust_leap_second(tmp);
} }
void
Time_zone_db::get_timezone_information(struct tz* curr_tz, const MYSQL_TIME *local_TIME) const
{
uint error;
my_time_t sec_in_utc;
const TRAN_TYPE_INFO *ttisp;
/* Get seconds since epoch. */
sec_in_utc= this->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);
}
/* /*
Get name of time zone Get name of time zone
...@@ -1309,6 +1360,7 @@ class Time_zone_offset : public Time_zone ...@@ -1309,6 +1360,7 @@ class Time_zone_offset : public Time_zone
uint *error_code) const; uint *error_code) const;
virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const; virtual void gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const;
virtual const String * get_name() const; virtual const String * get_name() const;
virtual void get_timezone_information(struct tz* curr_tz, const MYSQL_TIME *local_TIME) const;
/* /*
This have to be public because we want to be able to access it from This have to be public because we want to be able to access it from
my_offset_tzs_get_key() function my_offset_tzs_get_key() function
...@@ -1434,6 +1486,15 @@ Time_zone_offset::get_name() const ...@@ -1434,6 +1486,15 @@ Time_zone_offset::get_name() const
return &name; return &name;
} }
void
Time_zone_offset::get_timezone_information(struct tz* curr_tz, const MYSQL_TIME *local_TIME) const
{
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);
}
static Time_zone_utc tz_UTC; static Time_zone_utc tz_UTC;
static Time_zone_system tz_SYSTEM; static Time_zone_system tz_SYSTEM;
...@@ -1881,6 +1942,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) ...@@ -1881,6 +1942,7 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables)
*/ */
TIME_ZONE_INFO tmp_tz_info; TIME_ZONE_INFO tmp_tz_info;
memset(&tmp_tz_info, 0, sizeof(TIME_ZONE_INFO)); memset(&tmp_tz_info, 0, sizeof(TIME_ZONE_INFO));
memset(ttis, 0, sizeof(ttis));
DBUG_ENTER("tz_load_from_open_tables"); DBUG_ENTER("tz_load_from_open_tables");
......
...@@ -38,6 +38,19 @@ class THD; ...@@ -38,6 +38,19 @@ class THD;
Actual time zones which are specified by DB, or via offset Actual time zones which are specified by DB, or via offset
or use system functions are its descendants. 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.
*/
struct tz
{
long seconds_offset;
bool is_behind;
char abbrevation[8];
bool is_inited;
};
class Time_zone: public Sql_alloc class Time_zone: public Sql_alloc
{ {
public: public:
...@@ -62,6 +75,8 @@ class Time_zone: public Sql_alloc ...@@ -62,6 +75,8 @@ class Time_zone: public Sql_alloc
*/ */
virtual const String * get_name() const = 0; virtual const String * get_name() const = 0;
virtual void get_timezone_information(struct tz* curr_tz, const MYSQL_TIME *local_TIME) const = 0;
/** /**
We need this only for surpressing warnings, objects of this type are We need this only for surpressing warnings, objects of this type are
allocated on MEM_ROOT and should not require destruction. allocated on MEM_ROOT and should not require destruction.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment