Commit d259ba40 authored by unknown's avatar unknown

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.
parent 9c06c80d
...@@ -32,6 +32,7 @@ dist-hook: ...@@ -32,6 +32,7 @@ dist-hook:
$(INSTALL_DATA) $(srcdir)/r/*.result $(srcdir)/r/*.require $(distdir)/r $(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/*.dat $(srcdir)/std_data/*.001 $(distdir)/std_data
$(INSTALL_DATA) $(srcdir)/std_data/des_key_file $(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: install-data-local:
$(mkinstalldirs) \ $(mkinstalldirs) \
...@@ -50,6 +51,7 @@ install-data-local: ...@@ -50,6 +51,7 @@ install-data-local:
$(INSTALL_DATA) $(srcdir)/std_data/*.dat $(DESTDIR)$(testdir)/std_data $(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/*.*001 $(DESTDIR)$(testdir)/std_data
$(INSTALL_DATA) $(srcdir)/std_data/des_key_file $(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 SUFFIXES = .sh
......
from_unixtime(1072904422)
2004-01-01 00:00:00
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;
--timezone=:$MYSQL_TEST_DIR/std_data/Moscow_leap
#
# 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;
...@@ -81,6 +81,10 @@ long my_gmt_sec(TIME *t, long *my_timezone) ...@@ -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 :( 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. 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) - tmp=(time_t) (((calc_daynr((uint) t->year,(uint) t->month,(uint) t->day) -
(long) days_at_timestart)*86400L + (long) t->hour*3600L + (long) days_at_timestart)*86400L + (long) t->hour*3600L +
...@@ -93,7 +97,8 @@ long my_gmt_sec(TIME *t, long *my_timezone) ...@@ -93,7 +97,8 @@ long my_gmt_sec(TIME *t, long *my_timezone)
for (loop=0; for (loop=0;
loop < 2 && loop < 2 &&
(t->hour != (uint) l_time->tm_hour || (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++) loop++)
{ /* One check should be enough ? */ { /* One check should be enough ? */
/* Get difference in days */ /* Get difference in days */
...@@ -103,15 +108,22 @@ long my_gmt_sec(TIME *t, long *my_timezone) ...@@ -103,15 +108,22 @@ long my_gmt_sec(TIME *t, long *my_timezone)
else if (days > 1) else if (days > 1)
days= -1; days= -1;
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) + 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 current_timezone+= diff+3600; // Compensate for -3600 above
tmp+= (time_t) diff; tmp+= (time_t) diff;
localtime_r(&tmp,&tm_tmp); localtime_r(&tmp,&tm_tmp);
l_time=&tm_tmp; l_time=&tm_tmp;
} }
/* /*
Fix that if we are in the not existing daylight saving time hour Fix that if we are in the non existing daylight saving time hour
we move the start of the next real 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) if (loop == 2 && t->hour != (uint) l_time->tm_hour)
{ {
...@@ -121,7 +133,8 @@ long my_gmt_sec(TIME *t, long *my_timezone) ...@@ -121,7 +133,8 @@ long my_gmt_sec(TIME *t, long *my_timezone)
else if (days > 1) else if (days > 1)
days= -1; days= -1;
diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+ 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) if (diff == 3600)
tmp+=3600 - t->minute*60 - t->second; // Move to next hour tmp+=3600 - t->minute*60 - t->second; // Move to next hour
else if (diff == -3600) else if (diff == -3600)
......
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