mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 10:56:12 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1084 lines
		
	
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1084 lines
		
	
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* Copyright (c) 2000, 2010, Oracle and/or its affiliates.
 | |
|    Copyright (c) 2009, 2020, MariaDB Corporation.
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or modify
 | |
|    it under the terms of the GNU General Public License as published by
 | |
|    the Free Software Foundation; version 2 of the License.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
 | |
| 
 | |
| 
 | |
| /* Functions to handle date and time */
 | |
| 
 | |
| #include "mariadb.h"
 | |
| #include "sql_time.h"
 | |
| #include "tztime.h"                      // struct Time_zone
 | |
| #include "sql_class.h"                   // THD
 | |
| #include <m_ctype.h>
 | |
| 
 | |
| 
 | |
| #define MAX_DAY_NUMBER 3652424L
 | |
| 
 | |
| 	/* Some functions to calculate dates */
 | |
| 
 | |
| /*
 | |
|   Name description of interval names used in statements.
 | |
| 
 | |
|   'interval_type_to_name' is ordered and sorted on interval size and
 | |
|   interval complexity.
 | |
|   Order of elements in 'interval_type_to_name' should correspond to 
 | |
|   the order of elements in 'interval_type' enum
 | |
|   
 | |
|   See also interval_type, interval_names, append_interval
 | |
| */
 | |
| 
 | |
| LEX_CSTRING interval_type_to_name[INTERVAL_LAST] = {
 | |
|   { STRING_WITH_LEN("YEAR")},
 | |
|   { STRING_WITH_LEN("QUARTER")},
 | |
|   { STRING_WITH_LEN("MONTH")},
 | |
|   { STRING_WITH_LEN("WEEK")},
 | |
|   { STRING_WITH_LEN("DAY")},
 | |
|   { STRING_WITH_LEN("HOUR")},
 | |
|   { STRING_WITH_LEN("MINUTE")},
 | |
|   { STRING_WITH_LEN("SECOND")},
 | |
|   { STRING_WITH_LEN("MICROSECOND")},
 | |
|   { STRING_WITH_LEN("YEAR_MONTH")},
 | |
|   { STRING_WITH_LEN("DAY_HOUR")},
 | |
|   { STRING_WITH_LEN("DAY_MINUTE")},
 | |
|   { STRING_WITH_LEN("DAY_SECOND")},
 | |
|   { STRING_WITH_LEN("HOUR_MINUTE")},
 | |
|   { STRING_WITH_LEN("HOUR_SECOND")},
 | |
|   { STRING_WITH_LEN("MINUTE_SECOND")},
 | |
|   { STRING_WITH_LEN("DAY_MICROSECOND")},
 | |
|   { STRING_WITH_LEN("HOUR_MICROSECOND")},
 | |
|   { STRING_WITH_LEN("MINUTE_MICROSECOND")},
 | |
|   { STRING_WITH_LEN("SECOND_MICROSECOND")}
 | |
| };
 | |
| 
 | |
| int append_interval(String *str, interval_type int_type, const INTERVAL &interval)
 | |
