mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
Fix for bug #6387 "Queried timestamp values do not match the inserted
value if server runs in time zone with leap seconds". Now in my_gmt_sec() function we take into account difference between our target and estimation in seconds part. mysql-test/Makefile.am: Added mysql-test/std_data/Moscow_leap reuired by new timezone3.test to source distribution. sql/time.cc: my_gmt_sec(): When comparing our target broken-down datetime t value and proper representation of our estimation *l_time we should take into account that they could differ in second part if we have time zone leap seconds. Also added comments about some assumptions used in this function.
This commit is contained in:
parent
9c06c80dff
commit
d259ba4006
7 changed files with 123 additions and 5 deletions
|
@ -32,6 +32,7 @@ dist-hook:
|
|||
$(INSTALL_DATA) $(srcdir)/r/*.result $(srcdir)/r/*.require $(distdir)/r
|
||||
$(INSTALL_DATA) $(srcdir)/std_data/*.dat $(srcdir)/std_data/*.001 $(distdir)/std_data
|
||||
$(INSTALL_DATA) $(srcdir)/std_data/des_key_file $(distdir)/std_data
|
||||
$(INSTALL_DATA) $(srcdir)/std_data/Moscow_leap $(distdir)/std_data
|
||||
|
||||
install-data-local:
|
||||
$(mkinstalldirs) \
|
||||
|
@ -50,6 +51,7 @@ install-data-local:
|
|||
$(INSTALL_DATA) $(srcdir)/std_data/*.dat $(DESTDIR)$(testdir)/std_data
|
||||
$(INSTALL_DATA) $(srcdir)/std_data/*.*001 $(DESTDIR)$(testdir)/std_data
|
||||
$(INSTALL_DATA) $(srcdir)/std_data/des_key_file $(DESTDIR)$(testdir)/std_data
|
||||
$(INSTALL_DATA) $(srcdir)/std_data/Moscow_leap $(DESTDIR)$(testdir)/std_data
|
||||
|
||||
SUFFIXES = .sh
|
||||
|
||||
|
|
2
mysql-test/r/have_moscow_leap_timezone.require
Normal file
2
mysql-test/r/have_moscow_leap_timezone.require
Normal file
|
@ -0,0 +1,2 @@
|
|||
from_unixtime(1072904422)
|
||||
2004-01-01 00:00:00
|
41
mysql-test/r/timezone3.result
Normal file
41
mysql-test/r/timezone3.result
Normal file
|
@ -0,0 +1,41 @@
|
|||
drop table if exists t1;
|
||||
create table t1 (i int, c varchar(20));
|
||||
insert into t1 values
|
||||
(unix_timestamp("2004-01-01 00:00:00"), "2004-01-01 00:00:00");
|
||||
insert into t1 values
|
||||
(unix_timestamp("2004-03-28 01:59:59"), "2004-03-28 01:59:59"),
|
||||
(unix_timestamp("2004-03-28 02:30:00"), "2004-03-28 02:30:00"),
|
||||
(unix_timestamp("2004-03-28 03:00:00"), "2004-03-28 03:00:00");
|
||||
insert into t1 values
|
||||
(unix_timestamp('2004-05-01 00:00:00'),'2004-05-01 00:00:00');
|
||||
insert into t1 values
|
||||
(unix_timestamp('2004-10-31 01:00:00'),'2004-10-31 01:00:00'),
|
||||
(unix_timestamp('2004-10-31 02:00:00'),'2004-10-31 02:00:00'),
|
||||
(unix_timestamp('2004-10-31 02:59:59'),'2004-10-31 02:59:59'),
|
||||
(unix_timestamp('2004-10-31 04:00:00'),'2004-10-31 04:00:00'),
|
||||
(unix_timestamp('2004-10-31 02:59:59'),'2004-10-31 02:59:59');
|
||||
insert into t1 values
|
||||
(unix_timestamp('1981-07-01 03:59:59'),'1981-07-01 03:59:59'),
|
||||
(unix_timestamp('1981-07-01 04:00:00'),'1981-07-01 04:00:00');
|
||||
select i, from_unixtime(i), c from t1;
|
||||
i from_unixtime(i) c
|
||||
1072904422 2004-01-01 00:00:00 2004-01-01 00:00:00
|
||||
1080428421 2004-03-28 01:59:59 2004-03-28 01:59:59
|
||||
1080428422 2004-03-28 03:00:00 2004-03-28 02:30:00
|
||||
1080428422 2004-03-28 03:00:00 2004-03-28 03:00:00
|
||||
1083355222 2004-05-01 00:00:00 2004-05-01 00:00:00
|
||||
1099170022 2004-10-31 01:00:00 2004-10-31 01:00:00
|
||||
1099177222 2004-10-31 02:00:00 2004-10-31 02:00:00
|
||||
1099180821 2004-10-31 02:59:59 2004-10-31 02:59:59
|
||||
1099184422 2004-10-31 04:00:00 2004-10-31 04:00:00
|
||||
1099180821 2004-10-31 02:59:59 2004-10-31 02:59:59
|
||||
362793608 1981-07-01 03:59:59 1981-07-01 03:59:59
|
||||
362793610 1981-07-01 04:00:00 1981-07-01 04:00:00
|
||||
drop table t1;
|
||||
create table t1 (ts timestamp);
|
||||
insert into t1 values (19730101235900), (20040101235900);
|
||||
select * from t1;
|
||||
ts
|
||||
19730101235900
|
||||
20040101235900
|
||||
drop table t1;
|
BIN
mysql-test/std_data/Moscow_leap
Normal file
BIN
mysql-test/std_data/Moscow_leap
Normal file
Binary file not shown.
1
mysql-test/t/timezone3-master.opt
Normal file
1
mysql-test/t/timezone3-master.opt
Normal file
|
@ -0,0 +1 @@
|
|||
--timezone=:$MYSQL_TEST_DIR/std_data/Moscow_leap
|
59
mysql-test/t/timezone3.test
Normal file
59
mysql-test/t/timezone3.test
Normal file
|
@ -0,0 +1,59 @@
|
|||
#
|
||||
# Test of handling time zone with leap seconds.
|
||||
#
|
||||
# This test should be run with TZ=:$MYSQL_TEST_DIR/std_data/Moscow_leap
|
||||
# This implies that this test should be run only on systems that interpret
|
||||
# characters after colon in TZ variable as path to zoneinfo file.
|
||||
#
|
||||
# Check that we have successfully set time zone with leap seconds.
|
||||
--require r/have_moscow_leap_timezone.require
|
||||
disable_query_log;
|
||||
select from_unixtime(1072904422);
|
||||
enable_query_log;
|
||||
|
||||
# Initial clean-up
|
||||
--disable_warnings
|
||||
drop table if exists t1;
|
||||
--enable_warnings
|
||||
|
||||
#
|
||||
# Let us check behavior of conversion from broken-down representation
|
||||
# to time_t representation, for normal, non-existent and ambigious dates
|
||||
# (This check is similar to the one in timezone2.test in 4.1)
|
||||
#
|
||||
create table t1 (i int, c varchar(20));
|
||||
# Normal value without DST
|
||||
insert into t1 values
|
||||
(unix_timestamp("2004-01-01 00:00:00"), "2004-01-01 00:00:00");
|
||||
# Values around and in spring time-gap
|
||||
insert into t1 values
|
||||
(unix_timestamp("2004-03-28 01:59:59"), "2004-03-28 01:59:59"),
|
||||
(unix_timestamp("2004-03-28 02:30:00"), "2004-03-28 02:30:00"),
|
||||
(unix_timestamp("2004-03-28 03:00:00"), "2004-03-28 03:00:00");
|
||||
# Normal value with DST
|
||||
insert into t1 values
|
||||
(unix_timestamp('2004-05-01 00:00:00'),'2004-05-01 00:00:00');
|
||||
# Ambiguos values (also check for determenism)
|
||||
insert into t1 values
|
||||
(unix_timestamp('2004-10-31 01:00:00'),'2004-10-31 01:00:00'),
|
||||
(unix_timestamp('2004-10-31 02:00:00'),'2004-10-31 02:00:00'),
|
||||
(unix_timestamp('2004-10-31 02:59:59'),'2004-10-31 02:59:59'),
|
||||
(unix_timestamp('2004-10-31 04:00:00'),'2004-10-31 04:00:00'),
|
||||
(unix_timestamp('2004-10-31 02:59:59'),'2004-10-31 02:59:59');
|
||||
# Test of leap
|
||||
insert into t1 values
|
||||
(unix_timestamp('1981-07-01 03:59:59'),'1981-07-01 03:59:59'),
|
||||
(unix_timestamp('1981-07-01 04:00:00'),'1981-07-01 04:00:00');
|
||||
|
||||
select i, from_unixtime(i), c from t1;
|
||||
drop table t1;
|
||||
|
||||
#
|
||||
# Test for bug #6387 "Queried timestamp values do not match the
|
||||
# inserted". my_gmt_sec() function was not working properly if we
|
||||
# had time zone with leap seconds
|
||||
#
|
||||
create table t1 (ts timestamp);
|
||||
insert into t1 values (19730101235900), (20040101235900);
|
||||
select * from t1;
|
||||
drop table t1;
|
23
sql/time.cc
23
sql/time.cc
|
@ -81,6 +81,10 @@ long my_gmt_sec(TIME *t, long *my_timezone)
|
|||
I couldn't come up with a better way to get a repeatable result :(
|
||||
|
||||
We can't use mktime() as it's buggy on many platforms and not thread safe.
|
||||
|
||||
Note: this code assumes that our time_t estimation is not too far away
|
||||
from real value (we assume that localtime_r(tmp) will return something
|
||||
within 24 hrs from t) which is probably true for all current time zones.
|
||||
*/
|
||||
tmp=(time_t) (((calc_daynr((uint) t->year,(uint) t->month,(uint) t->day) -
|
||||
(long) days_at_timestart)*86400L + (long) t->hour*3600L +
|
||||
|
@ -93,7 +97,8 @@ long my_gmt_sec(TIME *t, long *my_timezone)
|
|||
for (loop=0;
|
||||
loop < 2 &&
|
||||
(t->hour != (uint) l_time->tm_hour ||
|
||||
t->minute != (uint) l_time->tm_min);
|
||||
t->minute != (uint) l_time->tm_min ||
|
||||
t->second != (uint) l_time->tm_sec);
|
||||
loop++)
|
||||
{ /* One check should be enough ? */
|
||||
/* Get difference in days */
|
||||
|
@ -103,15 +108,22 @@ long my_gmt_sec(TIME *t, long *my_timezone)
|
|||
else if (days > 1)
|
||||
days= -1;
|
||||
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
|
||||
(long) (60*((int) t->minute - (int) l_time->tm_min)));
|
||||
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
|
||||
(long) ((int) t->second - (int) l_time->tm_sec));
|
||||
current_timezone+= diff+3600; // Compensate for -3600 above
|
||||
tmp+= (time_t) diff;
|
||||
localtime_r(&tmp,&tm_tmp);
|
||||
l_time=&tm_tmp;
|
||||
}
|
||||
/*
|
||||
Fix that if we are in the not existing daylight saving time hour
|
||||
we move the start of the next real hour
|
||||
Fix that if we are in the non existing daylight saving time hour
|
||||
we move the start of the next real hour.
|
||||
|
||||
This code doesn't handle such exotical thing as time-gaps whose length
|
||||
is more than one hour or non-integer (latter can theoretically happen
|
||||
if one of seconds will be removed due leap correction, or because of
|
||||
general time correction like it happened for Africa/Monrovia time zone
|
||||
in year 1972).
|
||||
*/
|
||||
if (loop == 2 && t->hour != (uint) l_time->tm_hour)
|
||||
{
|
||||
|
@ -121,7 +133,8 @@ long my_gmt_sec(TIME *t, long *my_timezone)
|
|||
else if (days > 1)
|
||||
days= -1;
|
||||
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
|
||||
(long) (60*((int) t->minute - (int) l_time->tm_min)));
|
||||
(long) (60*((int) t->minute - (int) l_time->tm_min)) +
|
||||
(long) ((int) t->second - (int) l_time->tm_sec));
|
||||
if (diff == 3600)
|
||||
tmp+=3600 - t->minute*60 - t->second; // Move to next hour
|
||||
else if (diff == -3600)
|
||||
|
|
Loading…
Reference in a new issue