mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 15:50:51 +03:00 
			
		
		
		
	 e53a73e26c
			
		
	
	e53a73e26c
	
	
	
		
			
			Faster thr_alarm() Added 'Opened_files' status variable to track calls to my_open() Don't give warnings when running mysql_install_db Added option --source-install to mysql_install_db I had to do the following renames() as used polymorphism didn't work with Forte compiler on 64 bit systems index_read() -> index_read_map() index_read_idx() -> index_read_idx_map() index_read_last() -> index_read_last_map()
		
			
				
	
	
		
			1245 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1245 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* Copyright (C) 2004-2006 MySQL AB
 | |
| 
 | |
|  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 | |
| 
 | |
| #include <my_time.h>
 | |
| #include <m_string.h>
 | |
| #include <m_ctype.h>
 | |
| /* Windows version of localtime_r() is declared in my_ptrhead.h */
 | |
| #include <my_pthread.h>
 | |
| 
 | |
| ulonglong log_10_int[20]=
 | |
| {
 | |
|   1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
 | |
|   ULL(100000000), ULL(1000000000), ULL(10000000000), ULL(100000000000),
 | |
|   ULL(1000000000000), ULL(10000000000000), ULL(100000000000000),
 | |
|   ULL(1000000000000000), ULL(10000000000000000), ULL(100000000000000000),
 | |
|   ULL(1000000000000000000), ULL(10000000000000000000)
 | |
| };
 | |
| 
 | |
| 
 | |
| /* Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format */
 | |
| 
 | |
| static uchar internal_format_positions[]=
 | |
| {0, 1, 2, 3, 4, 5, 6, (uchar) 255};
 | |
| 
 | |
| static char time_separator=':';
 | |
| 
 | |
| static ulong const days_at_timestart=719528;	/* daynr at 1970.01.01 */
 | |
| uchar days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
 | |
| 
 | |
| /*
 | |
|   Offset of system time zone from UTC in seconds used to speed up 
 | |
|   work of my_system_gmt_sec() function.
 | |
| */
 | |
| static long my_time_zone=0;
 | |
| 
 | |
| 
 | |
| /* Calc days in one year. works with 0 <= year <= 99 */
 | |
| 
 | |
| uint calc_days_in_year(uint year)
 | |
| {
 | |
|   return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
 | |
|           366 : 365);
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Check datetime value for validity according to flags.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     check_date()
 | |
|       ltime          Date to check.
 | |
|       not_zero_date  ltime is not the zero date
 | |
|       flags          flags to check
 | |
|       was_cut        set to 2 if value was truncated.
 | |
| 		     NOTE: This is not touched if value was not truncated
 | |
|   NOTES
 | |
|     Here we assume that year and month is ok !
 | |
|     If month is 0 we allow any date. (This only happens if we allow zero
 | |
|     date parts in str_to_datetime())
 | |
|     Disallow dates with zero year and non-zero month and/or day.
 | |
| 
 | |
|   RETURN
 | |
|     0  ok
 | |
|     1  error
 | |
| */
 | |
| 
 | |
| my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
 | |
|                    ulong flags, int *was_cut)
 | |