| {
 | |
|   char buf[64];
 | |
|   size_t len;
 | |
|   switch (int_type) {
 | |
|   case INTERVAL_YEAR:
 | |
|     len= my_snprintf(buf,sizeof(buf),"%lu", interval.year);
 | |
|     break;
 | |
|   case INTERVAL_QUARTER:
 | |
|   case INTERVAL_MONTH:
 | |
|     len= my_snprintf(buf,sizeof(buf),"%lu", interval.month);
 | |
|     int_type=INTERVAL_MONTH;
 | |
|     break;
 | |
|   case INTERVAL_WEEK:
 | |
|   case INTERVAL_DAY:
 | |
|     len= my_snprintf(buf,sizeof(buf),"%lu", interval.day);
 | |
|     int_type=INTERVAL_DAY;
 | |
|     break;
 | |
|   case INTERVAL_HOUR:
 | |
|     len= my_snprintf(buf,sizeof(buf),"%lu", interval.hour);
 | |
|     break;
 | |
|   case INTERVAL_MINUTE:
 | |
|     len= my_snprintf(buf,sizeof(buf),"%llu", interval.minute);
 | |
|     break;
 | |
|   case INTERVAL_SECOND:
 | |
|     len= my_snprintf(buf,sizeof(buf),"%llu", interval.second);
 | |
|     break;
 | |
|   case INTERVAL_MICROSECOND:
 | |
|     len= my_snprintf(buf,sizeof(buf),"%llu", interval.second_part);
 | |
|     break;
 | |
|   case INTERVAL_YEAR_MONTH:
 | |
|     len= my_snprintf(buf,sizeof(buf),"'%lu-%02lu'",
 | |
|                      interval.year, interval.month);
 | |
|     break;
 | |
|   case INTERVAL_DAY_HOUR:
 | |
|     len= my_snprintf(buf,sizeof(buf),"'%lu %lu'", interval.day, interval.hour);
 | |
|     break;
 | |
|   case INTERVAL_DAY_MINUTE:
 | |
|     len= my_snprintf(buf,sizeof(buf),"'%lu %lu:%02llu'",
 | |
|                      interval.day, interval.hour, interval.minute);
 | |
|     break;
 | |
|   case INTERVAL_DAY_SECOND:
 | |
|     len= my_snprintf(buf,sizeof(buf),"'%lu %lu:%02llu:%02llu'",
 | |
|                      interval.day, interval.hour, interval.minute, interval.second);
 | |
|     break;
 | |
|   case INTERVAL_HOUR_MINUTE:
 | |
|     len= my_snprintf(buf,sizeof(buf),"'%lu:%02llu'", interval.hour, interval.minute);
 | |
|     break;
 | |
|   case INTERVAL_HOUR_SECOND:
 | |
|     len= my_snprintf(buf,sizeof(buf),"'%lu:%02llu:%02llu'",
 | |
|                      interval.hour, interval.minute, interval.second);
 | |
|     break;
 | |
|   case INTERVAL_MINUTE_SECOND:
 | |
|     len= my_snprintf(buf,sizeof(buf),"'%llu:%02llu'", interval.minute, interval.second);
 | |
|     break;
 | |
|   case INTERVAL_DAY_MICROSECOND:
 | |
|     len= my_snprintf(buf,sizeof(buf),"'%lu %lu:%02llu:%02llu.%06llu'",
 | |
|                      interval.day, interval.hour, interval.minute,
 | |
|                      interval.second, interval.second_part);
 | |
|     break;
 | |
|   case INTERVAL_HOUR_MICROSECOND:
 | |
|     len= my_snprintf(buf,sizeof(buf),"'%lu:%02llu:%02llu.%06llu'",
 | |
|                      interval.hour, interval.minute, interval.second,
 | |
|                      interval.second_part);
 | |
|     break;
 | |
|   case INTERVAL_MINUTE_MICROSECOND:
 | |
|     len= my_snprintf(buf,sizeof(buf),"'%llu:%02llu.%06llu'",
 | |
|                      interval.minute, interval.second, interval.second_part);
 | |
|     break;
 | |
|   case INTERVAL_SECOND_MICROSECOND:
 | |
|     len= my_snprintf(buf,sizeof(buf),"%llu.%06llu", interval.second, interval.second_part);
 | |
|     break;
 | |
|   default:
 | |
|     DBUG_ASSERT(0);
 | |
|     len= 0;
 | |
|   }
 | |
|   return str->append(buf, len) || str->append(' ') ||
 | |
|          str->append(interval_type_to_name + int_type);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Calc weekday from daynr
 | |
|   Returns 0 for monday, 1 for tuesday ...
 | |
| */
 | |
| 
 | |
| int calc_weekday(long daynr,bool sunday_first_day_of_week)
 | |
| {
 | |
|   DBUG_ENTER("calc_weekday");
 | |
|   DBUG_RETURN ((int) ((daynr + 5L + (sunday_first_day_of_week ? 1L : 0L)) % 7));
 | |
| }
 | |
| 
 | |
| /*
 | |
|   The bits in week_format has the following meaning:
 | |
|    WEEK_MONDAY_FIRST (0)  If not set	Sunday is first day of week
 | |
|       		   	  If set	Monday is first day of week
 | |
|    WEEK_YEAR (1)	  If not set	Week is in range 0-53
 | |
| 
 | |
|    	Week 0 is returned for the the last week of the previous year (for
 | |
| 	a date at start of january) In this case one can get 53 for the
 | |
| 	first week of next year.  This flag ensures that the week is
 | |
| 	relevant for the given year. Note that this flag is only
 | |
| 	relevant if WEEK_JANUARY is not set.
 | |
| 
 | |
| 			  If set	 Week is in range 1-53.
 | |
| 
 | |
| 	In this case one may get week 53 for a date in January (when
 | |
| 	the week is that last week of previous year) and week 1 for a
 | |
| 	date in December.
 | |
| 
 | |
|   WEEK_FIRST_WEEKDAY (2)  If not set	Weeks are numbered according
 | |
| 			   		to ISO 8601:1988
 | |
| 			  If set	The week that contains the first
 | |
| 					'first-day-of-week' is week 1.
 | |
| 	
 | |
| 	ISO 8601:1988 means that if the week containing January 1 has
 | |
| 	four or more days in the new year, then it is week 1;
 | |
| 	Otherwise it is the last week of the previous year, and the
 | |
| 	next week is week 1.
 | |
| */
 | |
| 
 | |
| uint calc_week(const MYSQL_TIME *l_time, uint week_behaviour, uint *year)
 | |
| {
 | |
|   uint days;
 | |
|   ulong daynr=calc_daynr(l_time->year,l_time->month,l_time->day);
 | |
|   ulong first_daynr=calc_daynr(l_time->year,1,1);
 | |
|   bool monday_first= MY_TEST(week_behaviour & WEEK_MONDAY_FIRST);
 | |
|   bool week_year= MY_TEST(week_behaviour & WEEK_YEAR);
 | |
|   bool first_weekday= MY_TEST(week_behaviour & WEEK_FIRST_WEEKDAY);
 | |
| 
 | |
|   uint weekday=calc_weekday(first_daynr, !monday_first);
 | |
|   *year=l_time->year;
 | |
| 
 | |
|   if (l_time->month == 1 && l_time->day <= 7-weekday)
 | |
|   {
 | |
|     if (!week_year && 
 | |
| 	((first_weekday && weekday != 0) ||
 | |
| 	 (!first_weekday && weekday >= 4)))
 | |
|       return 0;
 | |
|     week_year= 1;
 | |
|     (*year)--;
 | |
|     first_daynr-= (days=calc_days_in_year(*year));
 | |
|     weekday= (weekday + 53*7- days) % 7;
 | |
|   }
 | |
| 
 | |
|   if ((first_weekday && weekday != 0) ||
 | |
|       (!first_weekday && weekday >= 4))
 | |
|     days= daynr - (first_daynr+ (7-weekday));
 | |
|   else
 | |
|     days= daynr - (first_daynr - weekday);
 | |
| 
 | |
|   if (week_year && days >= 52*7)
 | |
|   {
 | |
|     weekday= (weekday + calc_days_in_year(*year)) % 7;
 | |
|     if ((!first_weekday && weekday < 4) ||
 | |
| 	(first_weekday && weekday == 0))
 | |
|     {
 | |
|       (*year)++;
 | |
|       return 1;
 | |
|     }
 | |
|   }
 | |
|   return days/7+1;
 | |
| }
 | |
| 
 | |
| 	/* Change a daynr to year, month and day */
 | |
| 	/* Daynr 0 is returned as date 00.00.00 */
 | |
| 
 | |
| bool get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month,
 | |
| 			 uint *ret_day)
 | |
