MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
my_time.c
1 /* Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 #include <my_time.h>
17 #include <m_string.h>
18 #include <m_ctype.h>
19 #include <myisampack.h>
20 /* Windows version of localtime_r() is declared in my_ptrhead.h */
21 #include <my_pthread.h>
22 
23 ulonglong log_10_int[20]=
24 {
25  1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
26  ULL(100000000), ULL(1000000000), ULL(10000000000), ULL(100000000000),
27  ULL(1000000000000), ULL(10000000000000), ULL(100000000000000),
28  ULL(1000000000000000), ULL(10000000000000000), ULL(100000000000000000),
29  ULL(1000000000000000000), ULL(10000000000000000000)
30 };
31 
32 
33 const char my_zero_datetime6[]= "0000-00-00 00:00:00.000000";
34 
35 
36 /* Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format */
37 
38 static uchar internal_format_positions[]=
39 {0, 1, 2, 3, 4, 5, 6, (uchar) 255};
40 
41 static char time_separator=':';
42 
43 static ulong const days_at_timestart=719528; /* daynr at 1970.01.01 */
44 uchar days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
45 
46 /*
47  Offset of system time zone from UTC in seconds used to speed up
48  work of my_system_gmt_sec() function.
49 */
50 static long my_time_zone=0;
51 
52 
53 /* Calc days in one year. works with 0 <= year <= 99 */
54 
55 uint calc_days_in_year(uint year)
56 {
57  return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
58  366 : 365);
59 }
60 
61 
67 inline void set_zero_time(MYSQL_TIME *tm,
68  enum enum_mysql_timestamp_type time_type)
69 {
70  memset(tm, 0, sizeof(*tm));
71  tm->time_type= time_type;
72 }
73 
74 
79 inline void set_max_hhmmss(MYSQL_TIME *tm)
80 {
81  tm->hour= TIME_MAX_HOUR;
82  tm->minute= TIME_MAX_MINUTE;
83  tm->second= TIME_MAX_SECOND;
84 }
85 
86 
92 void set_max_time(MYSQL_TIME *tm, my_bool neg)
93 {
94  set_zero_time(tm, MYSQL_TIMESTAMP_TIME);
95  set_max_hhmmss(tm);
96  tm->neg= neg;
97 }
98 
99 
120 my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
121  ulonglong flags, int *was_cut)
122 {
123  if (not_zero_date)
124  {
125  if (((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
126  (ltime->month == 0 || ltime->day == 0))
127  {
128  *was_cut= MYSQL_TIME_WARN_ZERO_IN_DATE;
129  return TRUE;
130  }
131  else if ((!(flags & TIME_INVALID_DATES) &&
132  ltime->month && ltime->day > days_in_month[ltime->month-1] &&
133  (ltime->month != 2 || calc_days_in_year(ltime->year) != 366 ||
134  ltime->day != 29)))
135  {
136  *was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
137  return TRUE;
138  }
139  }
140  else if (flags & TIME_NO_ZERO_DATE)
141  {
142  *was_cut= MYSQL_TIME_WARN_ZERO_DATE;
143  return TRUE;
144  }
145  return FALSE;
146 }
147 
148 
155 inline my_bool check_time_mmssff_range(const MYSQL_TIME *ltime)
156 {
157  return ltime->minute >= 60 || ltime->second >= 60 ||
158  ltime->second_part > 999999;
159 }
160 
161 
174 inline my_bool check_time_range_quick(const MYSQL_TIME *ltime)
175 {
176  longlong hour= (longlong) ltime->hour + 24LL * ltime->day;
177  /* The input value should not be fatally bad */
178  DBUG_ASSERT(!check_time_mmssff_range(ltime));
179  if (hour <= TIME_MAX_HOUR &&
180  (hour != TIME_MAX_HOUR || ltime->minute != TIME_MAX_MINUTE ||
181  ltime->second != TIME_MAX_SECOND || !ltime->second_part))
182  return FALSE;
183  return TRUE;
184 }
185 
186 
194 my_bool check_datetime_range(const MYSQL_TIME *ltime)
195 {
196  /*
197  In case of MYSQL_TIMESTAMP_TIME hour value can be up to TIME_MAX_HOUR.
198  In case of MYSQL_TIMESTAMP_DATETIME it cannot be bigger than 23.
199  */
200  return
201  ltime->year > 9999 || ltime->month > 12 || ltime->day > 31 ||
202  ltime->minute > 59 || ltime->second > 59 || ltime->second_part > 999999 ||
203  (ltime->hour >
204  (ltime->time_type == MYSQL_TIMESTAMP_TIME ? TIME_MAX_HOUR : 23));
205 }
206 
207 
208 /*
209  Convert a timestamp string to a MYSQL_TIME value.
210 
211  SYNOPSIS
212  str_to_datetime()
213  str String to parse
214  length Length of string
215  l_time Date is stored here
216  flags Bitmap of following items
217  TIME_FUZZY_DATE Set if we should allow partial dates
218  TIME_DATETIME_ONLY Set if we only allow full datetimes.
219  TIME_NO_ZERO_IN_DATE Don't allow partial dates
220  TIME_NO_ZERO_DATE Don't allow 0000-00-00 date
221  TIME_INVALID_DATES Allow 2000-02-31
222  status Conversion status
223 
224 
225  DESCRIPTION
226  At least the following formats are recogniced (based on number of digits)
227  YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
228  YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
229  YYYYMMDDTHHMMSS where T is a the character T (ISO8601)
230  Also dates where all parts are zero are allowed
231 
232  The second part may have an optional .###### fraction part.
233 
234  NOTES
235  This function should work with a format position vector as long as the
236  following things holds:
237  - All date are kept together and all time parts are kept together
238  - Date and time parts must be separated by blank
239  - Second fractions must come after second part and be separated
240  by a '.'. (The second fractions are optional)
241  - AM/PM must come after second fractions (or after seconds if no fractions)
242  - Year must always been specified.
243  - If time is before date, then we will use datetime format only if
244  the argument consist of two parts, separated by space.
245  Otherwise we will assume the argument is a date.
246  - The hour part must be specified in hour-minute-second order.
247 
248  status->warnings is set to:
249  0 Value OK
250  MYSQL_TIME_WARN_TRUNCATED If value was cut during conversion
251  MYSQL_TIME_WARN_OUT_OF_RANGE check_date(date,flags) considers date invalid
252 
253  l_time->time_type is set as follows:
254  MYSQL_TIMESTAMP_NONE String wasn't a timestamp, like
255  [DD [HH:[MM:[SS]]]].fraction.
256  l_time is not changed.
257  MYSQL_TIMESTAMP_DATE DATE string (YY MM and DD parts ok)
258  MYSQL_TIMESTAMP_DATETIME Full timestamp
259  MYSQL_TIMESTAMP_ERROR Timestamp with wrong values.
260  All elements in l_time is set to 0
261  RETURN VALUES
262  0 - Ok
263  1 - Error
264 */
265 
266 #define MAX_DATE_PARTS 8
267 
268 my_bool
269 str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
270  ulonglong flags, MYSQL_TIME_STATUS *status)
271 {
272  uint field_length, UNINIT_VAR(year_length), digits, i, number_of_fields;
273  uint date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
274  uint add_hours= 0, start_loop;
275  ulong not_zero_date, allow_space;
276  my_bool is_internal_format;
277  const char *pos, *UNINIT_VAR(last_field_pos);
278  const char *end=str+length;
279  const uchar *format_position;
280  my_bool found_delimitier= 0, found_space= 0;
281  uint frac_pos, frac_len;
282  DBUG_ENTER("str_to_datetime");
283  DBUG_PRINT("ENTER",("str: %.*s",length,str));
284 
285  LINT_INIT(field_length);
286 
287  my_time_status_init(status);
288 
289  /* Skip space at start */
290  for (; str != end && my_isspace(&my_charset_latin1, *str) ; str++)
291  ;
292  if (str == end || ! my_isdigit(&my_charset_latin1, *str))
293  {
294  status->warnings= MYSQL_TIME_WARN_TRUNCATED;
295  l_time->time_type= MYSQL_TIMESTAMP_NONE;
296  DBUG_RETURN(1);
297  }
298 
299  is_internal_format= 0;
300  /* This has to be changed if want to activate different timestamp formats */
301  format_position= internal_format_positions;
302 
303  /*
304  Calculate number of digits in first part.
305  If length= 8 or >= 14 then year is of format YYYY.
306  (YYYY-MM-DD, YYYYMMDD, YYYYYMMDDHHMMSS)
307  */
308  for (pos=str;
309  pos != end && (my_isdigit(&my_charset_latin1,*pos) || *pos == 'T');
310  pos++)
311  ;
312 
313  digits= (uint) (pos-str);
314  start_loop= 0; /* Start of scan loop */
315  date_len[format_position[0]]= 0; /* Length of year field */
316  if (pos == end || *pos == '.')
317  {
318  /* Found date in internal format (only numbers like YYYYMMDD) */
319  year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
320  field_length= year_length;
321  is_internal_format= 1;
322  format_position= internal_format_positions;
323  }
324  else
325  {
326  if (format_position[0] >= 3) /* If year is after HHMMDD */
327  {
328  /*
329  If year is not in first part then we have to determinate if we got
330  a date field or a datetime field.
331  We do this by checking if there is two numbers separated by
332  space in the input.
333  */
334  while (pos < end && !my_isspace(&my_charset_latin1, *pos))
335  pos++;
336  while (pos < end && !my_isdigit(&my_charset_latin1, *pos))
337  pos++;
338  if (pos == end)
339  {
340  if (flags & TIME_DATETIME_ONLY)
341  {
342  status->warnings= MYSQL_TIME_WARN_TRUNCATED;
343  l_time->time_type= MYSQL_TIMESTAMP_NONE;
344  DBUG_RETURN(1); /* Can't be a full datetime */
345  }
346  /* Date field. Set hour, minutes and seconds to 0 */
347  date[0]= date[1]= date[2]= date[3]= date[4]= 0;
348  start_loop= 5; /* Start with first date part */
349  }
350  }
351 
352  field_length= format_position[0] == 0 ? 4 : 2;
353  }
354 
355  /*
356  Only allow space in the first "part" of the datetime field and:
357  - after days, part seconds
358  - before and after AM/PM (handled by code later)
359 
360  2003-03-03 20:00:20 AM
361  20:00:20.000000 AM 03-03-2000
362  */
363  i= MY_MAX((uint) format_position[0], (uint) format_position[1]);
364  set_if_bigger(i, (uint) format_position[2]);
365  allow_space= ((1 << i) | (1 << format_position[6]));
366  allow_space&= (1 | 2 | 4 | 8 | 64);
367 
368  not_zero_date= 0;
369  for (i = start_loop;
370  i < MAX_DATE_PARTS-1 && str != end &&
371  my_isdigit(&my_charset_latin1,*str);
372  i++)
373  {
374  const char *start= str;
375  ulong tmp_value= (uint) (uchar) (*str++ - '0');
376 
377  /*
378  Internal format means no delimiters; every field has a fixed
379  width. Otherwise, we scan until we find a delimiter and discard
380  leading zeroes -- except for the microsecond part, where leading
381  zeroes are significant, and where we never process more than six
382  digits.
383  */
384  my_bool scan_until_delim= !is_internal_format &&
385  ((i != format_position[6]));
386 
387  while (str != end && my_isdigit(&my_charset_latin1,str[0]) &&
388  (scan_until_delim || --field_length))
389  {
390  tmp_value=tmp_value*10 + (ulong) (uchar) (*str - '0');
391  str++;
392  }
393  date_len[i]= (uint) (str - start);
394  if (tmp_value > 999999) /* Impossible date part */
395  {
396  status->warnings= MYSQL_TIME_WARN_TRUNCATED;
397  l_time->time_type= MYSQL_TIMESTAMP_NONE;
398  DBUG_RETURN(1);
399  }
400  date[i]=tmp_value;
401  not_zero_date|= tmp_value;
402 
403  /* Length of next field */
404  field_length= format_position[i+1] == 0 ? 4 : 2;
405 
406  if ((last_field_pos= str) == end)
407  {
408  i++; /* Register last found part */
409  break;
410  }
411  /* Allow a 'T' after day to allow CCYYMMDDT type of fields */
412  if (i == format_position[2] && *str == 'T')
413  {
414  str++; /* ISO8601: CCYYMMDDThhmmss */
415  continue;
416  }
417  if (i == format_position[5]) /* Seconds */
418  {
419  if (*str == '.') /* Followed by part seconds */
420  {
421  str++;
422  /*
423  Shift last_field_pos, so '2001-01-01 00:00:00.'
424  is treated as a valid value
425  */
426  last_field_pos= str;
427  field_length= 6; /* 6 digits */
428  }
429  continue;
430  }
431  while (str != end &&
432  (my_ispunct(&my_charset_latin1,*str) ||
433  my_isspace(&my_charset_latin1,*str)))
434  {
435  if (my_isspace(&my_charset_latin1,*str))
436  {
437  if (!(allow_space & (1 << i)))
438  {
439  status->warnings= MYSQL_TIME_WARN_TRUNCATED;
440  l_time->time_type= MYSQL_TIMESTAMP_NONE;
441  DBUG_RETURN(1);
442  }
443  found_space= 1;
444  }
445  str++;
446  found_delimitier= 1; /* Should be a 'normal' date */
447  }
448  /* Check if next position is AM/PM */
449  if (i == format_position[6]) /* Seconds, time for AM/PM */
450  {
451  i++; /* Skip AM/PM part */
452  if (format_position[7] != 255) /* If using AM/PM */
453  {
454  if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
455  {
456  if (str[0] == 'p' || str[0] == 'P')
457  add_hours= 12;
458  else if (str[0] != 'a' || str[0] != 'A')
459  continue; /* Not AM/PM */
460  str+= 2; /* Skip AM/PM */
461  /* Skip space after AM/PM */
462  while (str != end && my_isspace(&my_charset_latin1,*str))
463  str++;
464  }
465  }
466  }
467  last_field_pos= str;
468  }
469  if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
470  {
471  status->warnings= MYSQL_TIME_WARN_TRUNCATED;
472  l_time->time_type= MYSQL_TIMESTAMP_NONE;
473  DBUG_RETURN(1); /* Can't be a datetime */
474  }
475 
476  str= last_field_pos;
477 
478  number_of_fields= i - start_loop;
479  while (i < MAX_DATE_PARTS)
480  {
481  date_len[i]= 0;
482  date[i++]= 0;
483  }
484 
485  if (!is_internal_format)
486  {
487  year_length= date_len[(uint) format_position[0]];
488  if (!year_length) /* Year must be specified */
489  {
490  status->warnings= MYSQL_TIME_WARN_TRUNCATED;
491  l_time->time_type= MYSQL_TIMESTAMP_NONE;
492  DBUG_RETURN(1);
493  }
494 
495  l_time->year= date[(uint) format_position[0]];
496  l_time->month= date[(uint) format_position[1]];
497  l_time->day= date[(uint) format_position[2]];
498  l_time->hour= date[(uint) format_position[3]];
499  l_time->minute= date[(uint) format_position[4]];
500  l_time->second= date[(uint) format_position[5]];
501 
502  frac_pos= (uint) format_position[6];
503  frac_len= date_len[frac_pos];
504  status->fractional_digits= frac_len;
505  if (frac_len < 6)
506  date[frac_pos]*= (uint) log_10_int[DATETIME_MAX_DECIMALS - frac_len];
507  l_time->second_part= date[frac_pos];
508 
509  if (format_position[7] != (uchar) 255)
510  {
511  if (l_time->hour > 12)
512  {
513  status->warnings= MYSQL_TIME_WARN_TRUNCATED;
514  goto err;
515  }
516  l_time->hour= l_time->hour%12 + add_hours;
517  }
518  }
519  else
520  {
521  l_time->year= date[0];
522  l_time->month= date[1];
523  l_time->day= date[2];
524  l_time->hour= date[3];
525  l_time->minute= date[4];
526  l_time->second= date[5];
527  if (date_len[6] < 6)
528  date[6]*= (uint) log_10_int[DATETIME_MAX_DECIMALS - date_len[6]];
529  l_time->second_part=date[6];
530  status->fractional_digits= date_len[6];
531  }
532  l_time->neg= 0;
533 
534  if (year_length == 2 && not_zero_date)
535  l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900);
536 
537  /*
538  Set time_type before check_datetime_range(),
539  as the latter relies on initialized time_type value.
540  */
541  l_time->time_type= (number_of_fields <= 3 ?
542  MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME);
543 
544  if (number_of_fields < 3 || check_datetime_range(l_time))
545  {
546  /* Only give warning for a zero date if there is some garbage after */
547  if (!not_zero_date) /* If zero date */
548  {
549  for (; str != end ; str++)
550  {
551  if (!my_isspace(&my_charset_latin1, *str))
552  {
553  not_zero_date= 1; /* Give warning */
554  break;
555  }
556  }
557  }
558  status->warnings|= not_zero_date ? MYSQL_TIME_WARN_TRUNCATED :
559  MYSQL_TIME_WARN_ZERO_DATE;
560  goto err;
561  }
562 
563  if (check_date(l_time, not_zero_date != 0, flags, &status->warnings))
564  goto err;
565 
566  /* Scan all digits left after microseconds */
567  if (status->fractional_digits == 6 && str != end)
568  {
569  if (my_isdigit(&my_charset_latin1, *str))
570  {
571  /*
572  We don't need the exact nanoseconds value.
573  Knowing the first digit is enough for rounding.
574  */
575  status->nanoseconds= 100 * (int) (*str++ - '0');
576  for (; str != end && my_isdigit(&my_charset_latin1, *str); str++)
577  { }
578  }
579  }
580 
581  for (; str != end ; str++)
582  {
583  if (!my_isspace(&my_charset_latin1,*str))
584  {
585  status->warnings= MYSQL_TIME_WARN_TRUNCATED;
586  break;
587  }
588  }
589 
590  DBUG_RETURN(0);
591 
592 err:
593  set_zero_time(l_time, MYSQL_TIMESTAMP_ERROR);
594  DBUG_RETURN(1);
595 }
596 
597 
598 /*
599  Convert a time string to a MYSQL_TIME struct.
600 
601  SYNOPSIS
602  str_to_time()
603  str A string in full TIMESTAMP format or
604  [-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
605  [M]MSS or [S]S
606  There may be an optional [.second_part] after seconds
607  length Length of str
608  l_time Store result here
609  status Conversion status
610 
611  status.warning is set to:
612  MYSQL_TIME_WARN_TRUNCATED flag if the input string
613  was cut during conversion, and/or
614  MYSQL_TIME_WARN_OUT_OF_RANGE flag, if the value is out of range.
615 
616  NOTES
617  Because of the extra days argument, this function can only
618  work with times where the time arguments are in the above order.
619 
620  RETURN
621  0 ok
622  1 error
623 */
624 
625 my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
626  MYSQL_TIME_STATUS *status)
627 {
628  ulong date[5];
629  ulonglong value;
630  const char *end=str+length, *end_of_days;
631  my_bool found_days,found_hours;
632  uint state;
633 
634  my_time_status_init(status);
635  l_time->neg=0;
636  for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++)
637  length--;
638  if (str != end && *str == '-')
639  {
640  l_time->neg=1;
641  str++;
642  length--;
643  }
644  if (str == end)
645  return 1;
646 
647  /* Check first if this is a full TIMESTAMP */
648  if (length >= 12)
649  { /* Probably full timestamp */
650  (void) str_to_datetime(str, length, l_time,
651  (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), status);
652  if (l_time->time_type >= MYSQL_TIMESTAMP_ERROR)
653  return l_time->time_type == MYSQL_TIMESTAMP_ERROR;
654  my_time_status_init(status);
655  }
656 
657  /* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
658  for (value=0; str != end && my_isdigit(&my_charset_latin1,*str) ; str++)
659  value=value*10L + (long) (*str - '0');
660 
661  if (value > UINT_MAX)
662  return 1;
663 
664  /* Skip all space after 'days' */
665  end_of_days= str;
666  for (; str != end && my_isspace(&my_charset_latin1, str[0]) ; str++)
667  ;
668 
669  LINT_INIT(state);
670  found_days=found_hours=0;
671  if ((uint) (end-str) > 1 && str != end_of_days &&
672  my_isdigit(&my_charset_latin1, *str))
673  { /* Found days part */
674  date[0]= (ulong) value;
675  state= 1; /* Assume next is hours */
676  found_days= 1;
677  }
678  else if ((end-str) > 1 && *str == time_separator &&
679  my_isdigit(&my_charset_latin1, str[1]))
680  {
681  date[0]= 0; /* Assume we found hours */
682  date[1]= (ulong) value;
683  state=2;
684  found_hours=1;
685  str++; /* skip ':' */
686  }
687  else
688  {
689  /* String given as one number; assume HHMMSS format */
690  date[0]= 0;
691  date[1]= (ulong) (value/10000);
692  date[2]= (ulong) (value/100 % 100);
693  date[3]= (ulong) (value % 100);
694  state=4;
695  goto fractional;
696  }
697 
698  /* Read hours, minutes and seconds */
699  for (;;)
700  {
701  for (value=0; str != end && my_isdigit(&my_charset_latin1,*str) ; str++)
702  value=value*10L + (long) (*str - '0');
703  date[state++]= (ulong) value;
704  if (state == 4 || (end-str) < 2 || *str != time_separator ||
705  !my_isdigit(&my_charset_latin1,str[1]))
706  break;
707  str++; /* Skip time_separator (':') */
708  }
709 
710  if (state != 4)
711  { /* Not HH:MM:SS */
712  /* Fix the date to assume that seconds was given */
713  if (!found_hours && !found_days)
714  {
715  bmove_upp((uchar*) (date+4), (uchar*) (date+state),
716  sizeof(long)*(state-1));
717  memset(date, 0, sizeof(long)*(4-state));
718  }
719  else
720  memset((date+state), 0, sizeof(long)*(4-state));
721  }
722 
723 fractional:
724  /* Get fractional second part */
725  if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_latin1,str[1]))
726  {
727  int field_length= 5;
728  str++; value=(uint) (uchar) (*str - '0');
729  while (++str != end && my_isdigit(&my_charset_latin1, *str))
730  {
731  if (field_length-- > 0)
732  value= value*10 + (uint) (uchar) (*str - '0');
733  }
734  if (field_length >= 0)
735  {
736  status->fractional_digits= DATETIME_MAX_DECIMALS - field_length;
737  if (field_length > 0)
738  value*= (long) log_10_int[field_length];
739  }
740  else
741  {
742  /* Scan digits left after microseconds */
743  status->fractional_digits= 6;
744  status->nanoseconds= 100 * (int) (str[-1] - '0');
745  for ( ; str != end && my_isdigit(&my_charset_latin1, *str); str++)
746  { }
747  }
748  date[4]= (ulong) value;
749  }
750  else if ((end - str) == 1 && *str == '.')
751  {
752  str++;
753  date[4]= 0;
754  }
755  else
756  date[4]=0;
757 
758  /* Check for exponent part: E<gigit> | E<sign><digit> */
759  /* (may occur as result of %g formatting of time value) */
760  if ((end - str) > 1 &&
761  (*str == 'e' || *str == 'E') &&
762  (my_isdigit(&my_charset_latin1, str[1]) ||
763  ((str[1] == '-' || str[1] == '+') &&
764  (end - str) > 2 &&
765  my_isdigit(&my_charset_latin1, str[2]))))
766  return 1;
767 
768  if (internal_format_positions[7] != 255)
769  {
770  /* Read a possible AM/PM */
771  while (str != end && my_isspace(&my_charset_latin1, *str))
772  str++;
773  if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
774  {
775  if (str[0] == 'p' || str[0] == 'P')
776  {
777  str+= 2;
778  date[1]= date[1]%12 + 12;
779  }
780  else if (str[0] == 'a' || str[0] == 'A')
781  str+=2;
782  }
783  }
784 
785  /* Integer overflow checks */
786  if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
787  date[2] > UINT_MAX || date[3] > UINT_MAX ||
788  date[4] > UINT_MAX)
789  return 1;
790 
791  l_time->year= 0; /* For protocol::store_time */
792  l_time->month= 0;
793 
794  l_time->day= 0;
795  l_time->hour= date[1] + date[0] * 24; /* Mix days and hours */
796 
797  l_time->minute= date[2];
798  l_time->second= date[3];
799  l_time->second_part= date[4];
800  l_time->time_type= MYSQL_TIMESTAMP_TIME;
801 
802  if (check_time_mmssff_range(l_time))
803  {
804  status->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
805  return TRUE;
806  }
807 
808  /* Adjust the value into supported MYSQL_TIME range */
809  adjust_time_range(l_time, &status->warnings);
810 
811  /* Check if there is garbage at end of the MYSQL_TIME specification */
812  if (str != end)
813  {
814  do
815  {
816  if (!my_isspace(&my_charset_latin1,*str))
817  {
818  status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
819  break;
820  }
821  } while (++str != end);
822  }
823  return 0;
824 }
825 
826 
836 my_bool
837 number_to_time(longlong nr, MYSQL_TIME *ltime, int *warnings)
838 {
839  if (nr > TIME_MAX_VALUE)
840  {
841  /* For huge numbers try full DATETIME, like str_to_time does. */
842  if (nr >= 10000000000LL) /* '0001-00-00 00-00-00' */
843  {
844  int warnings_backup= *warnings;
845  if (number_to_datetime(nr, ltime, 0, warnings) != LL(-1))
846  return FALSE;
847  *warnings= warnings_backup;
848  }
849  set_max_time(ltime, 0);
850  *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
851  return TRUE;
852  }
853  else if (nr < -TIME_MAX_VALUE)
854  {
855  set_max_time(ltime, 1);
856  *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
857  return TRUE;
858  }
859  if ((ltime->neg= (nr < 0)))
860  nr= -nr;
861  if (nr % 100 >= 60 || nr / 100 % 100 >= 60) /* Check hours and minutes */
862  {
863  set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
864  *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
865  return TRUE;
866  }
867  ltime->time_type= MYSQL_TIMESTAMP_TIME;
868  ltime->year= ltime->month= ltime->day= 0;
869  TIME_set_hhmmss(ltime, nr);
870  ltime->second_part= 0;
871  return FALSE;
872 }
873 
874 
884 void adjust_time_range(struct st_mysql_time *my_time, int *warning)
885 {
886  DBUG_ASSERT(!check_time_mmssff_range(my_time));
887  if (check_time_range_quick(my_time))
888  {
889  my_time->day= my_time->second_part= 0;
890  set_max_hhmmss(my_time);
891  *warning|= MYSQL_TIME_WARN_OUT_OF_RANGE;
892  }
893 }
894 
895 
896 /*
897  Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
898 
899  SYNOPSIS
900  my_init_time()
901 */
902 void my_init_time(void)
903 {
904  time_t seconds;
905  struct tm *l_time,tm_tmp;
906  MYSQL_TIME my_time;
907  my_bool not_used;
908 
909  seconds= (time_t) time((time_t*) 0);
910  localtime_r(&seconds,&tm_tmp);
911  l_time= &tm_tmp;
912  my_time_zone= 3600; /* Comp. for -3600 in my_gmt_sec */
913  my_time.year= (uint) l_time->tm_year+1900;
914  my_time.month= (uint) l_time->tm_mon+1;
915  my_time.day= (uint) l_time->tm_mday;
916  my_time.hour= (uint) l_time->tm_hour;
917  my_time.minute= (uint) l_time->tm_min;
918  my_time.second= (uint) l_time->tm_sec;
919  my_time.time_type= MYSQL_TIMESTAMP_DATETIME;
920  my_time.neg= 0;
921  my_time.second_part= 0;
922  my_system_gmt_sec(&my_time, &my_time_zone, &not_used); /* Init my_time_zone */
923 }
924 
925 
926 /*
927  Handle 2 digit year conversions
928 
929  SYNOPSIS
930  year_2000_handling()
931  year 2 digit year
932 
933  RETURN
934  Year between 1970-2069
935 */
936 
937 uint year_2000_handling(uint year)
938 {
939  if ((year=year+1900) < 1900+YY_PART_YEAR)
940  year+=100;
941  return year;
942 }
943 
944 
945 /*
946  Calculate nr of day since year 0 in new date-system (from 1615)
947 
948  SYNOPSIS
949  calc_daynr()
950  year Year (exact 4 digit year, no year conversions)
951  month Month
952  day Day
953 
954  NOTES: 0000-00-00 is a valid date, and will return 0
955 
956  RETURN
957  Days since 0000-00-00
958 */
959 
960 long calc_daynr(uint year,uint month,uint day)
961 {
962  long delsum;
963  int temp;
964  int y= year; /* may be < 0 temporarily */
965  DBUG_ENTER("calc_daynr");
966 
967  if (y == 0 && month == 0)
968  DBUG_RETURN(0); /* Skip errors */
969  /* Cast to int to be able to handle month == 0 */
970  delsum= (long) (365 * y + 31 *((int) month - 1) + (int) day);
971  if (month <= 2)
972  y--;
973  else
974  delsum-= (long) ((int) month * 4 + 23) / 10;
975  temp=(int) ((y/100+1)*3)/4;
976  DBUG_PRINT("exit",("year: %d month: %d day: %d -> daynr: %ld",
977  y+(month <= 2),month,day,delsum+y/4-temp));
978  DBUG_ASSERT(delsum+(int) y/4-temp >= 0);
979  DBUG_RETURN(delsum+(int) y/4-temp);
980 } /* calc_daynr */
981 
982 
983 /*
984  Convert time in MYSQL_TIME representation in system time zone to its
985  my_time_t form (number of seconds in UTC since begginning of Unix Epoch).
986 
987  SYNOPSIS
988  my_system_gmt_sec()
989  t - time value to be converted
990  my_timezone - pointer to long where offset of system time zone
991  from UTC will be stored for caching
992  in_dst_time_gap - set to true if time falls into spring time-gap
993 
994  NOTES
995  The idea is to cache the time zone offset from UTC (including daylight
996  saving time) for the next call to make things faster. But currently we
997  just calculate this offset during startup (by calling my_init_time()
998  function) and use it all the time.
999  Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
1000  is not allowed).
1001 
1002  RETURN VALUE
1003  Time in UTC seconds since Unix Epoch representation.
1004 */
1005 my_time_t
1006 my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone,
1007  my_bool *in_dst_time_gap)
1008 {
1009  uint loop;
1010  time_t tmp= 0;
1011  int shift= 0;
1012  MYSQL_TIME tmp_time;
1013  MYSQL_TIME *t= &tmp_time;
1014  struct tm *l_time,tm_tmp;
1015  long diff, current_timezone;
1016 
1017  /*
1018  Use temp variable to avoid trashing input data, which could happen in
1019  case of shift required for boundary dates processing.
1020  */
1021  memcpy(&tmp_time, t_src, sizeof(MYSQL_TIME));
1022 
1023  if (!validate_timestamp_range(t))
1024  return 0;
1025 
1026  /*
1027  Calculate the gmt time based on current time and timezone
1028  The -1 on the end is to ensure that if have a date that exists twice
1029  (like 2002-10-27 02:00:0 MET), we will find the initial date.
1030 
1031  By doing -3600 we will have to call localtime_r() several times, but
1032  I couldn't come up with a better way to get a repeatable result :(
1033 
1034  We can't use mktime() as it's buggy on many platforms and not thread safe.
1035 
1036  Note: this code assumes that our time_t estimation is not too far away
1037  from real value (we assume that localtime_r(tmp) will return something
1038  within 24 hrs from t) which is probably true for all current time zones.
1039 
1040  Note2: For the dates, which have time_t representation close to
1041  MAX_INT32 (efficient time_t limit for supported platforms), we should
1042  do a small trick to avoid overflow. That is, convert the date, which is
1043  two days earlier, and then add these days to the final value.
1044 
1045  The same trick is done for the values close to 0 in time_t
1046  representation for platfroms with unsigned time_t (QNX).
1047 
1048  To be more verbose, here is a sample (extracted from the code below):
1049  (calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
1050  would return -2147480896 because of the long type overflow. In result
1051  we would get 1901 year in localtime_r(), which is an obvious error.
1052 
1053  Alike problem raises with the dates close to Epoch. E.g.
1054  (calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
1055  will give -3600.
1056 
1057  On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
1058  wil give us a date around 2106 year. Which is no good.
1059 
1060  Theoreticaly, there could be problems with the latter conversion:
1061  there are at least two timezones, which had time switches near 1 Jan
1062  of 1970 (because of political reasons). These are America/Hermosillo and
1063  America/Mazatlan time zones. They changed their offset on
1064  1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
1065  the code below will give incorrect results for dates close to
1066  1970-01-01, in the case OS takes into account these historical switches.
1067  Luckily, it seems that we support only one platform with unsigned
1068  time_t. It's QNX. And QNX does not support historical timezone data at all.
1069  E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
1070  historical information for localtime_r() etc. That is, the problem is not
1071  relevant to QNX.
1072 
1073  We are safe with shifts close to MAX_INT32, as there are no known
1074  time switches on Jan 2038 yet :)
1075  */
1076  if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && (t->day > 4))
1077  {
1078  /*
1079  Below we will pass (uint) (t->day - shift) to calc_daynr.
1080  As we don't want to get an overflow here, we will shift
1081  only safe dates. That's why we have (t->day > 4) above.
1082  */
1083  t->day-= 2;
1084  shift= 2;
1085  }
1086 #ifdef TIME_T_UNSIGNED
1087  else
1088  {
1089  /*
1090  We can get 0 in time_t representaion only on 1969, 31 of Dec or on
1091  1970, 1 of Jan. For both dates we use shift, which is added
1092  to t->day in order to step out a bit from the border.
1093  This is required for platforms, where time_t is unsigned.
1094  As far as I know, among the platforms we support it's only QNX.
1095  Note: the order of below if-statements is significant.
1096  */
1097 
1098  if ((t->year == TIMESTAMP_MIN_YEAR + 1) && (t->month == 1)
1099  && (t->day <= 10))
1100  {
1101  t->day+= 2;
1102  shift= -2;
1103  }
1104 
1105  if ((t->year == TIMESTAMP_MIN_YEAR) && (t->month == 12)
1106  && (t->day == 31))
1107  {
1108  t->year++;
1109  t->month= 1;
1110  t->day= 2;
1111  shift= -2;
1112  }
1113  }
1114 #endif
1115 
1116  tmp= (time_t) (((calc_daynr((uint) t->year, (uint) t->month, (uint) t->day) -
1117  (long) days_at_timestart) * SECONDS_IN_24H +
1118  (long) t->hour*3600L +
1119  (long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
1120  3600);
1121 
1122  current_timezone= my_time_zone;
1123  localtime_r(&tmp,&tm_tmp);
1124  l_time=&tm_tmp;
1125  for (loop=0;
1126  loop < 2 &&
1127  (t->hour != (uint) l_time->tm_hour ||
1128  t->minute != (uint) l_time->tm_min ||
1129  t->second != (uint) l_time->tm_sec);
1130  loop++)
1131  { /* One check should be enough ? */
1132  /* Get difference in days */
1133  int days= t->day - l_time->tm_mday;
1134  if (days < -1)
1135  days= 1; /* Month has wrapped */
1136  else if (days > 1)
1137  days= -1;
1138  diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
1139  (long) (60*((int) t->minute - (int) l_time->tm_min)) +
1140  (long) ((int) t->second - (int) l_time->tm_sec));
1141  current_timezone+= diff+3600; /* Compensate for -3600 above */
1142  tmp+= (time_t) diff;
1143  localtime_r(&tmp,&tm_tmp);
1144  l_time=&tm_tmp;
1145  }
1146  /*
1147  Fix that if we are in the non existing daylight saving time hour
1148  we move the start of the next real hour.
1149 
1150  This code doesn't handle such exotical thing as time-gaps whose length
1151  is more than one hour or non-integer (latter can theoretically happen
1152  if one of seconds will be removed due leap correction, or because of
1153  general time correction like it happened for Africa/Monrovia time zone
1154  in year 1972).
1155  */
1156  if (loop == 2 && t->hour != (uint) l_time->tm_hour)
1157  {
1158  int days= t->day - l_time->tm_mday;
1159  if (days < -1)
1160  days=1; /* Month has wrapped */
1161  else if (days > 1)
1162  days= -1;
1163  diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
1164  (long) (60*((int) t->minute - (int) l_time->tm_min)) +
1165  (long) ((int) t->second - (int) l_time->tm_sec));
1166  if (diff == 3600)
1167  tmp+=3600 - t->minute*60 - t->second; /* Move to next hour */
1168  else if (diff == -3600)
1169  tmp-=t->minute*60 + t->second; /* Move to previous hour */
1170 
1171  *in_dst_time_gap= 1;
1172  }
1173  *my_timezone= current_timezone;
1174 
1175 
1176  /* shift back, if we were dealing with boundary dates */
1177  tmp+= shift * SECONDS_IN_24H;
1178 
1179  /*
1180  This is possible for dates, which slightly exceed boundaries.
1181  Conversion will pass ok for them, but we don't allow them.
1182  First check will pass for platforms with signed time_t.
1183  instruction above (tmp+= shift*86400L) could exceed
1184  MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
1185  So, tmp < TIMESTAMP_MIN_VALUE will be triggered. On platfroms
1186  with unsigned time_t tmp+= shift*86400L might result in a number,
1187  larger then TIMESTAMP_MAX_VALUE, so another check will work.
1188  */
1189  if (!IS_TIME_T_VALID_FOR_TIMESTAMP(tmp))
1190  tmp= 0;
1191 
1192  return (my_time_t) tmp;
1193 } /* my_system_gmt_sec */
1194 
1195 
1203 static inline int
1204 my_useconds_to_str(char *to, ulong useconds, uint dec)
1205 {
1206  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1207  return sprintf(to, ".%0*lu", (int) dec,
1208  useconds / (ulong) log_10_int[DATETIME_MAX_DECIMALS - dec]);
1209 }
1210 
1211 
1212 /*
1213  Functions to convert time/date/datetime value to a string,
1214  using default format.
1215  This functions don't check that given MYSQL_TIME structure members are
1216  in valid range. If they are not, return value won't reflect any
1217  valid date either. Additionally, make_time doesn't take into
1218  account time->day member: it's assumed that days have been converted
1219  to hours already.
1220 
1221  RETURN
1222  number of characters written to 'to'
1223 */
1224 
1225 int my_time_to_str(const MYSQL_TIME *l_time, char *to, uint dec)
1226 {
1227  uint extra_hours= 0;
1228  int len= sprintf(to, "%s%02u:%02u:%02u", (l_time->neg ? "-" : ""),
1229  extra_hours + l_time->hour, l_time->minute, l_time->second);
1230  if (dec)
1231  len+= my_useconds_to_str(to + len, l_time->second_part, dec);
1232  return len;
1233 }
1234 
1235 int my_date_to_str(const MYSQL_TIME *l_time, char *to)
1236 {
1237  return sprintf(to, "%04u-%02u-%02u",
1238  l_time->year, l_time->month, l_time->day);
1239 }
1240 
1241 
1242 /*
1243  Convert datetime to a string 'YYYY-MM-DD hh:mm:ss'.
1244  Open coded for better performance.
1245  This code previously resided in field.cc, in Field_timestamp::val_str().
1246 
1247  @param to OUT The string pointer to print at.
1248  @param ltime The MYSQL_TIME value.
1249  @return The length of the result string.
1250 */
1251 static inline int
1252 TIME_to_datetime_str(char *to, const MYSQL_TIME *ltime)
1253 {
1254  uint32 temp, temp2;
1255  /* Year */
1256  temp= ltime->year / 100;
1257  *to++= (char) ('0' + temp / 10);
1258  *to++= (char) ('0' + temp % 10);
1259  temp= ltime->year % 100;
1260  *to++= (char) ('0' + temp / 10);
1261  *to++= (char) ('0' + temp % 10);
1262  *to++= '-';
1263  /* Month */
1264  temp= ltime->month;
1265  temp2= temp / 10;
1266  temp= temp-temp2 * 10;
1267  *to++= (char) ('0' + (char) (temp2));
1268  *to++= (char) ('0' + (char) (temp));
1269  *to++= '-';
1270  /* Day */
1271  temp= ltime->day;
1272  temp2= temp / 10;
1273  temp= temp - temp2 * 10;
1274  *to++= (char) ('0' + (char) (temp2));
1275  *to++= (char) ('0' + (char) (temp));
1276  *to++= ' ';
1277  /* Hour */
1278  temp= ltime->hour;
1279  temp2= temp / 10;
1280  temp= temp - temp2 * 10;
1281  *to++= (char) ('0' + (char) (temp2));
1282  *to++= (char) ('0' + (char) (temp));
1283  *to++= ':';
1284  /* Minute */
1285  temp= ltime->minute;
1286  temp2= temp / 10;
1287  temp= temp - temp2 * 10;
1288  *to++= (char) ('0' + (char) (temp2));
1289  *to++= (char) ('0' + (char) (temp));
1290  *to++= ':';
1291  /* Second */
1292  temp= ltime->second;
1293  temp2=temp / 10;
1294  temp= temp - temp2 * 10;
1295  *to++= (char) ('0' + (char) (temp2));
1296  *to++= (char) ('0' + (char) (temp));
1297  return 19;
1298 }
1299 
1300 
1308 int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint dec)
1309 {
1310  int len= TIME_to_datetime_str(to, l_time);
1311  if (dec)
1312  len+= my_useconds_to_str(to + len, l_time->second_part, dec);
1313  else
1314  to[len]= '\0';
1315  return len;
1316 }
1317 
1318 
1319 /*
1320  Convert struct DATE/TIME/DATETIME value to string using built-in
1321  MySQL time conversion formats.
1322 
1323  SYNOPSIS
1324  my_TIME_to_string()
1325 
1326  NOTE
1327  The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved.
1328 */
1329 
1330 int my_TIME_to_str(const MYSQL_TIME *l_time, char *to, uint dec)
1331 {
1332  switch (l_time->time_type) {
1333  case MYSQL_TIMESTAMP_DATETIME:
1334  return my_datetime_to_str(l_time, to, dec);
1335  case MYSQL_TIMESTAMP_DATE:
1336  return my_date_to_str(l_time, to);
1337  case MYSQL_TIMESTAMP_TIME:
1338  return my_time_to_str(l_time, to, dec);
1339  case MYSQL_TIMESTAMP_NONE:
1340  case MYSQL_TIMESTAMP_ERROR:
1341  to[0]='\0';
1342  return 0;
1343  default:
1344  DBUG_ASSERT(0);
1345  return 0;
1346  }
1347 }
1348 
1349 
1358 int my_timeval_to_str(const struct timeval *tm, char *to, uint dec)
1359 {
1360  int len= sprintf(to, "%d", (int) tm->tv_sec);
1361  if (dec)
1362  len+= my_useconds_to_str(to + len, tm->tv_usec, dec);
1363  return len;
1364 }
1365 
1366 
1367 /*
1368  Convert datetime value specified as number to broken-down TIME
1369  representation and form value of DATETIME type as side-effect.
1370 
1371  SYNOPSIS
1372  number_to_datetime()
1373  nr - datetime value as number
1374  time_res - pointer for structure for broken-down representation
1375  flags - flags to use in validating date, as in str_to_datetime()
1376  was_cut 0 Value ok
1377  1 If value was cut during conversion
1378  2 check_date(date,flags) considers date invalid
1379 
1380  DESCRIPTION
1381  Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
1382  YYYYMMDDHHMMSS to broken-down MYSQL_TIME representation. Return value in
1383  YYYYMMDDHHMMSS format as side-effect.
1384 
1385  This function also checks if datetime value fits in DATETIME range.
1386 
1387  RETURN VALUE
1388  -1 Timestamp with wrong values
1389  anything else DATETIME as integer in YYYYMMDDHHMMSS format
1390  Datetime value in YYYYMMDDHHMMSS format.
1391 
1392  was_cut if return value -1: one of
1393  - MYSQL_TIME_WARN_OUT_OF_RANGE
1394  - MYSQL_TIME_WARN_ZERO_DATE
1395  - MYSQL_TIME_WARN_TRUNCATED
1396  otherwise 0.
1397 */
1398 
1399 longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res,
1400  ulonglong flags, int *was_cut)
1401 {
1402  long part1,part2;
1403 
1404  *was_cut= 0;
1405  memset(time_res, 0, sizeof(*time_res));
1406  time_res->time_type=MYSQL_TIMESTAMP_DATE;
1407 
1408  if (nr == LL(0) || nr >= LL(10000101000000))
1409  {
1410  time_res->time_type=MYSQL_TIMESTAMP_DATETIME;
1411  if (nr > 99999999999999LL) /* 9999-99-99 99:99:99 */
1412  {
1413  *was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
1414  return LL(-1);
1415  }
1416  goto ok;
1417  }
1418  if (nr < 101)
1419  goto err;
1420  if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
1421  {
1422  nr= (nr+20000000L)*1000000L; /* YYMMDD, year: 2000-2069 */
1423  goto ok;
1424  }
1425  if (nr < (YY_PART_YEAR)*10000L+101L)
1426  goto err;
1427  if (nr <= 991231L)
1428  {
1429  nr= (nr+19000000L)*1000000L; /* YYMMDD, year: 1970-1999 */
1430  goto ok;
1431  }
1432  /*
1433  Though officially we support DATE values from 1000-01-01 only, one can
1434  easily insert a value like 1-1-1. So, for consistency reasons such dates
1435  are allowed when TIME_FUZZY_DATE is set.
1436  */
1437  if (nr < 10000101L && !(flags & TIME_FUZZY_DATE))
1438  goto err;
1439  if (nr <= 99991231L)
1440  {
1441  nr= nr*1000000L;
1442  goto ok;
1443  }
1444  if (nr < 101000000L)
1445  goto err;
1446 
1447  time_res->time_type=MYSQL_TIMESTAMP_DATETIME;
1448 
1449  if (nr <= (YY_PART_YEAR-1)*LL(10000000000)+LL(1231235959))
1450  {
1451  nr= nr+LL(20000000000000); /* YYMMDDHHMMSS, 2000-2069 */
1452  goto ok;
1453  }
1454  if (nr < YY_PART_YEAR*LL(10000000000)+ LL(101000000))
1455  goto err;
1456  if (nr <= LL(991231235959))
1457  nr= nr+LL(19000000000000); /* YYMMDDHHMMSS, 1970-1999 */
1458 
1459  ok:
1460  part1=(long) (nr/LL(1000000));
1461  part2=(long) (nr - (longlong) part1*LL(1000000));
1462  time_res->year= (int) (part1/10000L); part1%=10000L;
1463  time_res->month= (int) part1 / 100;
1464  time_res->day= (int) part1 % 100;
1465  time_res->hour= (int) (part2/10000L); part2%=10000L;
1466  time_res->minute=(int) part2 / 100;
1467  time_res->second=(int) part2 % 100;
1468 
1469  if (!check_datetime_range(time_res) &&
1470  !check_date(time_res, (nr != 0), flags, was_cut))
1471  return nr;
1472 
1473  /* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */
1474  if (!nr && (flags & TIME_NO_ZERO_DATE))
1475  return LL(-1);
1476 
1477  err:
1478  *was_cut= MYSQL_TIME_WARN_TRUNCATED;
1479  return LL(-1);
1480 }
1481 
1482 
1488 ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *my_time)
1489 {
1490  return ((ulonglong) (my_time->year * 10000UL +
1491  my_time->month * 100UL +
1492  my_time->day) * ULL(1000000) +
1493  (ulonglong) (my_time->hour * 10000UL +
1494  my_time->minute * 100UL +
1495  my_time->second));
1496 }
1497 
1498 
1499 
1505 ulonglong TIME_to_ulonglong_date(const MYSQL_TIME *my_time)
1506 {
1507  return (ulonglong) (my_time->year * 10000UL + my_time->month * 100UL +
1508  my_time->day);
1509 }
1510 
1511 
1519 ulonglong TIME_to_ulonglong_time(const MYSQL_TIME *my_time)
1520 {
1521  return (ulonglong) (my_time->hour * 10000UL +
1522  my_time->minute * 100UL +
1523  my_time->second);
1524 }
1525 
1526 
1532 void TIME_set_yymmdd(MYSQL_TIME *ltime, uint yymmdd)
1533 {
1534  ltime->day= (int) (yymmdd % 100);
1535  ltime->month= (int) (yymmdd / 100) % 100;
1536  ltime->year= (int) (yymmdd / 10000);
1537 }
1538 
1539 
1545 void TIME_set_hhmmss(MYSQL_TIME *ltime, uint hhmmss)
1546 {
1547  ltime->second= (int) (hhmmss % 100);
1548  ltime->minute= (int) (hhmmss / 100) % 100;
1549  ltime->hour= (int) (hhmmss / 10000);
1550 }
1551 
1552 
1553 /*
1554  Convert struct MYSQL_TIME (date and time split into year/month/day/hour/...
1555  to a number in format YYYYMMDDHHMMSS (DATETIME),
1556  YYYYMMDD (DATE) or HHMMSS (TIME).
1557 
1558  SYNOPSIS
1559  TIME_to_ulonglong()
1560 
1561  DESCRIPTION
1562  The function is used when we need to convert value of time item
1563  to a number if it's used in numeric context, i. e.:
1564  SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
1565  SELECT ?+1;
1566 
1567  NOTE
1568  This function doesn't check that given MYSQL_TIME structure members are
1569  in valid range. If they are not, return value won't reflect any
1570  valid date either.
1571 */
1572 
1573 ulonglong TIME_to_ulonglong(const MYSQL_TIME *my_time)
1574 {
1575  switch (my_time->time_type) {
1576  case MYSQL_TIMESTAMP_DATETIME:
1577  return TIME_to_ulonglong_datetime(my_time);
1578  case MYSQL_TIMESTAMP_DATE:
1579  return TIME_to_ulonglong_date(my_time);
1580  case MYSQL_TIMESTAMP_TIME:
1581  return TIME_to_ulonglong_time(my_time);
1582  case MYSQL_TIMESTAMP_NONE:
1583  case MYSQL_TIMESTAMP_ERROR:
1584  return ULL(0);
1585  default:
1586  DBUG_ASSERT(0);
1587  }
1588  return 0;
1589 }
1590 
1591 
1592 /*** TIME low-level memory and disk representation routines ***/
1593 
1594 /*
1595  In-memory format:
1596 
1597  1 bit sign (Used for sign, when on disk)
1598  1 bit unused (Reserved for wider hour range, e.g. for intervals)
1599  10 bit hour (0-836)
1600  6 bit minute (0-59)
1601  6 bit second (0-59)
1602  24 bits microseconds (0-999999)
1603 
1604  Total: 48 bits = 6 bytes
1605  Suhhhhhh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
1606 */
1607 
1608 
1615 longlong TIME_to_longlong_time_packed(const MYSQL_TIME *ltime)
1616 {
1617  /* If month is 0, we mix day with hours: "1 00:10:10" -> "24:00:10" */
1618  long hms= (((ltime->month ? 0 : ltime->day * 24) + ltime->hour) << 12) |
1619  (ltime->minute << 6) | ltime->second;
1620  longlong tmp= MY_PACKED_TIME_MAKE(hms, ltime->second_part);
1621  return ltime->neg ? -tmp : tmp;
1622 }
1623 
1624 
1631 void TIME_from_longlong_time_packed(MYSQL_TIME *ltime, longlong tmp)
1632 {
1633  long hms;
1634  if ((ltime->neg= (tmp < 0)))
1635  tmp= -tmp;
1636  hms= MY_PACKED_TIME_GET_INT_PART(tmp);
1637  ltime->year= (uint) 0;
1638  ltime->month= (uint) 0;
1639  ltime->day= (uint) 0;
1640  ltime->hour= (uint) (hms >> 12) % (1 << 10); /* 10 bits starting at 12th */
1641  ltime->minute= (uint) (hms >> 6) % (1 << 6); /* 6 bits starting at 6th */
1642  ltime->second= (uint) hms % (1 << 6); /* 6 bits starting at 0th */
1643  ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
1644  ltime->time_type= MYSQL_TIMESTAMP_TIME;
1645 }
1646 
1647 
1653 uint my_time_binary_length(uint dec)
1654 {
1655  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1656  return 3 + (dec + 1) / 2;
1657 }
1658 
1659 
1660 /*
1661  On disk we convert from signed representation to unsigned
1662  representation using TIMEF_OFS, so all values become binary comparable.
1663 */
1664 #define TIMEF_OFS 0x800000000000LL
1665 #define TIMEF_INT_OFS 0x800000LL
1666 
1667 
1675 void my_time_packed_to_binary(longlong nr, uchar *ptr, uint dec)
1676 {
1677  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1678  /* Make sure the stored value was previously properly rounded or truncated */
1679  DBUG_ASSERT((MY_PACKED_TIME_GET_FRAC_PART(nr) %
1680  (int) log_10_int[DATETIME_MAX_DECIMALS - dec]) == 0);
1681 
1682  switch (dec)
1683  {
1684  case 0:
1685  default:
1686  mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
1687  break;
1688 
1689  case 1:
1690  case 2:
1691  mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
1692  ptr[3]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
1693  break;
1694 
1695  case 4:
1696  case 3:
1697  mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
1698  mi_int2store(ptr + 3, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
1699  break;
1700 
1701  case 5:
1702  case 6:
1703  mi_int6store(ptr, nr + TIMEF_OFS);
1704  break;
1705  }
1706 }
1707 
1708 
1717 longlong my_time_packed_from_binary(const uchar *ptr, uint dec)
1718 {
1719  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1720 
1721  switch (dec)
1722  {
1723  case 0:
1724  default:
1725  {
1726  longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
1727  return MY_PACKED_TIME_MAKE_INT(intpart);
1728  }
1729  case 1:
1730  case 2:
1731  {
1732  longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
1733  int frac= (uint) ptr[3];
1734  if (intpart < 0 && frac)
1735  {
1736  /*
1737  Negative values are stored with reverse fractional part order,
1738  for binary sort compatibility.
1739 
1740  Disk value intpart frac Time value Memory value
1741  800000.00 0 0 00:00:00.00 0000000000.000000
1742  7FFFFF.FF -1 255 -00:00:00.01 FFFFFFFFFF.FFD8F0
1743  7FFFFF.9D -1 99 -00:00:00.99 FFFFFFFFFF.F0E4D0
1744  7FFFFF.00 -1 0 -00:00:01.00 FFFFFFFFFF.000000
1745  7FFFFE.FF -1 255 -00:00:01.01 FFFFFFFFFE.FFD8F0
1746  7FFFFE.F6 -2 246 -00:00:01.10 FFFFFFFFFE.FE7960
1747 
1748  Formula to convert fractional part from disk format
1749  (now stored in "frac" variable) to absolute value: "0x100 - frac".
1750  To reconstruct in-memory value, we shift
1751  to the next integer value and then substruct fractional part.
1752  */
1753  intpart++; /* Shift to the next integer value */
1754  frac-= 0x100; /* -(0x100 - frac) */
1755  }
1756  return MY_PACKED_TIME_MAKE(intpart, frac * 10000);
1757  }
1758 
1759  case 3:
1760  case 4:
1761  {
1762  longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
1763  int frac= mi_uint2korr(ptr + 3);
1764  if (intpart < 0 && frac)
1765  {
1766  /*
1767  Fix reverse fractional part order: "0x10000 - frac".
1768  See comments for FSP=1 and FSP=2 above.
1769  */
1770  intpart++; /* Shift to the next integer value */
1771  frac-= 0x10000; /* -(0x10000-frac) */
1772  }
1773  return MY_PACKED_TIME_MAKE(intpart, frac * 100);
1774  }
1775 
1776  case 5:
1777  case 6:
1778  return ((longlong) mi_uint6korr(ptr)) - TIMEF_OFS;
1779  }
1780 }
1781 
1782 
1783 /*** DATETIME and DATE low-level memory and disk representation routines ***/
1784 
1785 /*
1786  1 bit sign (used when on disk)
1787  17 bits year*13+month (year 0-9999, month 0-12)
1788  5 bits day (0-31)
1789  5 bits hour (0-23)
1790  6 bits minute (0-59)
1791  6 bits second (0-59)
1792  24 bits microseconds (0-999999)
1793 
1794  Total: 64 bits = 8 bytes
1795 
1796  SYYYYYYY.YYYYYYYY.YYdddddh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
1797 */
1798 
1804 longlong TIME_to_longlong_datetime_packed(const MYSQL_TIME *ltime)
1805 {
1806  longlong ymd= ((ltime->year * 13 + ltime->month) << 5) | ltime->day;
1807  longlong hms= (ltime->hour << 12) | (ltime->minute << 6) | ltime->second;
1808  longlong tmp= MY_PACKED_TIME_MAKE(((ymd << 17) | hms), ltime->second_part);
1809  DBUG_ASSERT(!check_datetime_range(ltime)); /* Make sure no overflow */
1810  return ltime->neg ? -tmp : tmp;
1811 }
1812 
1813 
1822 longlong TIME_to_longlong_date_packed(const MYSQL_TIME *ltime)
1823 {
1824  longlong ymd= ((ltime->year * 13 + ltime->month) << 5) | ltime->day;
1825  return MY_PACKED_TIME_MAKE_INT(ymd << 17);
1826 }
1827 
1828 
1833 longlong year_to_longlong_datetime_packed(long year)
1834 {
1835  longlong ymd= ((year * 13) << 5);
1836  return MY_PACKED_TIME_MAKE_INT(ymd << 17);
1837 }
1838 
1839 
1845 void TIME_from_longlong_datetime_packed(MYSQL_TIME *ltime, longlong tmp)
1846 {
1847  longlong ymd, hms;
1848  longlong ymdhms, ym;
1849  if ((ltime->neg= (tmp < 0)))
1850  tmp= -tmp;
1851 
1852  ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
1853  ymdhms= MY_PACKED_TIME_GET_INT_PART(tmp);
1854 
1855  ymd= ymdhms >> 17;
1856  ym= ymd >> 5;
1857  hms= ymdhms % (1 << 17);
1858 
1859  ltime->day= ymd % (1 << 5);
1860  ltime->month= ym % 13;
1861  ltime->year= ym / 13;
1862 
1863  ltime->second= hms % (1 << 6);
1864  ltime->minute= (hms >> 6) % (1 << 6);
1865  ltime->hour= (hms >> 12);
1866 
1867  ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
1868 }
1869 
1870 
1876 void TIME_from_longlong_date_packed(MYSQL_TIME *ltime, longlong tmp)
1877 {
1878  TIME_from_longlong_datetime_packed(ltime, tmp);
1879  ltime->time_type= MYSQL_TIMESTAMP_DATE;
1880 }
1881 
1882 
1887 uint my_datetime_binary_length(uint dec)
1888 {
1889  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1890  return 5 + (dec + 1) / 2;
1891 }
1892 
1893 
1894 /*
1895  On disk we store as unsigned number with DATETIMEF_INT_OFS offset,
1896  for HA_KETYPE_BINARY compatibilty purposes.
1897 */
1898 #define DATETIMEF_INT_OFS 0x8000000000LL
1899 
1900 
1909 longlong my_datetime_packed_from_binary(const uchar *ptr, uint dec)
1910 {
1911  longlong intpart= mi_uint5korr(ptr) - DATETIMEF_INT_OFS;
1912  int frac;
1913  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1914  switch (dec)
1915  {
1916  case 0:
1917  default:
1918  return MY_PACKED_TIME_MAKE_INT(intpart);
1919  case 1:
1920  case 2:
1921  frac= ((int) (signed char) ptr[5]) * 10000;
1922  break;
1923  case 3:
1924  case 4:
1925  frac= mi_sint2korr(ptr + 5) * 100;
1926  break;
1927  case 5:
1928  case 6:
1929  frac= mi_sint3korr(ptr + 5);
1930  break;
1931  }
1932  return MY_PACKED_TIME_MAKE(intpart, frac);
1933 }
1934 
1935 
1943 void my_datetime_packed_to_binary(longlong nr, uchar *ptr, uint dec)
1944 {
1945  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1946  /* The value being stored must have been properly rounded or truncated */
1947  DBUG_ASSERT((MY_PACKED_TIME_GET_FRAC_PART(nr) %
1948  (int) log_10_int[DATETIME_MAX_DECIMALS - dec]) == 0);
1949 
1950  mi_int5store(ptr, MY_PACKED_TIME_GET_INT_PART(nr) + DATETIMEF_INT_OFS);
1951  switch (dec)
1952  {
1953  case 0:
1954  default:
1955  break;
1956  case 1:
1957  case 2:
1958  ptr[5]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
1959  break;
1960  case 3:
1961  case 4:
1962  mi_int2store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
1963  break;
1964  case 5:
1965  case 6:
1966  mi_int3store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr));
1967  }
1968 }
1969 
1970 
1971 /*** TIMESTAMP low-level memory and disk representation routines ***/
1972 
1978 uint my_timestamp_binary_length(uint dec)
1979 {
1980  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1981  return 4 + (dec + 1) / 2;
1982 }
1983 
1984 
1992 void my_timestamp_from_binary(struct timeval *tm, const uchar *ptr, uint dec)
1993 {
1994  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
1995  tm->tv_sec= mi_uint4korr(ptr);
1996  switch (dec)
1997  {
1998  case 0:
1999  default:
2000  tm->tv_usec= 0;
2001  break;
2002  case 1:
2003  case 2:
2004  tm->tv_usec= ((int) ptr[4]) * 10000;
2005  break;
2006  case 3:
2007  case 4:
2008  tm->tv_usec= mi_sint2korr(ptr + 4) * 100;
2009  break;
2010  case 5:
2011  case 6:
2012  tm->tv_usec= mi_sint3korr(ptr + 4);
2013  }
2014 }
2015 
2016 
2024 void my_timestamp_to_binary(const struct timeval *tm, uchar *ptr, uint dec)
2025 {
2026  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
2027  /* Stored value must have been previously properly rounded or truncated */
2028  DBUG_ASSERT((tm->tv_usec %
2029  (int) log_10_int[DATETIME_MAX_DECIMALS - dec]) == 0);
2030  mi_int4store(ptr, tm->tv_sec);
2031  switch (dec)
2032  {
2033  case 0:
2034  default:
2035  break;
2036  case 1:
2037  case 2:
2038  ptr[4]= (unsigned char) (char) (tm->tv_usec / 10000);
2039  break;
2040  case 3:
2041  case 4:
2042  mi_int2store(ptr + 4, tm->tv_usec / 100);
2043  break;
2044  /* Impossible second precision. Fall through */
2045  case 5:
2046  case 6:
2047  mi_int3store(ptr + 4, tm->tv_usec);
2048  }
2049 }
2050 
2051 /****************************************/
2052 
2053 
2061 longlong TIME_to_longlong_packed(const MYSQL_TIME *ltime)
2062 {
2063  switch (ltime->time_type) {
2064  case MYSQL_TIMESTAMP_DATE:
2065  return TIME_to_longlong_date_packed(ltime);
2066  case MYSQL_TIMESTAMP_DATETIME:
2067  return TIME_to_longlong_datetime_packed(ltime);
2068  case MYSQL_TIMESTAMP_TIME:
2069  return TIME_to_longlong_time_packed(ltime);
2070  case MYSQL_TIMESTAMP_NONE:
2071  case MYSQL_TIMESTAMP_ERROR:
2072  return 0;
2073  }
2074  DBUG_ASSERT(0);
2075  return 0;
2076 }
2077 
2078 /*** End of low level format routines ***/