summaryrefslogtreecommitdiff
path: root/sql/tztime.cc
diff options
context:
space:
mode:
authorpetr/cps@mysql.com/owlet.local <>2006-11-01 16:47:40 +0300
committerpetr/cps@mysql.com/owlet.local <>2006-11-01 16:47:40 +0300
commit3ec542dfbdd08c13a6c33b07f7ddd9650c0e0ad8 (patch)
treee2642512c7a6f4cbdaa661730aca0413380e045e /sql/tztime.cc
parentacaa584c5523753cab5b3a50ed3c95672b462541 (diff)
downloadmariadb-git-3ec542dfbdd08c13a6c33b07f7ddd9650c0e0ad8.tar.gz
Fix Bug #9191 "TIMESTAMP/from_unixtime() no longer accepts 2^31-1"
(4.1 version, with post-review fixes) The fix for another Bug (6439) limited FROM_UNIXTIME() to TIMESTAMP_MAX_VALUE which is 2145916799 or 2037-12-01 23:59:59 GMT, however unix timestamp in general is not considered to be limited by this value. All dates up to power(2,31)-1 are valid. This patch extends allowed TIMESTAMP range so, that max TIMESTAMP value is power(2,31)-1. It also corrects FROM_UNIXTIME() and UNIX_TIMESTAMP() functions, so that max allowed UNIX_TIMESTAMP() is power(2,31)-1. FROM_UNIXTIME() is fixed accordingly to allow conversion of dates up to 2038-01-19 03:14:07 UTC. The patch also fixes CONVERT_TZ() function to allow extended range of dates. The main problem solved in the patch is possible overflows of variables, used in broken-time representation to time_t conversion (required for UNIX_TIMESTAMP).
Diffstat (limited to 'sql/tztime.cc')
-rw-r--r--sql/tztime.cc76
1 files changed, 68 insertions, 8 deletions
diff --git a/sql/tztime.cc b/sql/tztime.cc
index b0a32748998..60c5e8efd71 100644
--- a/sql/tztime.cc
+++ b/sql/tztime.cc
@@ -885,9 +885,14 @@ TIME_to_gmt_sec(const TIME *t, const TIME_ZONE_INFO *sp, bool *in_dst_time_gap)
my_time_t local_t;
uint saved_seconds;
uint i;
+ int shift= 0;
DBUG_ENTER("TIME_to_gmt_sec");
+ if (!validate_timestamp_range(t))
+ return 0;
+
+
/* We need this for correct leap seconds handling */
if (t->second < SECS_PER_MIN)
saved_seconds= 0;
@@ -895,11 +900,29 @@ TIME_to_gmt_sec(const TIME *t, const TIME_ZONE_INFO *sp, bool *in_dst_time_gap)
saved_seconds= t->second;
/*
- NOTE If we want to convert full my_time_t range without MySQL
- restrictions we should catch overflow here somehow.
+ NOTE: to convert full my_time_t range we do a shift of the
+ boundary dates here to avoid overflow of my_time_t.
+ We use alike approach in my_system_gmt_sec().
+
+ However in that function we also have to take into account
+ overflow near 0 on some platforms. That's because my_system_gmt_sec
+ uses localtime_r(), which doesn't work with negative values correctly
+ on platforms with unsigned time_t (QNX). Here we don't use localtime()
+ => we negative values of local_t are ok.
*/
- local_t= sec_since_epoch(t->year, t->month, t->day,
+ if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && t->day > 4)
+ {
+ /*
+ We will pass (t->day - shift) to sec_since_epoch(), and
+ want this value to be a positive number, so we shift
+ only dates > 4.01.2038 (to avoid owerflow).
+ */
+ shift= 2;
+ }
+
+
+ local_t= sec_since_epoch(t->year, t->month, (t->day - shift),
t->hour, t->minute,
saved_seconds ? 0 : t->second);
@@ -918,6 +941,22 @@ TIME_to_gmt_sec(const TIME *t, const TIME_ZONE_INFO *sp, bool *in_dst_time_gap)
/* binary search for our range */
i= find_time_range(local_t, sp->revts, sp->revcnt);
+ /*
+ As there are no offset switches at the end of TIMESTAMP range,
+ we could simply check for overflow here (and don't need to bother
+ about DST gaps etc)
+ */
+ if (shift)
+ {
+ if (local_t > (TIMESTAMP_MAX_VALUE - shift*86400L +
+ sp->revtis[i].rt_offset - saved_seconds))
+ {
+ DBUG_RETURN(0); /* my_time_t overflow */
+ }
+ else
+ local_t+= shift*86400L;
+ }
+
if (sp->revtis[i].rt_type)
{
/*
@@ -927,10 +966,16 @@ TIME_to_gmt_sec(const TIME *t, const TIME_ZONE_INFO *sp, bool *in_dst_time_gap)
beginning of the gap.
*/
*in_dst_time_gap= 1;
- DBUG_RETURN(sp->revts[i] - sp->revtis[i].rt_offset + saved_seconds);
+ local_t= sp->revts[i] - sp->revtis[i].rt_offset + saved_seconds;
}
else
- DBUG_RETURN(local_t - sp->revtis[i].rt_offset + saved_seconds);
+ local_t= local_t - sp->revtis[i].rt_offset + saved_seconds;
+
+ /* check for TIMESTAMP_MAX_VALUE was already done above */
+ if (local_t < TIMESTAMP_MIN_VALUE)
+ local_t= 0;
+
+ DBUG_RETURN(local_t);
}
@@ -1294,9 +1339,24 @@ Time_zone_offset::Time_zone_offset(long tz_offset_arg):
my_time_t
Time_zone_offset::TIME_to_gmt_sec(const TIME *t, bool *in_dst_time_gap) const
{
- return sec_since_epoch(t->year, t->month, t->day,
- t->hour, t->minute, t->second) -
- offset;
+ my_time_t local_t;
+
+ /*
+ Check timestamp range.we have to do this as calling function relies on
+ us to make all validation checks here.
+ */
+ if (!validate_timestamp_range(t))
+ return 0;
+
+ local_t= sec_since_epoch(t->year, t->month, t->day,
+ t->hour, t->minute, t->second) -
+ offset;
+
+ if (local_t >= TIMESTAMP_MIN_VALUE && local_t <= TIMESTAMP_MAX_VALUE)
+ return local_t;
+
+ /* range error*/
+ return 0;
}