mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 07:45:44 +00:00
Merge pchardin@bk-internal.mysql.com:/home/bk/mysql-4.1
into outpost.site:/home/cps/mysql/trees/4.1-runtime-bug9191 configure.in: Auto merged include/my_time.h: Auto merged mysql-test/r/func_time.result: Auto merged mysql-test/r/rename.result: Auto merged mysql-test/t/func_time.test: Auto merged sql-common/my_time.c: Auto merged sql/item_timefunc.cc: Auto merged sql/time.cc: Auto merged mysql-test/t/rename.test: choose one of the race problem solutions. It was solved differently in -runtime and mainstream
This commit is contained in:
@ -714,14 +714,27 @@ long calc_daynr(uint year,uint month,uint day)
|
||||
RETURN VALUE
|
||||
Time in UTC seconds since Unix Epoch representation.
|
||||
*/
|
||||
my_time_t
|
||||
my_system_gmt_sec(const MYSQL_TIME *t, long *my_timezone, bool *in_dst_time_gap)
|
||||
my_time_t
|
||||
my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone,
|
||||
bool *in_dst_time_gap)
|
||||
{
|
||||
uint loop;
|
||||
time_t tmp;
|
||||
time_t tmp= 0;
|
||||
int shift= 0;
|
||||
MYSQL_TIME tmp_time;
|
||||
MYSQL_TIME *t= &tmp_time;
|
||||
struct tm *l_time,tm_tmp;
|
||||
long diff, current_timezone;
|
||||
|
||||
/*
|
||||
Use temp variable to avoid trashing input data, which could happen in
|
||||
case of shift required for boundary dates processing.
|
||||
*/
|
||||
memcpy(&tmp_time, t_src, sizeof(MYSQL_TIME));
|
||||
|
||||
if (!validate_timestamp_range(t))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
Calculate the gmt time based on current time and timezone
|
||||
The -1 on the end is to ensure that if have a date that exists twice
|
||||
@ -735,13 +748,89 @@ my_system_gmt_sec(const MYSQL_TIME *t, long *my_timezone, bool *in_dst_time_gap)
|
||||
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 +
|
||||
(long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
|
||||
3600);
|
||||
current_timezone= my_time_zone;
|
||||
|
||||
Note2: For the dates, which have time_t representation close to
|
||||
MAX_INT32 (efficient time_t limit for supported platforms), we should
|
||||
do a small trick to avoid overflow. That is, convert the date, which is
|
||||
two days earlier, and then add these days to the final value.
|
||||
|
||||
The same trick is done for the values close to 0 in time_t
|
||||
representation for platfroms with unsigned time_t (QNX).
|
||||
|
||||
To be more verbose, here is a sample (extracted from the code below):
|
||||
(calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
|
||||
would return -2147480896 because of the long type overflow. In result
|
||||
we would get 1901 year in localtime_r(), which is an obvious error.
|
||||
|
||||
Alike problem raises with the dates close to Epoch. E.g.
|
||||
(calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
|
||||
will give -3600.
|
||||
|
||||
On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
|
||||
wil give us a date around 2106 year. Which is no good.
|
||||
|
||||
Theoreticaly, there could be problems with the latter conversion:
|
||||
there are at least two timezones, which had time switches near 1 Jan
|
||||
of 1970 (because of political reasons). These are America/Hermosillo and
|
||||
America/Mazatlan time zones. They changed their offset on
|
||||
1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
|
||||
the code below will give incorrect results for dates close to
|
||||
1970-01-01, in the case OS takes into account these historical switches.
|
||||
Luckily, it seems that we support only one platform with unsigned
|
||||
time_t. It's QNX. And QNX does not support historical timezone data at all.
|
||||
E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
|
||||
historical information for localtime_r() etc. That is, the problem is not
|
||||
relevant to QNX.
|
||||
|
||||
We are safe with shifts close to MAX_INT32, as there are no known
|
||||
time switches on Jan 2038 yet :)
|
||||
*/
|
||||
if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && (t->day > 4))
|
||||
{
|
||||
/*
|
||||
Below we will pass (uint) (t->day - shift) to calc_daynr.
|
||||
As we don't want to get an overflow here, we will shift
|
||||
only safe dates. That's why we have (t->day > 4) above.
|
||||
*/
|
||||
t->day-= 2;
|
||||
shift= 2;
|
||||
}
|
||||
#ifdef TIME_T_UNSIGNED
|
||||
else
|
||||
{
|
||||
/*
|
||||
We can get 0 in time_t representaion only on 1969, 31 of Dec or on
|
||||
1970, 1 of Jan. For both dates we use shift, which is added
|
||||
to t->day in order to step out a bit from the border.
|
||||
This is required for platforms, where time_t is unsigned.
|
||||
As far as I know, among the platforms we support it's only QNX.
|
||||
Note: the order of below if-statements is significant.
|
||||
*/
|
||||
|
||||
if ((t->year == TIMESTAMP_MIN_YEAR + 1) && (t->month == 1)
|
||||
&& (t->day <= 10))
|
||||
{
|
||||
t->day+= 2;
|
||||
shift= -2;
|
||||
}
|
||||
|
||||
if ((t->year == TIMESTAMP_MIN_YEAR) && (t->month == 12)
|
||||
&& (t->day == 31))
|
||||
{
|
||||
t->year++;
|
||||
t->month= 1;
|
||||
t->day= 2;
|
||||
shift= -2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
tmp= (time_t) (((calc_daynr((uint) t->year, (uint) t->month, (uint) t->day) -
|
||||
(long) days_at_timestart)*86400L + (long) t->hour*3600L +
|
||||
(long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
|
||||
3600);
|
||||
|
||||
current_timezone= my_time_zone;
|
||||
localtime_r(&tmp,&tm_tmp);
|
||||
l_time=&tm_tmp;
|
||||
for (loop=0;
|
||||
@ -793,7 +882,24 @@ my_system_gmt_sec(const MYSQL_TIME *t, long *my_timezone, bool *in_dst_time_gap)
|
||||
*in_dst_time_gap= 1;
|
||||
}
|
||||
*my_timezone= current_timezone;
|
||||
|
||||
|
||||
|
||||
/* shift back, if we were dealing with boundary dates */
|
||||
tmp+= shift*86400L;
|
||||
|
||||
/*
|
||||
This is possible for dates, which slightly exceed boundaries.
|
||||
Conversion will pass ok for them, but we don't allow them.
|
||||
First check will pass for platforms with signed time_t.
|
||||
instruction above (tmp+= shift*86400L) could exceed
|
||||
MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
|
||||
So, tmp < TIMESTAMP_MIN_VALUE will be triggered. On platfroms
|
||||
with unsigned time_t tmp+= shift*86400L might result in a number,
|
||||
larger then TIMESTAMP_MAX_VALUE, so another check will work.
|
||||
*/
|
||||
if ((tmp < TIMESTAMP_MIN_VALUE) || (tmp > TIMESTAMP_MAX_VALUE))
|
||||
tmp= 0;
|
||||
end:
|
||||
return (my_time_t) tmp;
|
||||
} /* my_system_gmt_sec */
|
||||
|
||||
|
Reference in New Issue
Block a user