summaryrefslogtreecommitdiff
path: root/sql/time.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/time.cc')
-rw-r--r--sql/time.cc151
1 files changed, 128 insertions, 23 deletions
diff --git a/sql/time.cc b/sql/time.cc
index ba81fcc86c2..4285e02fb65 100644
--- a/sql/time.cc
+++ b/sql/time.cc
@@ -242,6 +242,109 @@ str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time,
}
+/**
+ converts a pair of numbers (integer part, microseconds) to MYSQL_TIME
+
+ @param neg sign of the time value
+ @param nr integer part of the number to convert
+ @param sec_part microsecond part of the number
+ @param ltime converted value will be written here
+ @param fuzzydate conversion flags (TIME_FUZZY_DATE, etc)
+ @param str original number, as a Lazy_string. For the warning
+ @param f_type how to treat the number as. Possible values
+ MYSQL_TYPE_TIME, MYSQL_TYPE_DATE, MYSQL_TYPE_DATETIME
+ or MYSQL_TYPE_SET (which means auto-set to TIME or
+ DATETIME depending on fuzzydate)
+ @param name field name for the warning message; NULL if not a field
+
+ @returns 0 for success, 1 for a failure
+*/
+static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part,
+ MYSQL_TIME *ltime, ulong fuzzydate,
+ const Lazy_string *str,
+ enum_field_types f_type,
+ const char *name)
+{
+ int was_cut;
+ longlong res;
+
+ if (f_type == MYSQL_TYPE_SET)
+ f_type= fuzzydate & TIME_TIME_ONLY ? MYSQL_TYPE_TIME
+ : MYSQL_TYPE_DATETIME;
+
+ switch (f_type) {
+ case MYSQL_TYPE_YEAR:
+ bzero(ltime, sizeof(*ltime));
+ ltime->year= nr;
+ ltime->time_type= MYSQL_TIMESTAMP_DATE;
+ if (nr > 9999 || (fuzzydate & (TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE)))
+ res= -1;
+ break;
+ case MYSQL_TYPE_TIME:
+ res= number_to_time(neg, nr, sec_part, ltime, &was_cut);
+ break;
+ default:
+ res= number_to_datetime(nr, sec_part, ltime, fuzzydate, &was_cut);
+ break;
+ }
+ if (res >= 0)
+ return 0;
+
+ make_truncated_value_warning(current_thd,
+ MYSQL_ERROR::WARN_LEVEL_WARN, str,
+ mysql_type_to_time_type(f_type),
+ name);
+ return 1;
+}
+
+
+bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime,
+ ulong fuzzydate,
+ enum_field_types f_type,
+ const char *name)
+{
+ const Lazy_string_double str(value);
+ ulonglong nr;
+ ulong sec_part;
+ bool neg= value < 0;
+
+ if (neg)
+ value= -value;
+
+ nr = value > LONGLONG_MAX ? LONGLONG_MAX
+ : static_cast<ulonglong>(trunc(value));
+ sec_part= (ulong)((value - nr)*TIME_SECOND_PART_FACTOR);
+ return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate,
+ &str, f_type, name);
+}
+
+
+bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime,
+ ulong fuzzydate,
+ enum_field_types f_type,
+ const char *name)
+{
+ const Lazy_string_decimal str(value);
+ ulonglong nr;
+ ulong sec_part;
+ bool neg= my_decimal2seconds(value, &nr, &sec_part);
+ return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate,
+ &str, f_type, name);
+}
+
+
+bool int_to_datetime_with_warn(longlong value, MYSQL_TIME *ltime,
+ ulong fuzzydate,
+ enum_field_types f_type,
+ const char *name)
+{
+ const Lazy_string_num str(value);
+ bool neg= value < 0;
+ return number_to_time_with_warn(neg, neg ? -value : value, 0, ltime,
+ fuzzydate, &str, f_type, name);
+}
+
+
/*
Convert a datetime from broken-down MYSQL_TIME representation to corresponding
TIMESTAMP value.
@@ -263,23 +366,8 @@ str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time,
*/
my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code)
{
- my_time_t timestamp;
- my_bool in_dst_time_gap= 0;
-
- *error_code= 0;
thd->time_zone_used= 1;
-
- timestamp= thd->variables.time_zone->TIME_to_gmt_sec(t, &in_dst_time_gap);
- if (timestamp)
- {
- if (in_dst_time_gap)
- *error_code= ER_WARN_INVALID_TIMESTAMP;
- return timestamp;
- }
-
- /* If we are here we have range error. */
- *error_code= ER_WARN_DATA_OUT_OF_RANGE;
- return 0;
+ return thd->variables.time_zone->TIME_to_gmt_sec(t, error_code);
}
@@ -757,6 +845,7 @@ void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level leve
(((((X)->day * 24LL + (X)->hour) * 60LL + \
(X)->minute) * 60LL + (X)->second)*1000000LL + \
(X)->second_part)
+#define GET_PART(X, N) X % N ## LL; X/= N ## LL
bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL interval)
{
@@ -782,7 +871,7 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL inter
case INTERVAL_DAY:
{
longlong usec, daynr;
- my_bool neg= ltime->neg;
+ my_bool neg= 0;
enum enum_mysql_timestamp_type time_type= ltime->time_type;
if (time_type != MYSQL_TIMESTAMP_TIME)
@@ -790,17 +879,31 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL inter
usec= COMBINE(ltime) + sign*COMBINE(&interval);
- unpack_time(usec, ltime);
- ltime->time_type= time_type;
+ if (usec < 0)
+ {
+ neg= 1;
+ usec= -usec;
+ }
+
+ ltime->second_part= GET_PART(usec, 1000000);
+ ltime->second= GET_PART(usec, 60);
+ ltime->minute= GET_PART(usec, 60);
ltime->neg^= neg;
if (time_type == MYSQL_TIMESTAMP_TIME)
- break;
+ {
+ if (usec > TIME_MAX_HOUR)
+ goto invalid_date;
+ ltime->hour= usec;
+ ltime->day= 0;
+ return 0;
+ }
if (int_type != INTERVAL_DAY)
ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date
- daynr= usec/1000000/24/60/60;
+ ltime->hour= GET_PART(usec, 24);
+ daynr= usec;
/* Day number from year 0 to 9999-12-31 */
if ((ulonglong) daynr > MAX_DAY_NUMBER)
@@ -846,13 +949,15 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL inter
goto null_date;
}
- return 0; // Ok
+ if (ltime->time_type != MYSQL_TIMESTAMP_TIME)
+ return 0; // Ok
invalid_date:
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_DATETIME_FUNCTION_OVERFLOW,
ER(ER_DATETIME_FUNCTION_OVERFLOW),
- "datetime");
+ ltime->time_type == MYSQL_TIMESTAMP_TIME ?
+ "time" : "datetime");
null_date:
return 1;
}