| {
 | |
|   if (not_zero_date)
 | |
|   {
 | |
|     if ((((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
 | |
|          (ltime->month == 0 || ltime->day == 0)) ||
 | |
|         (!(flags & TIME_INVALID_DATES) &&
 | |
|          ltime->month && ltime->day > days_in_month[ltime->month-1] &&
 | |
|          (ltime->month != 2 || calc_days_in_year(ltime->year) != 366 ||
 | |
|           ltime->day != 29)))
 | |
|     {
 | |
|       *was_cut= 2;
 | |
|       return TRUE;
 | |
|     }
 | |
|   }
 | |
|   else if (flags & TIME_NO_ZERO_DATE)
 | |
|   {
 | |
|     /*
 | |
|       We don't set *was_cut here to signal that the problem was a zero date
 | |
|       and not an invalid date
 | |
|     */
 | |
|     return TRUE;
 | |
|   }
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Convert a timestamp string to a MYSQL_TIME value.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     str_to_datetime()
 | |
|     str                 String to parse
 | |
|     length              Length of string
 | |
|     l_time              Date is stored here
 | |
|     flags               Bitmap of following items
 | |
|                         TIME_FUZZY_DATE    Set if we should allow partial dates
 | |
|                         TIME_DATETIME_ONLY Set if we only allow full datetimes.
 | |
|                         TIME_NO_ZERO_IN_DATE	Don't allow partial dates
 | |
|                         TIME_NO_ZERO_DATE	Don't allow 0000-00-00 date
 | |
|                         TIME_INVALID_DATES	Allow 2000-02-31
 | |
|     was_cut             0	Value ok
 | |
| 			1       If value was cut during conversion
 | |
| 			2	Date part was within ranges but date was wrong
 | |
| 
 | |
|   DESCRIPTION
 | |
|     At least the following formats are recogniced (based on number of digits)
 | |
|     YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
 | |
|     YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
 | |
|     YYYYMMDDTHHMMSS  where T is a the character T (ISO8601)
 | |
|     Also dates where all parts are zero are allowed
 | |
| 
 | |
|     The second part may have an optional .###### fraction part.
 | |
| 
 | |
|   NOTES
 | |
|    This function should work with a format position vector as long as the
 | |
|    following things holds:
 | |
|    - All date are kept together and all time parts are kept together
 | |
|    - Date and time parts must be separated by blank
 | |
|    - Second fractions must come after second part and be separated
 | |
|      by a '.'.  (The second fractions are optional)
 | |
|    - AM/PM must come after second fractions (or after seconds if no fractions)
 | |
|    - Year must always been specified.
 | |
|    - If time is before date, then we will use datetime format only if
 | |
|      the argument consist of two parts, separated by space.
 | |
|      Otherwise we will assume the argument is a date.
 | |
|    - The hour part must be specified in hour-minute-second order.
 | |
| 
 | |
|   RETURN VALUES
 | |
|     MYSQL_TIMESTAMP_NONE        String wasn't a timestamp, like
 | |
|                                 [DD [HH:[MM:[SS]]]].fraction.
 | |
|                                 l_time is not changed.
 | |
|     MYSQL_TIMESTAMP_DATE        DATE string (YY MM and DD parts ok)
 | |
|     MYSQL_TIMESTAMP_DATETIME    Full timestamp
 | |
|     MYSQL_TIMESTAMP_ERROR       Timestamp with wrong values.
 | |
|                                 All elements in l_time is set to 0
 | |
| */
 | |
| 
 | |
| #define MAX_DATE_PARTS 8
 | |
| 
 | |
| enum enum_mysql_timestamp_type
 | |
| str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
 | |
|                 uint flags, int *was_cut)
 | |
| {
 | |
|   uint field_length, year_length, digits, i, number_of_fields;
 | |
|   uint date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
 | |
|   uint add_hours= 0, start_loop;
 | |
|   ulong not_zero_date, allow_space;
 | |
|   my_bool is_internal_format;
 | |
|   const char *pos, *last_field_pos;
 | |
|   const char *end=str+length;
 | |
|   const uchar *format_position;
 | |
|   my_bool found_delimitier= 0, found_space= 0;
 | |
|   uint frac_pos, frac_len;
 | |
|   DBUG_ENTER("str_to_datetime");
 | |
|   DBUG_PRINT("ENTER",("str: %.*s",length,str));
 | |
| 
 | |
|   LINT_INIT(field_length);
 | |
|   LINT_INIT(year_length);
 | |
|   LINT_INIT(last_field_pos);
 | |
| 
 | |
|   *was_cut= 0;
 | |
| 
 | |
|   /* Skip space at start */
 | |
|   for (; str != end && my_isspace(&my_charset_latin1, *str) ; str++)
 | |
|     ;
 | |
|   if (str == end || ! my_isdigit(&my_charset_latin1, *str))
 | |
|   {
 | |
|     *was_cut= 1;
 | |
|     DBUG_RETURN(MYSQL_TIMESTAMP_NONE);
 | |
|   }
 | |
| 
 | |
|   is_internal_format= 0;
 | |
|   /* This has to be changed if want to activate different timestamp formats */
 | |
|   format_position= internal_format_positions;
 | |
| 
 | |
|   /*
 | |
|     Calculate number of digits in first part.
 | |
|     If length= 8 or >= 14 then year is of format YYYY.
 | |
|     (YYYY-MM-DD,  YYYYMMDD, YYYYYMMDDHHMMSS)
 | |
|   */
 | |
|   for (pos=str;
 | |
|        pos != end && (my_isdigit(&my_charset_latin1,*pos) || *pos == 'T');
 | |
|        pos++)
 | |
|     ;
 | |
| 
 | |
|   digits= (uint) (pos-str);
 | |
|   start_loop= 0;                                /* Start of scan loop */
 | |
|   date_len[format_position[0]]= 0;              /* Length of year field */
 | |
|   if (pos == end || *pos == '.')
 | |
|   {
 | |
|     /* Found date in internal format (only numbers like YYYYMMDD) */
 | |
|     year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
 | |
|     field_length= year_length;
 | |
|     is_internal_format= 1;
 | |
|     format_position= internal_format_positions;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     if (format_position[0] >= 3)                /* If year is after HHMMDD */
 | |
|     {
 | |
|       /*
 | |
|         If year is not in first part then we have to determinate if we got
 | |
|         a date field or a datetime field.
 | |
|         We do this by checking if there is two numbers separated by
 | |
|         space in the input.
 | |
|       */
 | |
|       while (pos < end && !my_isspace(&my_charset_latin1, *pos))
 | |
|         pos++;
 | |
|       while (pos < end && !my_isdigit(&my_charset_latin1, *pos))
 | |
|         pos++;
 | |
|       if (pos == end)
 | |
|       {
 | |
|         if (flags & TIME_DATETIME_ONLY)
 | |
|         {
 | |
|           *was_cut= 1;
 | |
|           DBUG_RETURN(MYSQL_TIMESTAMP_NONE);   /* Can't be a full datetime */
 | |
|         }
 | |
|         /* Date field.  Set hour, minutes and seconds to 0 */
 | |
|         date[0]= date[1]= date[2]= date[3]= date[4]= 0;
 | |
|         start_loop= 5;                         /* Start with first date part */
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     field_length= format_position[0] == 0 ? 4 : 2;
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|     Only allow space in the first "part" of the datetime field and:
 | |
|     - after days, part seconds
 | |
|     - before and after AM/PM (handled by code later)
 | |
| 
 | |
|     2003-03-03 20:00:20 AM
 | |
|     20:00:20.000000 AM 03-03-2000
 | |
|   */
 | |
|   i= max((uint) format_position[0], (uint) format_position[1]);
 | |
|   set_if_bigger(i, (uint) format_position[2]);
 | |
|   allow_space= ((1 << i) | (1 << format_position[6]));
 | |
|   allow_space&= (1 | 2 | 4 | 8);
 | |
| 
 | |
|   not_zero_date= 0;
 | |
|   for (i = start_loop;
 | |
|        i < MAX_DATE_PARTS-1 && str != end &&
 | |
|          my_isdigit(&my_charset_latin1,*str);
 | |
|        i++)
 | |
|   {
 | |
|     const char *start= str;
 | |
|     ulong tmp_value= (uint) (uchar) (*str++ - '0');
 | |
|     while (str != end && my_isdigit(&my_charset_latin1,str[0]) &&
 | |
|            (!is_internal_format || --field_length))
 | |
|     {
 | |
|       tmp_value=tmp_value*10 + (ulong) (uchar) (*str - '0');
 | |
|       str++;
 | |
|     }
 | |
|     date_len[i]= (uint) (str - start);
 | |
|     if (tmp_value > 999999)                     /* Impossible date part */
 | |
|     {
 | |
|       *was_cut= 1;
 | |
|       DBUG_RETURN(MYSQL_TIMESTAMP_NONE);
 | |
|     }
 | |
|     date[i]=tmp_value;
 | |
|     not_zero_date|= tmp_value;
 | |
| 
 | |
|     /* Length of next field */
 | |
|     field_length= format_position[i+1] == 0 ? 4 : 2;
 | |
| 
 | |
|     if ((last_field_pos= str) == end)
 | |
|     {
 | |
|       i++;                                      /* Register last found part */
 | |
|       break;
 | |
|     }
 | |
|     /* Allow a 'T' after day to allow CCYYMMDDT type of fields */
 | |
|     if (i == format_position[2] && *str == 'T')
 | |
|     {
 | |
|       str++;                                    /* ISO8601:  CCYYMMDDThhmmss */
 | |
|       continue;
 | |
|     }
 | |
|     if (i == format_position[5])                /* Seconds */
 | |
|     {
 | |
|       if (*str == '.')                          /* Followed by part seconds */
 | |
|       {
 | |
|         str++;
 | |
|         field_length= 6;                        /* 6 digits */
 | |
|       }
 | |
|       continue;
 | |
|     }
 | |
|     while (str != end &&
 | |
|            (my_ispunct(&my_charset_latin1,*str) ||
 | |
|             my_isspace(&my_charset_latin1,*str)))
 | |
|     {
 | |
|       if (my_isspace(&my_charset_latin1,*str))
 | |
|       {
 | |
|         if (!(allow_space & (1 << i)))
 | |
|         {
 | |
|           *was_cut= 1;
 | |
|           DBUG_RETURN(MYSQL_TIMESTAMP_NONE);
 | |
|         }
 | |
|         found_space= 1;
 | |
|       }
 | |
|       str++;
 | |
|       found_delimitier= 1;                      /* Should be a 'normal' date */
 | |
|     }
 | |
|     /* Check if next position is AM/PM */
 | |
|     if (i == format_position[6])                /* Seconds, time for AM/PM */
 | |
|     {
 | |
|       i++;                                      /* Skip AM/PM part */
 | |
|       if (format_position[7] != 255)            /* If using AM/PM */
 | |
|       {
 | |
|         if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
 | |
|         {
 | |
|           if (str[0] == 'p' || str[0] == 'P')
 | |
|             add_hours= 12;
 | |
|           else if (str[0] != 'a' || str[0] != 'A')
 | |
|             continue;                           /* Not AM/PM */
 | |
|           str+= 2;                              /* Skip AM/PM */
 | |
|           /* Skip space after AM/PM */
 | |
|           while (str != end && my_isspace(&my_charset_latin1,*str))
 | |
|             str++;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     last_field_pos= str;
 | |
|   }
 | |
|   if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
 | |
|   {
 | |
|     *was_cut= 1;
 | |
|     DBUG_RETURN(MYSQL_TIMESTAMP_NONE);          /* Can't be a datetime */
 | |
|   }
 | |
| 
 | |
|   str= last_field_pos;
 | |
| 
 | |
|   number_of_fields= i - start_loop;
 | |
|   while (i < MAX_DATE_PARTS)
 | |
|   {
 | |
|     date_len[i]= 0;
 | |
|     date[i++]= 0;
 | |
|   }
 | |
| 
 | |
|   if (!is_internal_format)
 | |
|   {
 | |
|     year_length= date_len[(uint) format_position[0]];
 | |
|     if (!year_length)                           /* Year must be specified */
 | |
|     {
 | |
|       *was_cut= 1;
 | |
|       DBUG_RETURN(MYSQL_TIMESTAMP_NONE);
 | |
|     }
 | |
| 
 | |
|     l_time->year=               date[(uint) format_position[0]];
 | |
|     l_time->month=              date[(uint) format_position[1]];
 | |
|     l_time->day=                date[(uint) format_position[2]];
 | |
|     l_time->hour=               date[(uint) format_position[3]];
 | |
|     l_time->minute=             date[(uint) format_position[4]];
 | |
|     l_time->second=             date[(uint) format_position[5]];
 | |
| 
 | |
|     frac_pos= (uint) format_position[6];
 | |
|     frac_len= date_len[frac_pos];
 | |
|     if (frac_len < 6)
 | |
|       date[frac_pos]*= (uint) log_10_int[6 - frac_len];
 | |
|     l_time->second_part= date[frac_pos];
 | |
| 
 | |
|     if (format_position[7] != (uchar) 255)
 | |
|     {
 | |
|       if (l_time->hour > 12)
 | |
|       {
 | |
|         *was_cut= 1;
 | |
|         goto err;
 | |
|       }
 | |
|       l_time->hour= l_time->hour%12 + add_hours;
 | |
|     }
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     l_time->year=       date[0];
 | |
|     l_time->month=      date[1];
 | |
|     l_time->day=        date[2];
 | |
|     l_time->hour=       date[3];
 | |
|     l_time->minute=     date[4];
 | |
|     l_time->second=     date[5];
 | |
|     if (date_len[6] < 6)
 | |
|       date[6]*= (uint) log_10_int[6 - date_len[6]];
 | |
|     l_time->second_part=date[6];
 | |
|   }
 | |
|   l_time->neg= 0;
 | |
| 
 | |
|   if (year_length == 2 && not_zero_date)
 | |
|     l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900);
 | |
| 
 | |
|   if (number_of_fields < 3 ||
 | |
|       l_time->year > 9999 || l_time->month > 12 ||
 | |
|       l_time->day > 31 || l_time->hour > 23 ||
 | |
|       l_time->minute > 59 || l_time->second > 59)
 | |
|   {
 | |
|     /* Only give warning for a zero date if there is some garbage after */
 | |
|     if (!not_zero_date)                         /* If zero date */
 | |
|     {
 | |
|       for (; str != end ; str++)
 | |
|       {
 | |
|         if (!my_isspace(&my_charset_latin1, *str))
 | |
|         {
 | |
|           not_zero_date= 1;                     /* Give warning */
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     *was_cut= test(not_zero_date);
 | |
|     goto err;
 | |
|   }
 | |
| 
 | |
|   if (check_date(l_time, not_zero_date != 0, flags, was_cut))
 | |
|     goto err;
 | |
| 
 | |
|   l_time->time_type= (number_of_fields <= 3 ?
 | |
|                       MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME);
 | |
| 
 | |
|   for (; str != end ; str++)
 | |
|   {
 | |
|     if (!my_isspace(&my_charset_latin1,*str))
 | |
|     {
 | |
|       *was_cut= 1;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   DBUG_RETURN(l_time->time_type=
 | |
|               (number_of_fields <= 3 ? MYSQL_TIMESTAMP_DATE :
 | |
|                                        MYSQL_TIMESTAMP_DATETIME));
 | |
| 
 | |
| err:
 | |
|   bzero((char*) l_time, sizeof(*l_time));
 | |
|   DBUG_RETURN(MYSQL_TIMESTAMP_ERROR);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  Convert a time string to a MYSQL_TIME struct.
 | |
| 
 | |
|   SYNOPSIS
 | |
|    str_to_time()
 | |
|    str                  A string in full TIMESTAMP format or
 | |
|                         [-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
 | |
|                         [M]MSS or [S]S
 | |
|                         There may be an optional [.second_part] after seconds
 | |
|    length               Length of str
 | |
|    l_time               Store result here
 | |
|    warning              Set MYSQL_TIME_WARN_TRUNCATED flag if the input string
 | |
|                         was cut during conversion, and/or
 | |
|                         MYSQL_TIME_WARN_OUT_OF_RANGE flag, if the value is
 | |
|                         out of range.
 | |
| 
 | |
|    NOTES
 | |
|      Because of the extra days argument, this function can only
 | |
|      work with times where the time arguments are in the above order.
 | |
| 
 | |
|    RETURN
 | |
|      0  ok
 | |
|      1  error
 | |
| */
 | |
| 
 | |
| my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
 | |
|                     int *warning)
 | |
| {
 | |
|   ulong date[5];
 | |
|   ulonglong value;
 | |
|   const char *end=str+length, *end_of_days;
 | |
|   my_bool found_days,found_hours;
 | |
|   uint state;
 | |
| 
 | |
|   l_time->neg=0;
 | |
|   *warning= 0;
 | |
|   for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++)
 | |
|     length--;
 | |
|   if (str != end && *str == '-')
 | |
|   {
 | |
|     l_time->neg=1;
 | |
|     str++;
 | |
|     length--;
 | |
|   }
 | |
|   if (str == end)
 | |
|     return 1;
 | |
| 
 | |
|   /* Check first if this is a full TIMESTAMP */
 | |
|   if (length >= 12)
 | |
|   {                                             /* Probably full timestamp */
 | |
|     int was_cut;
 | |
|     enum enum_mysql_timestamp_type
 | |
|       res= str_to_datetime(str, length, l_time,
 | |
|                            (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &was_cut);
 | |
|     if ((int) res >= (int) MYSQL_TIMESTAMP_ERROR)
 | |
|     {
 | |
|       if (was_cut)
 | |
|         *warning|= MYSQL_TIME_WARN_TRUNCATED;
 | |
|       return res == MYSQL_TIMESTAMP_ERROR;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
 | |
|   for (value=0; str != end && my_isdigit(&my_charset_latin1,*str) ; str++)
 | |
|     value=value*10L + (long) (*str - '0');
 | |
| 
 | |
|   /* Skip all space after 'days' */
 | |
|   end_of_days= str;
 | |
|   for (; str != end && my_isspace(&my_charset_latin1, str[0]) ; str++)
 | |
|     ;
 | |
| 
 | |
|   LINT_INIT(state);
 | |
|   found_days=found_hours=0;
 | |
|   if ((uint) (end-str) > 1 && str != end_of_days &&
 | |
|       my_isdigit(&my_charset_latin1, *str))
 | |
|   {                                             /* Found days part */
 | |
|     date[0]= (ulong) value;
 | |
|     state= 1;                                   /* Assume next is hours */
 | |
|     found_days= 1;
 | |
|   }
 | |
|   else if ((end-str) > 1 &&  *str == time_separator &&
 | |
|            my_isdigit(&my_charset_latin1, str[1]))
 | |
|   {
 | |
|     date[0]= 0;                                 /* Assume we found hours */
 | |
|     date[1]= (ulong) value;
 | |
|     state=2;
 | |
|     found_hours=1;
 | |
|     str++;                                      /* skip ':' */
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     /* String given as one number; assume HHMMSS format */
 | |
|     date[0]= 0;
 | |
|     date[1]= (ulong) (value/10000);
 | |
|     date[2]= (ulong) (value/100 % 100);
 | |
|     date[3]= (ulong) (value % 100);
 | |
|     state=4;
 | |
|     goto fractional;
 | |
|   }
 | |
| 
 | |
|   /* Read hours, minutes and seconds */
 | |
|   for (;;)
 | |
|   {
 | |
|     for (value=0; str != end && my_isdigit(&my_charset_latin1,*str) ; str++)
 | |
|       value=value*10L + (long) (*str - '0');
 | |
|     date[state++]= (ulong) value;
 | |
|     if (state == 4 || (end-str) < 2 || *str != time_separator ||
 | |
|         !my_isdigit(&my_charset_latin1,str[1]))
 | |
|       break;
 | |
|     str++;                                      /* Skip time_separator (':') */
 | |
|   }
 | |
| 
 | |
|   if (state != 4)
 | |
|   {                                             /* Not HH:MM:SS */
 | |
|     /* Fix the date to assume that seconds was given */
 | |
|     if (!found_hours && !found_days)
 | |
|     {
 | |
|       bmove_upp((uchar*) (date+4), (uchar*) (date+state),
 | |
|                 sizeof(long)*(state-1));
 | |
|       bzero((uchar*) date, sizeof(long)*(4-state));
 | |
|     }
 | |
|     else
 | |
|       bzero((uchar*) (date+state), sizeof(long)*(4-state));
 | |
|   }
 | |
| 
 | |
| fractional:
 | |
|   /* Get fractional second part */
 | |
|   if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_latin1,str[1]))
 | |
|   {
 | |
|     int field_length= 5;
 | |
|     str++; value=(uint) (uchar) (*str - '0');
 | |
|     while (++str != end && my_isdigit(&my_charset_latin1, *str))
 | |
|     {
 | |
|       if (field_length-- > 0)
 | |
|         value= value*10 + (uint) (uchar) (*str - '0');
 | |
|     }
 | |
|     if (field_length > 0)
 | |
|       value*= (long) log_10_int[field_length];
 | |
|     else if (field_length < 0)
 | |
|       *warning|= MYSQL_TIME_WARN_TRUNCATED;
 | |
|     date[4]= (ulong) value;
 | |
|   }
 | |
|   else
 | |
|     date[4]=0;
 | |
|     
 | |
|   /* Check for exponent part: E<gigit> | E<sign><digit> */
 | |
|   /* (may occur as result of %g formatting of time value) */
 | |
|   if ((end - str) > 1 &&
 | |
|       (*str == 'e' || *str == 'E') &&
 | |
|       (my_isdigit(&my_charset_latin1, str[1]) ||
 | |
|        ((str[1] == '-' || str[1] == '+') &&
 | |
|         (end - str) > 2 &&
 | |
|         my_isdigit(&my_charset_latin1, str[2]))))
 | |
|     return 1;
 | |
| 
 | |
|   if (internal_format_positions[7] != 255)
 | |
|   {
 | |
|     /* Read a possible AM/PM */
 | |
|     while (str != end && my_isspace(&my_charset_latin1, *str))
 | |
|       str++;
 | |
|     if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
 | |
|     {
 | |
|       if (str[0] == 'p' || str[0] == 'P')
 | |
|       {
 | |
|         str+= 2;
 | |
|         date[1]= date[1]%12 + 12;
 | |
|       }
 | |
|       else if (str[0] == 'a' || str[0] == 'A')
 | |
|         str+=2;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* Integer overflow checks */
 | |
|   if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
 | |
|       date[2] > UINT_MAX || date[3] > UINT_MAX ||
 | |
|       date[4] > UINT_MAX)
 | |
|     return 1;
 | |
|   
 | |
|   l_time->year=         0;                      /* For protocol::store_time */
 | |
|   l_time->month=        0;
 | |
|   l_time->day=          date[0];
 | |
|   l_time->hour=         date[1];
 | |
|   l_time->minute=       date[2];
 | |
|   l_time->second=       date[3];
 | |
|   l_time->second_part=  date[4];
 | |
|   l_time->time_type= MYSQL_TIMESTAMP_TIME;
 | |
| 
 | |
|   /* Check if the value is valid and fits into MYSQL_TIME range */
 | |
|   if (check_time_range(l_time, warning))
 | |
|     return 1;
 | |
|   
 | |
|   /* Check if there is garbage at end of the MYSQL_TIME specification */
 | |
|   if (str != end)
 | |
|   {
 | |
|     do
 | |
|     {
 | |
|       if (!my_isspace(&my_charset_latin1,*str))
 | |
|       {
 | |
|         *warning|= MYSQL_TIME_WARN_TRUNCATED;
 | |
|         break;
 | |
|       }
 | |
|     } while (++str != end);
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Check 'time' value to lie in the MYSQL_TIME range
 | |
| 
 | |
|   SYNOPSIS:
 | |
|     check_time_range()
 | |
|     time     pointer to MYSQL_TIME value
 | |
|     warning  set MYSQL_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
 | |
| 
 | |
|   DESCRIPTION
 | |
|   If the time value lies outside of the range [-838:59:59, 838:59:59],
 | |
|   set it to the closest endpoint of the range and set
 | |
|   MYSQL_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
 | |
| 
 | |
|   RETURN
 | |
|     0        time value is valid, but was possibly truncated
 | |
|     1        time value is invalid
 | |
| */
 | |
| 
 | |
| int check_time_range(struct st_mysql_time *my_time, int *warning) 
 | |
| {
 | |
|   longlong hour;
 | |
| 
 | |
|   if (my_time->minute >= 60 || my_time->second >= 60)
 | |
|     return 1;
 | |
| 
 | |
|   hour= my_time->hour + (24*my_time->day);
 | |
|   if (hour <= TIME_MAX_HOUR &&
 | |
|       (hour != TIME_MAX_HOUR || my_time->minute != TIME_MAX_MINUTE ||
 | |
|        my_time->second != TIME_MAX_SECOND || !my_time->second_part))
 | |
|     return 0;
 | |
| 
 | |
|   my_time->day= 0;
 | |
|   my_time->hour= TIME_MAX_HOUR;
 | |
|   my_time->minute= TIME_MAX_MINUTE;
 | |
|   my_time->second= TIME_MAX_SECOND;
 | |
|   my_time->second_part= 0;
 | |
|   *warning|= MYSQL_TIME_WARN_OUT_OF_RANGE;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     init_time()
 | |
| */
 | |
| void init_time(void)
 | |
| {
 | |
|   time_t seconds;
 | |
|   struct tm *l_time,tm_tmp;
 | |
|   MYSQL_TIME my_time;
 | |
|   my_bool not_used;
 | |
| 
 | |
|   seconds= (time_t) time((time_t*) 0);
 | |
|   localtime_r(&seconds,&tm_tmp);
 | |
|   l_time= &tm_tmp;
 | |
|   my_time_zone=		3600;		/* Comp. for -3600 in my_gmt_sec */
 | |
|   my_time.year=		(uint) l_time->tm_year+1900;
 | |
|   my_time.month=	(uint) l_time->tm_mon+1;
 | |
|   my_time.day=		(uint) l_time->tm_mday;
 | |
|   my_time.hour=		(uint) l_time->tm_hour;
 | |
|   my_time.minute=	(uint) l_time->tm_min;
 | |
|   my_time.second=	(uint) l_time->tm_sec;
 | |
|   my_system_gmt_sec(&my_time, &my_time_zone, ¬_used); /* Init my_time_zone */
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Handle 2 digit year conversions
 | |
| 
 | |
|   SYNOPSIS
 | |
|   year_2000_handling()
 | |
|   year     2 digit year
 | |
| 
 | |
|   RETURN
 | |
|     Year between 1970-2069
 | |
| */
 | |
| 
 | |
| uint year_2000_handling(uint year)
 | |
| {
 | |
|   if ((year=year+1900) < 1900+YY_PART_YEAR)
 | |
|     year+=100;
 | |
|   return year;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Calculate nr of day since year 0 in new date-system (from 1615)
 | |
| 
 | |
|   SYNOPSIS
 | |
|     calc_daynr()
 | |
|     year		 Year (exact 4 digit year, no year conversions)
 | |
|     month		 Month
 | |
|     day			 Day
 | |
| 
 | |
|   NOTES: 0000-00-00 is a valid date, and will return 0
 | |
| 
 | |
|   RETURN
 | |
|     Days since 0000-00-00
 | |
| */
 | |
| 
 | |
| long calc_daynr(uint year,uint month,uint day)
 | |
| {
 | |
|   long delsum;
 | |
|   int temp;
 | |
|   DBUG_ENTER("calc_daynr");
 | |
| 
 | |
|   if (year == 0 && month == 0 && day == 0)
 | |
|     DBUG_RETURN(0);				/* Skip errors */
 | |
|   delsum= (long) (365L * year+ 31*(month-1) +day);
 | |
|   if (month <= 2)
 | |
|       year--;
 | |
|   else
 | |
|     delsum-= (long) (month*4+23)/10;
 | |
|   temp=(int) ((year/100+1)*3)/4;
 | |
|   DBUG_PRINT("exit",("year: %d  month: %d  day: %d -> daynr: %ld",
 | |
| 		     year+(month <= 2),month,day,delsum+year/4-temp));
 | |
|   DBUG_RETURN(delsum+(int) year/4-temp);
 | |
| } /* calc_daynr */
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Convert time in MYSQL_TIME representation in system time zone to its
 | |
|   my_time_t form (number of seconds in UTC since begginning of Unix Epoch).
 | |
| 
 | |
|   SYNOPSIS
 | |
|     my_system_gmt_sec()
 | |
|       t               - time value to be converted
 | |
|       my_timezone     - pointer to long where offset of system time zone
 | |
|                         from UTC will be stored for caching
 | |
|       in_dst_time_gap - set to true if time falls into spring time-gap
 | |
| 
 | |
|   NOTES
 | |
|     The idea is to cache the time zone offset from UTC (including daylight 
 | |
|     saving time) for the next call to make things faster. But currently we 
 | |
|     just calculate this offset during startup (by calling init_time() 
 | |
|     function) and use it all the time.
 | |
|     Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
 | |
|     is not allowed).
 | |
| 
 | |
|   RETURN VALUE
 | |
|     Time in UTC seconds since Unix Epoch representation.
 | |
| */
 | |
| my_time_t
 | |
| my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone,
 | |
|                   my_bool *in_dst_time_gap)
 | |
| {
 | |
|   uint loop;
 | |
|   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
 | |
|     (like 2002-10-27 02:00:0 MET), we will find the initial date.
 | |
| 
 | |
|     By doing -3600 we will have to call localtime_r() several times, but
 | |
|     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.
 | |
| 
 | |
|     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;
 | |
|        loop < 2 &&
 | |
| 	 (t->hour != (uint) l_time->tm_hour ||
 | |
| 	  t->minute != (uint) l_time->tm_min ||
 | |
|           t->second != (uint) l_time->tm_sec);
 | |
|        loop++)
 | |
|   {					/* One check should be enough ? */
 | |
|     /* Get difference in days */
 | |
|     int days= t->day - l_time->tm_mday;
 | |
|     if (days < -1)
 | |
|       days= 1;					/* Month has wrapped */
 | |
|     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) ((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 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)
 | |
|   {
 | |
|     int days= t->day - l_time->tm_mday;
 | |
|     if (days < -1)
 | |
|       days=1;					/* Month has wrapped */
 | |
|     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) ((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)
 | |
|       tmp-=t->minute*60 + t->second;		/* Move to previous hour */
 | |
| 
 | |
|     *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;
 | |
| 
 | |
|   return (my_time_t) tmp;
 | |
| } /* my_system_gmt_sec */
 | |
| 
 | |
| 
 | |
| /* Set MYSQL_TIME structure to 0000-00-00 00:00:00.000000 */
 | |
| 
 | |
| void set_zero_time(MYSQL_TIME *tm, enum enum_mysql_timestamp_type time_type)
 | |
| {
 | |
|   bzero((void*) tm, sizeof(*tm));
 | |
|   tm->time_type= time_type;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Functions to convert time/date/datetime value to a string,
 | |
|   using default format.
 | |
|   This functions don't check that given MYSQL_TIME structure members are
 | |
|   in valid range. If they are not, return value won't reflect any
 | |
|   valid date either. Additionally, make_time doesn't take into
 | |
|   account time->day member: it's assumed that days have been converted
 | |
|   to hours already.
 | |
| 
 | |
|   RETURN
 | |
|     number of characters written to 'to'
 | |
| */
 | |
| 
 | |
| int my_time_to_str(const MYSQL_TIME *l_time, char *to)
 | |
| {
 | |
|   uint extra_hours= 0;
 | |
|   return my_sprintf(to, (to, "%s%02u:%02u:%02u",
 | |
|                          (l_time->neg ? "-" : ""),
 | |
|                          extra_hours+ l_time->hour,
 | |
|                          l_time->minute,
 | |
|                          l_time->second));
 | |
| }
 | |
| 
 | |
| int my_date_to_str(const MYSQL_TIME *l_time, char *to)
 | |
| {
 | |
|   return my_sprintf(to, (to, "%04u-%02u-%02u",
 | |
|                          l_time->year,
 | |
|                          l_time->month,
 | |
|                          l_time->day));
 | |
| }
 | |
| 
 | |
| int my_datetime_to_str(const MYSQL_TIME *l_time, char *to)
 | |
| {
 | |
|   return my_sprintf(to, (to, "%04u-%02u-%02u %02u:%02u:%02u",
 | |
|                          l_time->year,
 | |
|                          l_time->month,
 | |
|                          l_time->day,
 | |
|                          l_time->hour,
 | |
|                          l_time->minute,
 | |
|                          l_time->second));
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Convert struct DATE/TIME/DATETIME value to string using built-in
 | |
|   MySQL time conversion formats.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     my_TIME_to_string()
 | |
| 
 | |
|   NOTE
 | |
|     The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved.
 | |
| */
 | |
| 
 | |
| int my_TIME_to_str(const MYSQL_TIME *l_time, char *to)
 | |
| {
 | |
|   switch (l_time->time_type) {
 | |
|   case MYSQL_TIMESTAMP_DATETIME:
 | |
|     return my_datetime_to_str(l_time, to);
 | |
|   case MYSQL_TIMESTAMP_DATE:
 | |
|     return my_date_to_str(l_time, to);
 | |
|   case MYSQL_TIMESTAMP_TIME:
 | |
|     return my_time_to_str(l_time, to);
 | |
|   case MYSQL_TIMESTAMP_NONE:
 | |
|   case MYSQL_TIMESTAMP_ERROR:
 | |
|     to[0]='\0';
 | |
|     return 0;
 | |
|   default:
 | |
|     DBUG_ASSERT(0);
 | |
|     return 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Convert datetime value specified as number to broken-down TIME
 | |
|   representation and form value of DATETIME type as side-effect.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     number_to_datetime()
 | |
|       nr         - datetime value as number
 | |
|       time_res   - pointer for structure for broken-down representation
 | |
|       flags      - flags to use in validating date, as in str_to_datetime()
 | |
|       was_cut    0      Value ok
 | |
|                  1      If value was cut during conversion
 | |
|                  2      Date part was within ranges but date was wrong
 | |
| 
 | |
|   DESCRIPTION
 | |
|     Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
 | |
|     YYYYMMDDHHMMSS to broken-down MYSQL_TIME representation. Return value in
 | |
|     YYYYMMDDHHMMSS format as side-effect.
 | |
| 
 | |
|     This function also checks if datetime value fits in DATETIME range.
 | |
| 
 | |
|   RETURN VALUE
 | |
|     -1              Timestamp with wrong values
 | |
|     anything else   DATETIME as integer in YYYYMMDDHHMMSS format
 | |
|     Datetime value in YYYYMMDDHHMMSS format.
 | |
| */
 | |
| 
 | |
| longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res,
 | |
|                             uint flags, int *was_cut)
 | |
| {
 | |
|   long part1,part2;
 | |
| 
 | |
|   *was_cut= 0;
 | |
| 
 | |
|   if (nr == LL(0) || nr >= LL(10000101000000))
 | |
|     goto ok;
 | |
|   if (nr < 101)
 | |
|     goto err;
 | |
|   if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
 | |
|   {
 | |
|     nr= (nr+20000000L)*1000000L;                 /* YYMMDD, year: 2000-2069 */
 | |
|     goto ok;
 | |
|   }
 | |
|   if (nr < (YY_PART_YEAR)*10000L+101L)
 | |
|     goto err;
 | |
|   if (nr <= 991231L)
 | |
|   {
 | |
|     nr= (nr+19000000L)*1000000L;                 /* YYMMDD, year: 1970-1999 */
 | |
|     goto ok;
 | |
|   }
 | |
|   if (nr < 10000101L)
 | |
|     goto err;
 | |
|   if (nr <= 99991231L)
 | |
|   {
 | |
|     nr= nr*1000000L;
 | |
|     goto ok;
 | |
|   }
 | |
|   if (nr < 101000000L)
 | |
|     goto err;
 | |
|   if (nr <= (YY_PART_YEAR-1)*LL(10000000000)+LL(1231235959))
 | |
|   {
 | |
|     nr= nr+LL(20000000000000);                   /* YYMMDDHHMMSS, 2000-2069 */
 | |
|     goto ok;
 | |
|   }
 | |
|   if (nr <  YY_PART_YEAR*LL(10000000000)+ LL(101000000))
 | |
|     goto err;
 | |
|   if (nr <= LL(991231235959))
 | |
|     nr= nr+LL(19000000000000);		/* YYMMDDHHMMSS, 1970-1999 */
 | |
| 
 | |
|  ok:
 | |
|   part1=(long) (nr/LL(1000000));
 | |
|   part2=(long) (nr - (longlong) part1*LL(1000000));
 | |
|   bzero((char*) time_res, sizeof(*time_res));
 | |
|   time_res->year=  (int) (part1/10000L);  part1%=10000L;
 | |
|   time_res->month= (int) part1 / 100;
 | |
|   time_res->day=   (int) part1 % 100;
 | |
|   time_res->hour=  (int) (part2/10000L);  part2%=10000L;
 | |
|   time_res->minute=(int) part2 / 100;
 | |
|   time_res->second=(int) part2 % 100;
 | |
| 
 | |
|   if (time_res->year <= 9999 && time_res->month <= 12 &&
 | |
|       time_res->day <= 31 && time_res->hour <= 23 &&
 | |
|       time_res->minute <= 59 && time_res->second <= 59 &&
 | |
|       !check_date(time_res, (nr != 0), flags, was_cut))
 | |
|     return nr;
 | |
| 
 | |
|   /* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */
 | |
|   if (!nr && (flags & TIME_NO_ZERO_DATE))
 | |
|     return LL(-1);
 | |
| 
 | |
|  err:
 | |
|   *was_cut= 1;
 | |
|   return LL(-1);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Convert time value to integer in YYYYMMDDHHMMSS format */
 | |
| 
 | |
| ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *my_time)
 | |
| {
 | |
|   return ((ulonglong) (my_time->year * 10000UL +
 | |
|                        my_time->month * 100UL +
 | |
|                        my_time->day) * ULL(1000000) +
 | |
|           (ulonglong) (my_time->hour * 10000UL +
 | |
|                        my_time->minute * 100UL +
 | |
|                        my_time->second));
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Convert MYSQL_TIME value to integer in YYYYMMDD format */
 | |
| 
 | |
| ulonglong TIME_to_ulonglong_date(const MYSQL_TIME *my_time)
 | |
| {
 | |
|   return (ulonglong) (my_time->year * 10000UL + my_time->month * 100UL +
 | |
|                       my_time->day);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Convert MYSQL_TIME value to integer in HHMMSS format.
 | |
|   This function doesn't take into account time->day member:
 | |
|   it's assumed that days have been converted to hours already.
 | |
| */
 | |
| 
 | |
| ulonglong TIME_to_ulonglong_time(const MYSQL_TIME *my_time)
 | |
| {
 | |
|   return (ulonglong) (my_time->hour * 10000UL +
 | |
|                       my_time->minute * 100UL +
 | |
|                       my_time->second);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Convert struct MYSQL_TIME (date and time split into year/month/day/hour/...
 | |
|   to a number in format YYYYMMDDHHMMSS (DATETIME),
 | |
|   YYYYMMDD (DATE)  or HHMMSS (TIME).
 | |
| 
 | |
|   SYNOPSIS
 | |
|     TIME_to_ulonglong()
 | |
| 
 | |
|   DESCRIPTION
 | |
|     The function is used when we need to convert value of time item
 | |
|     to a number if it's used in numeric context, i. e.:
 | |
|     SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
 | |
|     SELECT ?+1;
 | |
| 
 | |
|   NOTE
 | |
|     This function doesn't check that given MYSQL_TIME structure members are
 | |
|     in valid range. If they are not, return value won't reflect any
 | |
|     valid date either.
 | |
| */
 | |
| 
 | |
| ulonglong TIME_to_ulonglong(const MYSQL_TIME *my_time)
 | |
| {
 | |
|   switch (my_time->time_type) {
 | |
|   case MYSQL_TIMESTAMP_DATETIME:
 | |
|     return TIME_to_ulonglong_datetime(my_time);
 | |
|   case MYSQL_TIMESTAMP_DATE:
 | |
|     return TIME_to_ulonglong_date(my_time);
 | |
|   case MYSQL_TIMESTAMP_TIME:
 | |
|     return TIME_to_ulonglong_time(my_time);
 | |
|   case MYSQL_TIMESTAMP_NONE:
 | |
|   case MYSQL_TIMESTAMP_ERROR:
 | |
|     return ULL(0);
 | |
|   default:
 | |
|     DBUG_ASSERT(0);
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 |