diff --git a/mysql-test/Makefile.am b/mysql-test/Makefile.am index ba96c5947ba..8b0c096120a 100644 --- a/mysql-test/Makefile.am +++ b/mysql-test/Makefile.am @@ -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 diff --git a/mysql-test/r/have_moscow_leap_timezone.require b/mysql-test/r/have_moscow_leap_timezone.require new file mode 100644 index 00000000000..f27452d7770 --- /dev/null +++ b/mysql-test/r/have_moscow_leap_timezone.require @@ -0,0 +1,2 @@ +from_unixtime(1072904422) +2004-01-01 00:00:00 diff --git a/mysql-test/r/timezone3.result b/mysql-test/r/timezone3.result new file mode 100644 index 00000000000..2135dd33511 --- /dev/null +++ b/mysql-test/r/timezone3.result @@ -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; diff --git a/mysql-test/std_data/Moscow_leap b/mysql-test/std_data/Moscow_leap new file mode 100644 index 00000000000..4994c005595 Binary files /dev/null and b/mysql-test/std_data/Moscow_leap differ diff --git a/mysql-test/t/timezone3-master.opt b/mysql-test/t/timezone3-master.opt new file mode 100644 index 00000000000..6910e6e6e8d --- /dev/null +++ b/mysql-test/t/timezone3-master.opt @@ -0,0 +1 @@ +--timezone=:$MYSQL_TEST_DIR/std_data/Moscow_leap diff --git a/mysql-test/t/timezone3.test b/mysql-test/t/timezone3.test new file mode 100644 index 00000000000..8910783cd85 --- /dev/null +++ b/mysql-test/t/timezone3.test @@ -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; diff --git a/sql/time.cc b/sql/time.cc index 0363d764100..38670db054f 100644 --- a/sql/time.cc +++ b/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)