| {
 | |
|   uint year,temp,leap_day,day_of_year,days_in_year;
 | |
|   uchar *month_pos;
 | |
|   DBUG_ENTER("get_date_from_daynr");
 | |
| 
 | |
|   if (daynr < 366 || daynr > MAX_DAY_NUMBER)
 | |
|     DBUG_RETURN(1);
 | |
| 
 | |
|   year= (uint) (daynr*100 / 36525L);
 | |
|   temp=(((year-1)/100+1)*3)/4;
 | |
|   day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp;
 | |
|   while (day_of_year > (days_in_year= calc_days_in_year(year)))
 | |
|   {
 | |
|     day_of_year-=days_in_year;
 | |
|     (year)++;
 | |
|   }
 | |
|   leap_day=0;
 | |
|   if (days_in_year == 366)
 | |
|   {
 | |
|     if (day_of_year > 31+28)
 | |
|     {
 | |
|       day_of_year--;
 | |
|       if (day_of_year == 31+28)
 | |
|         leap_day=1;		/* Handle leapyears leapday */
 | |
|     }
 | |
|   }
 | |
|   *ret_month=1;
 | |
|   for (month_pos= days_in_month ;
 | |
|        day_of_year > (uint) *month_pos ;
 | |
|        day_of_year-= *(month_pos++), (*ret_month)++)
 | |
|     ;
 | |
|   *ret_year=year;
 | |
|   *ret_day=day_of_year+leap_day;
 | |
|   DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| 	/* Functions to handle periods */
 | |
| 
 | |
| ulong convert_period_to_month(ulong period)
 | |
| {
 | |
|   ulong a,b;
 | |
|   if (period == 0 || period > 999912)
 | |
|     return 0L;
 | |
|   if ((a=period/100) < YY_PART_YEAR)
 | |
|     a+=2000;
 | |
|   else if (a < 100)
 | |
|     a+=1900;
 | |
|   b=period%100;
 | |
|   return a*12+b-1;
 | |
| }
 | |
| 
 | |
| 
 | |
| ulong convert_month_to_period(ulong month)
 | |
| {
 | |
|   ulong year;
 | |
|   if (month == 0L)
 | |
|     return 0L;
 | |
|   if ((year=month/12) < 100)
 | |
|   {
 | |
|     year+=(year < YY_PART_YEAR) ? 2000 : 1900;
 | |
|   }
 | |
|   return year*100+month%12+1;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool
 | |
| check_date_with_warn(THD *thd, const MYSQL_TIME *ltime,
 | |
|                      date_conv_mode_t fuzzydate, timestamp_type ts_type)
 | |
| {
 | |
|   int unused;
 | |
|   if (check_date(ltime, fuzzydate, &unused))
 | |
|   {
 | |
|     ErrConvTime str(ltime);
 | |
|     make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
|                                  &str, ts_type, nullptr, nullptr, nullptr);
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool
 | |
| adjust_time_range_with_warn(THD *thd, MYSQL_TIME *ltime, uint dec)
 | |
| {
 | |
|   MYSQL_TIME copy= *ltime;
 | |
|   ErrConvTime str(©);
 | |
|   int warnings= 0;
 | |
|   if (check_time_range(ltime, dec, &warnings))
 | |
|     return true;
 | |
|   if (warnings)
 | |
|     thd->push_warning_truncated_wrong_value("time", str.ptr());
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Convert a string to 8-bit representation,
 | |
|   for use in str_to_time/str_to_date/str_to_date.
 | |
|   
 | |
|   In the future to_ascii() can be extended to convert
 | |
|   non-ASCII digits to ASCII digits
 | |
|   (for example, ARABIC-INDIC, DEVANAGARI, BENGALI, and so on)
 | |
|   so DATE/TIME/DATETIME values understand digits in the
 | |
|   respected scripts.
 | |
| */
 | |
| static uint
 | |
| to_ascii(CHARSET_INFO *cs,
 | |
|          const char *src, size_t src_length,
 | |
|          char *dst, size_t dst_length)
 | |
|                      
 | |
| {
 | |
|   int cnvres;
 | |
|   my_wc_t wc;
 | |
|   const char *srcend= src + src_length;
 | |
|   char *dst0= dst, *dstend= dst + dst_length - 1;
 | |
|   while (dst < dstend &&
 | |
|          (cnvres= cs->mb_wc(&wc,
 | |
|                             (const uchar*) src,
 | |
|                             (const uchar*) srcend)) > 0 &&
 | |
|          wc < 128)
 | |
|   {
 | |
|     src+= cnvres;
 | |
|     *dst++= static_cast<char>(wc);
 | |
|   }
 | |
|   *dst= '\0';
 | |
|   return (uint)(dst - dst0);
 | |
| }
 | |
| 
 | |
| 
 | |
| class TemporalAsciiBuffer: public LEX_CSTRING
 | |
| {
 | |
|   char cnv[32];
 | |
| public:
 | |
|   TemporalAsciiBuffer(const char *str, size_t length, CHARSET_INFO *cs)
 | |
|   {
 | |
|     if ((cs->state & MY_CS_NONASCII) != 0)
 | |
|     {
 | |
|       LEX_CSTRING::str= cnv;
 | |
|       LEX_CSTRING::length= to_ascii(cs, str, length, cnv, sizeof(cnv));
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       LEX_CSTRING::str= str;
 | |
|       LEX_CSTRING::length= length;
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| 
 | |
| /* Character set-aware version of ascii_to_datetime_or_date_or_time() */
 | |
| bool Temporal::str_to_datetime_or_date_or_time(THD *thd, MYSQL_TIME_STATUS *st,
 | |
|                                                const char *str, size_t length,
 | |
|                                                CHARSET_INFO *cs,
 | |
|                                                date_mode_t fuzzydate)
 | |
| {
 | |
|   TemporalAsciiBuffer tmp(str, length, cs);
 | |
|   return ascii_to_datetime_or_date_or_time(st, tmp.str, tmp.length, fuzzydate)||
 | |
|          add_nanoseconds(thd, &st->warnings, fuzzydate, st->nanoseconds);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Character set-aware version of str_to_datetime_or_date() */
 | |
| bool Temporal::str_to_datetime_or_date(THD *thd, MYSQL_TIME_STATUS *status,
 | |
|                                        const char *str, size_t length,
 | |
|                                        CHARSET_INFO *cs,
 | |
|                                        date_mode_t flags)
 | |
| {
 | |
|   TemporalAsciiBuffer tmp(str, length, cs);
 | |
|   return ascii_to_datetime_or_date(status, tmp.str, tmp.length, flags) ||
 | |
|          add_nanoseconds(thd, &status->warnings, flags, status->nanoseconds);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Character set-aware version of ascii_to_temporal() */
 | |
| bool Temporal::str_to_temporal(THD *thd, MYSQL_TIME_STATUS *status,
 | |
|                                const char *str, size_t length, CHARSET_INFO *cs,
 | |
|                                date_mode_t flags)
 | |
| {
 | |
|   TemporalAsciiBuffer tmp(str, length, cs);
 | |
|   return ascii_to_temporal(status, tmp.str, tmp.length, flags) ||
 | |
|          add_nanoseconds(thd, &status->warnings, flags, status->nanoseconds);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Character set-aware version of str_to_DDhhmmssff() */
 | |
| bool Interval_DDhhmmssff::str_to_DDhhmmssff(MYSQL_TIME_STATUS *status,
 | |
|                                             const char *str, size_t length,
 | |
|                                             CHARSET_INFO *cs, ulong max_hour)
 | |
| {
 | |
|   TemporalAsciiBuffer tmp(str, length, cs);
 | |
|   bool rc= ::str_to_DDhhmmssff(tmp.str, tmp.length, this, UINT_MAX32, status);
 | |
|   DBUG_ASSERT(status->warnings || !rc);
 | |
|   return rc;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Convert a timestamp string to a MYSQL_TIME value and produce a warning 
 | |
|   if string was truncated during conversion.
 | |
| 
 | |
|   NOTE
 | |
|     See description of str_to_datetime_xxx() for more information.
 | |
| */
 | |
| 
 | |
| bool
 | |
| str_to_datetime_with_warn(THD *thd, CHARSET_INFO *cs,
 | |
|                           const char *str, size_t length, MYSQL_TIME *to,
 | |
|                           date_mode_t mode)
 | |
| {
 | |
|   Temporal::Warn_push warn(thd, nullptr, nullptr, nullptr, to, mode);
 | |
|   Temporal_hybrid *t= new(to) Temporal_hybrid(thd, &warn, str, length, cs, mode);
 | |
|   return !t->is_valid_temporal();
 | |
| }
 | |
| 
 | |
| 
 | |
| bool double_to_datetime_with_warn(THD *thd, double value, MYSQL_TIME *ltime,
 | |
|                                   date_mode_t fuzzydate,
 | |
|                                   const TABLE_SHARE *s, const char *field_name)
 | |
| {
 | |
|   Temporal::Warn_push warn(thd, s ? s->db.str : nullptr,
 | |
|                            s ? s->table_name.str : nullptr,
 | |
|                            field_name, ltime, fuzzydate);
 | |
|   Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, &warn, value, fuzzydate);
 | |
|   return !t->is_valid_temporal();
 | |
| }
 | |
| 
 | |
| 
 | |
| bool decimal_to_datetime_with_warn(THD *thd, const my_decimal *value,
 | |
|                                    MYSQL_TIME *ltime,
 | |
|                                    date_mode_t fuzzydate,
 | |
|                                    const TABLE_SHARE *s, const char *field_name)
 | |
| {
 | |
|   Temporal::Warn_push warn(thd, s ? s->db.str : nullptr,
 | |
|                            s ? s->table_name.str : nullptr,
 | |
|                            field_name, ltime, fuzzydate);
 | |
|   Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, &warn, value, fuzzydate);
 | |
|   return !t->is_valid_temporal();
 | |
| }
 | |
| 
 | |
| 
 | |
| bool int_to_datetime_with_warn(THD *thd, const Longlong_hybrid &nr,
 | |
|                                MYSQL_TIME *ltime,
 | |
|                                date_mode_t fuzzydate,
 | |
|                                const TABLE_SHARE *s, const char *field_name)
 | |
| {
 | |
|   /*
 | |
|     Note: conversion from an integer to TIME can overflow to '838:59:59.999999',
 | |
|     so the conversion result can have fractional digits.
 | |
|   */
 | |
|   Temporal::Warn_push warn(thd, s ? s->db.str : nullptr,
 | |
|                            s ? s->table_name.str : nullptr,
 | |
|                            field_name, ltime, fuzzydate);
 | |
|   Temporal_hybrid *t= new (ltime) Temporal_hybrid(thd, &warn, nr, fuzzydate);
 | |
|   return !t->is_valid_temporal();
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Convert a datetime from broken-down MYSQL_TIME representation to
 | |
|   corresponding TIMESTAMP value.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     TIME_to_timestamp()
 | |
|       thd             - current thread
 | |
|       t               - datetime in broken-down representation, 
 | |
|       error_code      - 0, if the conversion was successful;
 | |
|                         ER_WARN_DATA_OUT_OF_RANGE, if t contains datetime value
 | |
|                            which is out of TIMESTAMP range;
 | |
|                         ER_WARN_INVALID_TIMESTAMP, if t represents value which
 | |
|                            doesn't exists (falls into the spring time-gap).
 | |
|    
 | |
|   RETURN
 | |
|      Number seconds in UTC since start of Unix Epoch corresponding to t.
 | |
|      0 - in case of ER_WARN_DATA_OUT_OF_RANGE
 | |
| */
 | |
| 
 | |
| my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code)
 | |
| {
 | |
|   thd->used|= THD::TIME_ZONE_USED;
 | |
|   return thd->variables.time_zone->TIME_to_gmt_sec(t, error_code);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Convert a system time structure to TIME
 | |
| */
 | |
| 
 | |
| void localtime_to_TIME(MYSQL_TIME *to, struct tm *from)
 | |
| {
 | |
|   to->neg=0;
 | |
|   to->second_part=0;
 | |
|   to->year=	(int) ((from->tm_year+1900) % 10000);
 | |
|   to->month=	(int) from->tm_mon+1;
 | |
|   to->day=	(int) from->tm_mday;
 | |
|   to->hour=	(int) from->tm_hour;
 | |
|   to->minute=	(int) from->tm_min;
 | |
|   to->second=   (int) from->tm_sec;
 | |
| }
 | |
| 
 | |
| 
 | |
| void calc_time_from_sec(MYSQL_TIME *to, ulong seconds, ulong microseconds)
 | |
| {
 | |
|   long t_seconds;
 | |
|   // to->neg is not cleared, it may already be set to a useful value
 | |
|   to->time_type= MYSQL_TIMESTAMP_TIME;
 | |
|   to->year= 0;
 | |
|   to->month= 0;
 | |
|   to->day= 0;
 | |
|   to->hour= seconds/3600L;
 | |
|   t_seconds= seconds%3600L;
 | |
|   to->minute= t_seconds/60L;
 | |
|   to->second= t_seconds%60L;
 | |
|   to->second_part= microseconds;
 | |
| }
 | |
| 
 | |
| 
 | |
| KNOWN_DATE_TIME_FORMAT known_date_time_formats[6]=
 | |
| {
 | |
|   {"USA", "%m.%d.%Y", "%Y-%m-%d %H.%i.%s", "%h:%i:%s %p" },
 | |
|   {"JIS", "%Y-%m-%d", "%Y-%m-%d %H:%i:%s", "%H:%i:%s" },
 | |
|   {"ISO", "%Y-%m-%d", "%Y-%m-%d %H:%i:%s", "%H:%i:%s" },
 | |
|   {"EUR", "%d.%m.%Y", "%Y-%m-%d %H.%i.%s", "%H.%i.%s" },
 | |
|   {"INTERNAL", "%Y%m%d",   "%Y%m%d%H%i%s", "%H%i%s" },
 | |
|   { 0, 0, 0, 0 }
 | |
| };
 | |
| 
 | |
| 
 | |
| const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
 | |
| 				     timestamp_type type)
 | |
| {
 | |
|   switch (type) {
 | |
|   case MYSQL_TIMESTAMP_DATE:
 | |
|     return format->date_format;
 | |
|   case MYSQL_TIMESTAMP_DATETIME:
 | |
|     return format->datetime_format;
 | |
|   case MYSQL_TIMESTAMP_TIME:
 | |
|     return format->time_format;
 | |
|   default:
 | |
|     DBUG_ASSERT(0);				// Impossible
 | |
|     return 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Convert TIME/DATE/DATETIME value to String.
 | |
|   @param l_time   DATE value
 | |
|   @param OUT str  String to convert to
 | |
|   @param dec      Number of fractional digits.
 | |
| */
 | |
| bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec)
 | |
| {
 | |
|   if (str->alloc(MAX_DATE_STRING_REP_LENGTH))
 | |
|     return true;
 | |
|   str->set_charset(&my_charset_numeric);
 | |
|   str->length(my_TIME_to_str(ltime, const_cast<char*>(str->ptr()), dec));
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| void make_truncated_value_warning(THD *thd,
 | |
|                                   Sql_condition::enum_warning_level level,
 | |
|                                   const ErrConv *sval,
 | |
| 				  timestamp_type time_type,
 | |
|                                   const char *db_name, const char *table_name,
 | |
|                                   const char *field_name)
 | |
| {
 | |
|   const char *type_str= Temporal::type_name_by_timestamp_type(time_type);
 | |
|   return thd->push_warning_wrong_or_truncated_value
 | |
|     (level, time_type <= MYSQL_TIMESTAMP_ERROR, type_str, sval->ptr(),
 | |
|      db_name, table_name, field_name);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Daynumber from year 0 to 9999-12-31 */
 | |
| #define COMBINE(X)                                                      \
 | |
|                (((((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(THD *thd, MYSQL_TIME *ltime, interval_type int_type,
 | |
|                        const INTERVAL &interval, bool push_warn)
 | |
| {
 | |
|   long sign;
 | |
| 
 | |
|   sign= (interval.neg == (bool)ltime->neg ? 1 : -1);
 | |
| 
 | |
|   switch (int_type) {
 | |
|   case INTERVAL_SECOND:
 | |
|   case INTERVAL_SECOND_MICROSECOND:
 | |
|   case INTERVAL_MICROSECOND:
 | |
|   case INTERVAL_MINUTE:
 | |
|   case INTERVAL_HOUR:
 | |
|   case INTERVAL_MINUTE_MICROSECOND:
 | |
|   case INTERVAL_MINUTE_SECOND:
 | |
|   case INTERVAL_HOUR_MICROSECOND:
 | |
|   case INTERVAL_HOUR_SECOND:
 | |
|   case INTERVAL_HOUR_MINUTE:
 | |
|   case INTERVAL_DAY_MICROSECOND:
 | |
|   case INTERVAL_DAY_SECOND:
 | |
|   case INTERVAL_DAY_MINUTE:
 | |
|   case INTERVAL_DAY_HOUR:
 | |
|   case INTERVAL_DAY:
 | |
|   {
 | |
|     longlong usec, daynr;
 | |
|     my_bool neg= 0;
 | |
|     enum enum_mysql_timestamp_type time_type= ltime->time_type;
 | |
| 
 | |
|     if (((ulonglong) interval.day +
 | |
|          (ulonglong) interval.hour / 24 +
 | |
|          (ulonglong) interval.minute / 24 / 60 +
 | |
|          (ulonglong) interval.second / 24 / 60 / 60) > MAX_DAY_NUMBER)
 | |
|       goto invalid_date;
 | |
| 
 | |
|     if (time_type != MYSQL_TIMESTAMP_TIME)
 | |
|       ltime->day+= calc_daynr(ltime->year, ltime->month, 1) - 1;
 | |
| 
 | |
|     usec= COMBINE(ltime) + sign*COMBINE(&interval);
 | |
| 
 | |
|     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)
 | |
|     {
 | |
|       if (usec > TIME_MAX_HOUR)
 | |
|         goto invalid_date;
 | |
|       ltime->hour= static_cast<uint>(usec);
 | |
|       ltime->day= 0;
 | |
|       return 0;
 | |
|     }
 | |
|     else if (ltime->neg)
 | |
|       goto invalid_date;
 | |
| 
 | |
|     if (int_type != INTERVAL_DAY)
 | |
|       ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date
 | |
| 
 | |
|     ltime->hour= GET_PART(usec, 24);
 | |
|     daynr= usec;
 | |
| 
 | |
|     /* Day number from year 0 to 9999-12-31 */
 | |
|     if (get_date_from_daynr((long) daynr, <ime->year, <ime->month,
 | |
|                             <ime->day))
 | |
|       goto invalid_date;
 | |
|     break;
 | |
|   }
 | |
|   case INTERVAL_WEEK:
 | |
|   {
 | |
|     longlong period= calc_daynr(ltime->year, ltime->month, ltime->day) +
 | |
|                      (longlong) sign * (longlong) interval.day;
 | |
|     if (period < 0 || period > 0x7FFFFFFF)
 | |
|       goto invalid_date;
 | |
|     /* Daynumber from year 0 to 9999-12-31 */
 | |
|     if (get_date_from_daynr((long) period,<ime->year,<ime->month,
 | |
|                             <ime->day))
 | |
|       goto invalid_date;
 | |
|     break;
 | |
|   }
 | |
|   case INTERVAL_YEAR:
 | |
|     ltime->year+= sign * (long) interval.year;
 | |
|     if ((ulong) ltime->year >= 10000L)
 | |
|       goto invalid_date;
 | |
|     if (ltime->month == 2 && ltime->day == 29 &&
 | |
| 	calc_days_in_year(ltime->year) != 366)
 | |
|       ltime->day=28;				// Was leap-year
 | |
|     break;
 | |
|   case INTERVAL_YEAR_MONTH:
 | |
|   case INTERVAL_QUARTER:
 | |
|   case INTERVAL_MONTH:
 | |
|   {
 | |
|     long period= (ltime->year*12 + sign * (long) interval.year*12 +
 | |
| 	         ltime->month-1 + sign * (long) interval.month);
 | |
|     if ((ulong) period >= 120000L)
 | |
|       goto invalid_date;
 | |
|     ltime->year= (uint) (period / 12);
 | |
|     ltime->month= (uint) (period % 12L)+1;
 | |
|     /* Adjust day if the new month doesn't have enough days */
 | |
|     if (ltime->day > days_in_month[ltime->month-1])
 | |
|     {
 | |
|       ltime->day = days_in_month[ltime->month-1];
 | |
|       if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
 | |
| 	ltime->day++;				// Leap-year
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
|   default:
 | |
|     goto null_date;
 | |
|   }
 | |
| 
 | |
|   if (ltime->time_type != MYSQL_TIMESTAMP_TIME)
 | |
|     return 0;                                   // Ok
 | |
| 
 | |
| invalid_date:
 | |
|   if (push_warn)
 | |
|   {
 | |
|     push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
 | |
|                         ER_DATETIME_FUNCTION_OVERFLOW,
 | |
|                         ER_THD(thd, ER_DATETIME_FUNCTION_OVERFLOW),
 | |
|                         ltime->time_type == MYSQL_TIMESTAMP_TIME ?
 | |
|                         "time" : "datetime");
 | |
|   }
 | |
| null_date:
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Calculate difference between two datetime values as seconds + microseconds.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     calc_time_diff()
 | |
|       l_time1         - TIME/DATE/DATETIME value
 | |
|       l_time2         - TIME/DATE/DATETIME value
 | |
|       l_sign          - 1 absolute values are substracted,
 | |
|                         -1 absolute values are added.
 | |
|       seconds_out     - Out parameter where difference between
 | |
|                         l_time1 and l_time2 in seconds is stored.
 | |
|       microseconds_out- Out parameter where microsecond part of difference
 | |
|                         between l_time1 and l_time2 is stored.
 | |
| 
 | |
|   NOTE
 | |
|     This function calculates difference between l_time1 and l_time2 absolute
 | |
|     values. So one should set l_sign and correct result if he want to take
 | |
|     signs into account (i.e. for MYSQL_TIME values).
 | |
| 
 | |
|   RETURN VALUES
 | |
|     Returns sign of difference.
 | |
|     1 means negative result
 | |
|     0 means positive result
 | |
| 
 | |
| */
 | |
| 
 | |
| bool
 | |
| calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
 | |
|                int l_sign, ulonglong *seconds_out, ulong *microseconds_out)
 | |
| {
 | |
|   long days;
 | |
|   bool neg;
 | |
|   longlong microseconds;
 | |
| 
 | |
|   /*
 | |
|     We suppose that if first argument is MYSQL_TIMESTAMP_TIME
 | |
|     the second argument should be TIMESTAMP_TIME also.
 | |
|     We should check it before calc_time_diff call.
 | |
|   */
 | |
|   if (l_time1->time_type == MYSQL_TIMESTAMP_TIME)  // Time value
 | |
|     days= (long)l_time1->day - l_sign * (long)l_time2->day;
 | |
|   else
 | |
|   {
 | |
|     days= calc_daynr((uint) l_time1->year,
 | |
| 		     (uint) l_time1->month,
 | |
| 		     (uint) l_time1->day);
 | |
|     if (l_time2->time_type == MYSQL_TIMESTAMP_TIME)
 | |
|       days-= l_sign * (long)l_time2->day;
 | |
|     else
 | |
|       days-= l_sign*calc_daynr((uint) l_time2->year,
 | |
| 			       (uint) l_time2->month,
 | |
| 			       (uint) l_time2->day);
 | |
|   }
 | |
| 
 | |
|   microseconds= ((longlong)days * SECONDS_IN_24H +
 | |
|                  (longlong)(l_time1->hour*3600LL +
 | |
|                             l_time1->minute*60L +
 | |
|                             l_time1->second) -
 | |
|                  l_sign*(longlong)(l_time2->hour*3600LL +
 | |
|                                    l_time2->minute*60L +
 | |
|                                    l_time2->second)) * 1000000LL +
 | |
|                 (longlong)l_time1->second_part -
 | |
|                 l_sign*(longlong)l_time2->second_part;
 | |
| 
 | |
|   neg= 0;
 | |
|   if (microseconds < 0)
 | |
|   {
 | |
|     microseconds= -microseconds;
 | |
|     neg= 1;
 | |
|   }
 | |
|   *seconds_out= (ulonglong) microseconds/1000000L;
 | |
|   *microseconds_out= (ulong) (microseconds%1000000L);
 | |
|   return neg;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
 | |
|                     int l_sign, MYSQL_TIME *l_time3, date_mode_t fuzzydate)
 | |
| {
 | |
|   ulonglong seconds;
 | |
|   ulong microseconds;
 | |
|   bzero((char *) l_time3, sizeof(*l_time3));
 | |
|   l_time3->neg= calc_time_diff(l_time1, l_time2, l_sign,
 | |
| 			       &seconds, µseconds);
 | |
|   /*
 | |
|     For MYSQL_TIMESTAMP_TIME only:
 | |
|       If first argument was negative and diff between arguments
 | |
|       is non-zero we need to swap sign to get proper result.
 | |
|   */
 | |
|   if (l_time1->neg && (seconds || microseconds))
 | |
|     l_time3->neg= 1 - l_time3->neg;         // Swap sign of result
 | |
| 
 | |
|   /*
 | |
|     seconds is longlong, when casted to long it may become a small number
 | |
|     even if the original seconds value was too large and invalid.
 | |
|     as a workaround we limit seconds by a large invalid long number
 | |
|     ("invalid" means > TIME_MAX_SECOND)
 | |
|   */
 | |
|   set_if_smaller(seconds, INT_MAX32);
 | |
|   calc_time_from_sec(l_time3, (ulong) seconds, microseconds);
 | |
|   return ((fuzzydate & TIME_NO_ZERO_DATE) && (seconds == 0) &&
 | |
|           (microseconds == 0));
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Compares 2 MYSQL_TIME structures
 | |
| 
 | |
|   SYNOPSIS
 | |
|     my_time_compare()
 | |
| 
 | |
|       a - first time
 | |
|       b - second time
 | |
| 
 | |
|   RETURN VALUE
 | |
|    -1   - a < b
 | |
|     0   - a == b
 | |
|     1   - a > b
 | |
| 
 | |
| */
 | |
| 
 | |
| int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b)
 | |
| {
 | |
|   ulonglong a_t= pack_time(a);
 | |
|   ulonglong b_t= pack_time(b);
 | |
| 
 | |
|   if (a_t < b_t)
 | |
|     return -1;
 | |
|   if (a_t > b_t)
 | |
|     return 1;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Convert TIME to DATETIME.
 | |
|   @param   ltime    The value to convert.
 | |
|   @return  false on success, true of error (negative time).
 | |
| */
 | |
| bool time_to_datetime(MYSQL_TIME *ltime)
 | |
| {
 | |
|   DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_TIME);
 | |
|   DBUG_ASSERT(ltime->year == 0);
 | |
|   DBUG_ASSERT(ltime->month == 0);
 | |
|   DBUG_ASSERT(ltime->day == 0);
 | |
|   if (ltime->neg)
 | |
|     return true;
 | |
|   uint day= ltime->hour / 24;
 | |
|   ltime->hour%= 24;
 | |
|   ltime->month= day / 31;
 | |
|   ltime->day= day % 31;  
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*** Conversion from TIME to DATETIME ***/
 | |
| 
 | |
| /*
 | |
|   Simple case: TIME is within normal 24 hours internal.
 | |
|   Mix DATE part of ldate and TIME part of ltime together.
 | |
| */
 | |
| static void
 | |
| mix_date_and_time_simple(MYSQL_TIME *ldate, const MYSQL_TIME *ltime)
 | |
| {
 | |
|   DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE ||
 | |
|               ldate->time_type == MYSQL_TIMESTAMP_DATETIME);
 | |
|   ldate->hour= ltime->hour;
 | |
|   ldate->minute= ltime->minute;
 | |
|   ldate->second= ltime->second;
 | |
|   ldate->second_part= ltime->second_part;
 | |
|   ldate->time_type= MYSQL_TIMESTAMP_DATETIME;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Complex case: TIME is negative or outside of the 24 hour interval.
 | |
| */
 | |
| static void
 | |
| mix_date_and_time_complex(MYSQL_TIME *ldate, const MYSQL_TIME *ltime)
 | |
| {
 | |
|   DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE ||
 | |
|               ldate->time_type == MYSQL_TIMESTAMP_DATETIME);
 | |
|   ulonglong seconds;
 | |
|   ulong days, useconds;
 | |
|   int sign= ltime->neg ? 1 : -1;
 | |
|   ldate->neg= calc_time_diff(ldate, ltime, sign, &seconds, &useconds);
 | |
| 
 | |
|   DBUG_ASSERT(!ldate->neg);
 | |
|   DBUG_ASSERT(ldate->year > 0);
 | |
| 
 | |
|   days= (long) (seconds / SECONDS_IN_24H);
 | |
|   calc_time_from_sec(ldate, seconds % SECONDS_IN_24H, useconds);
 | |
|   get_date_from_daynr(days, &ldate->year, &ldate->month, &ldate->day);
 | |
|   ldate->time_type= MYSQL_TIMESTAMP_DATETIME;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Mix a date value and a time value.
 | |
| 
 | |
|   @param  IN/OUT  ldate  Date value.
 | |
|   @param          ltime  Time value.
 | |
| */
 | |
| static void
 | |
| mix_date_and_time(MYSQL_TIME *to, const MYSQL_TIME *from)
 | |
| {
 | |
|   if (!from->neg && from->hour < 24)
 | |
|     mix_date_and_time_simple(to, from);
 | |
|   else
 | |
|     mix_date_and_time_complex(to, from);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Get current date in DATE format
 | |
| */
 | |
| void set_current_date(THD *thd, MYSQL_TIME *to)
 | |
| {
 | |
|   thd->variables.time_zone->gmt_sec_to_TIME(to, thd->query_start());
 | |
|   thd->used|= THD::TIME_ZONE_USED;
 | |
|   datetime_to_date(to);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   5.5 compatible conversion from TIME to DATETIME
 | |
| */
 | |
| static bool
 | |
| time_to_datetime_old(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to)
 | |
| {
 | |
|   DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME);
 | |
| 
 | |
|   if (from->neg)
 | |
|     return true;
 | |
| 
 | |
|   /* Set the date part */
 | |
|   uint day= from->hour / 24;
 | |
|   to->day= day % 31;
 | |
|   to->month= day / 31;
 | |
|   to->year= 0;
 | |
|   /* Set the time part */
 | |
|   to->hour= from->hour % 24;
 | |
|   to->minute= from->minute;
 | |
|   to->second= from->second;
 | |
|   to->second_part= from->second_part;
 | |
|   /* set sign and type */
 | |
|   to->neg= 0;
 | |
|   to->time_type= MYSQL_TIMESTAMP_DATETIME;
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Convert time to datetime.
 | |
| 
 | |
|   The time value is added to the current datetime value.
 | |
|   @param  IN  ltime    Time value to convert from.
 | |
|   @param  OUT ltime2   Datetime value to convert to.
 | |
| */
 | |
| bool
 | |
| time_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to)
 | |
| {
 | |
|   if (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST)
 | |
|     return time_to_datetime_old(thd, from, to);
 | |
|   set_current_date(thd, to);
 | |
|   mix_date_and_time(to, from);
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| bool
 | |
| time_to_datetime_with_warn(THD *thd,
 | |
|                            const MYSQL_TIME *from, MYSQL_TIME *to,
 | |
|                            date_conv_mode_t fuzzydate)
 | |
| {
 | |
|   int warn= 0;
 | |
|   DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME);
 | |
|   /*
 | |
|     After time_to_datetime() we need to do check_date(), as
 | |
|     the caller may want TIME_NO_ZERO_DATE or TIME_NO_ZERO_IN_DATE.
 | |
|     Note, the SQL standard time->datetime conversion mode always returns
 | |
|     a valid date based on CURRENT_DATE. So we need to do check_date()
 | |
|     only in the old mode.
 | |
|   */
 | |
|   if (time_to_datetime(thd, from, to) ||
 | |
|       ((thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) &&
 | |
|         check_date(to, fuzzydate, &warn)))
 | |
|   {
 | |
|     ErrConvTime str(from);
 | |
|     thd->push_warning_truncated_wrong_value("datetime", str.ptr());
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| longlong pack_time(const MYSQL_TIME *my_time)
 | |
| {
 | |
|   return  ((((((my_time->year     * 13ULL +
 | |
|                my_time->month)    * 32ULL +
 | |
|                my_time->day)      * 24ULL +
 | |
|                my_time->hour)     * 60ULL +
 | |
|                my_time->minute)   * 60ULL +
 | |
|                my_time->second)   * 1000000ULL +
 | |
|                my_time->second_part) * (my_time->neg ? -1 : 1);
 | |
| }
 | |
| 
 | |
| #define get_one(WHERE, FACTOR) WHERE= (ulong)(packed % FACTOR); packed/= FACTOR
 | |
| 
 | |
| void unpack_time(longlong packed, MYSQL_TIME *my_time,
 | |
|                      enum_mysql_timestamp_type ts_type)
 | |
| {
 | |
|   if ((my_time->neg= packed < 0))
 | |
|     packed= -packed;
 | |
|   get_one(my_time->second_part, 1000000ULL);
 | |
|   get_one(my_time->second,           60U);
 | |
|   get_one(my_time->minute,           60U);
 | |
|   get_one(my_time->hour,             24U);
 | |
|   get_one(my_time->day,              32U);
 | |
|   get_one(my_time->month,            13U);
 | |
|   my_time->year= (uint)packed;
 | |
|   my_time->time_type= ts_type;
 | |
|   switch (ts_type) {
 | |
|   case MYSQL_TIMESTAMP_TIME:
 | |
|     my_time->hour+= (my_time->month * 32 + my_time->day) * 24;
 | |
|     my_time->month= my_time->day= 0;
 | |
|     break;
 | |
|   case MYSQL_TIMESTAMP_DATE:
 | |
|     my_time->hour= my_time->minute= my_time->second= my_time->second_part= 0;
 | |
|     break;
 | |
|   case MYSQL_TIMESTAMP_NONE:
 | |
|   case MYSQL_TIMESTAMP_ERROR:
 | |
|     DBUG_ASSERT(0);
 | |
|   case MYSQL_TIMESTAMP_DATETIME:
 | |
|     break;
 | |
|   }
 | |
| }
 | 
