Drizzled Public API Documentation

time.cc
1 /* Copyright (C) 2004-2006 MySQL AB
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 <config.h>
17 
18 #include <drizzled/type/time.h>
19 
20 #include <drizzled/util/gmtime.h>
21 
22 #include <drizzled/internal/m_string.h>
23 #include <drizzled/charset.h>
24 #include <drizzled/util/test.h>
25 #include <drizzled/definitions.h>
26 #include <drizzled/sql_string.h>
27 
28 #include <cstdio>
29 #include <algorithm>
30 
31 using namespace std;
32 
33 namespace drizzled {
34 
35 static int check_time_range(type::Time *my_time, int *warning);
36 
37 /* Windows version of localtime_r() is declared in my_ptrhead.h */
38 
39 uint64_t log_10_int[20]=
40 {
41  1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
42  100000000ULL, 1000000000ULL, 10000000000ULL, 100000000000ULL,
43  1000000000000ULL, 10000000000000ULL, 100000000000000ULL,
44  1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL,
45  1000000000000000000ULL, 10000000000000000000ULL
46 };
47 
48 
49 /* Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format */
50 
51 static unsigned char internal_format_positions[]=
52 {0, 1, 2, 3, 4, 5, 6, (unsigned char) 255};
53 
54 static char time_separator=':';
55 
56 static uint32_t const days_at_timestart=719528; /* daynr at 1970.01.01 */
57 unsigned char days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
58 
59 /*
60  Offset of system time zone from UTC in seconds used to speed up
61  work of my_system_gmt_sec() function.
62 */
63 static long my_time_zone=0;
64 
65 
66 /* Calc days in one year. works with 0 <= year <= 99 */
67 
68 uint32_t calc_days_in_year(uint32_t year)
69 {
70  return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
71  366 : 365);
72 }
73 
74 
75 namespace type {
97 bool Time::check(bool not_zero_date, uint32_t flags, type::cut_t &was_cut) const
98 {
99  if (not_zero_date)
100  {
101  if ((((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
102  (month == 0 || day == 0)) ||
103  (not (flags & TIME_INVALID_DATES) &&
104  month && day > days_in_month[month-1] &&
105  (month != 2 || calc_days_in_year(year) != 366 ||
106  day != 29)))
107  {
108  was_cut= type::INVALID;
109  return true;
110  }
111  }
112  else if (flags & TIME_NO_ZERO_DATE)
113  {
114  /*
115  We don't set &was_cut here to signal that the problem was a zero date
116  and not an invalid date
117  */
118  return true;
119  }
120  return false;
121 }
122 
123 /*
124  Convert a timestamp string to a type::Time value.
125 
126  SYNOPSIS
127  store()
128  str String to parse
129  length Length of string
130  l_time Date is stored here
131  flags Bitmap of following items
132  TIME_FUZZY_DATE Set if we should allow partial dates
133  TIME_DATETIME_ONLY Set if we only allow full datetimes.
134  TIME_NO_ZERO_IN_DATE Don't allow partial dates
135  TIME_NO_ZERO_DATE Don't allow 0000-00-00 date
136  TIME_INVALID_DATES Allow 2000-02-31
137  was_cut 0 Value OK
138  1 If value was cut during conversion
139  2 check(date,flags) considers date invalid
140 
141  DESCRIPTION
142  At least the following formats are recogniced (based on number of digits)
143  YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
144  YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
145  YYYYMMDDTHHMMSS where T is a the character T (ISO8601)
146  Also dates where all parts are zero are allowed
147 
148  The second part may have an optional .###### fraction part.
149 
150  NOTES
151  This function should work with a format position vector as long as the
152  following things holds:
153  - All date are kept together and all time parts are kept together
154  - Date and time parts must be separated by blank
155  - Second fractions must come after second part and be separated
156  by a '.'. (The second fractions are optional)
157  - AM/PM must come after second fractions (or after seconds if no fractions)
158  - Year must always been specified.
159  - If time is before date, then we will use datetime format only if
160  the argument consist of two parts, separated by space.
161  Otherwise we will assume the argument is a date.
162  - The hour part must be specified in hour-minute-second order.
163 
164  RETURN VALUES
165  DRIZZLE_TIMESTAMP_NONE String wasn't a timestamp, like
166  [DD [HH:[MM:[SS]]]].fraction.
167  l_time is not changed.
168  DRIZZLE_TIMESTAMP_DATE DATE string (YY MM and DD parts ok)
169  DRIZZLE_TIMESTAMP_DATETIME Full timestamp
170  DRIZZLE_TIMESTAMP_ERROR Timestamp with wrong values.
171  All elements in l_time is set to 0
172 */
173 
174 #define MAX_DATE_PARTS 8
175 
176 type::timestamp_t Time::store(const char *str, uint32_t length, uint32_t flags, type::cut_t &was_cut)
177 {
178  uint32_t field_length, year_length=4, digits, i, number_of_fields;
179  uint32_t date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
180  uint32_t add_hours= 0, start_loop;
181  uint32_t not_zero_date, allow_space;
182  bool is_internal_format;
183  const char *pos, *last_field_pos=NULL;
184  const char *end=str+length;
185  const unsigned char *format_position;
186  bool found_delimitier= 0, found_space= 0;
187  uint32_t frac_pos, frac_len;
188 
189  was_cut= type::VALID;
190 
191  /* Skip space at start */
192  for (; str != end && my_charset_utf8_general_ci.isspace(*str) ; str++)
193  ;
194 
195  if (str == end || not my_charset_utf8_general_ci.isdigit(*str))
196  {
197  was_cut= type::CUT;
198  return(type::DRIZZLE_TIMESTAMP_NONE);
199  }
200 
201  is_internal_format= 0;
202  /* This has to be changed if want to activate different timestamp formats */
203  format_position= internal_format_positions;
204 
205  /*
206  Calculate number of digits in first part.
207  If length= 8 or >= 14 then year is of format YYYY.
208  (YYYY-MM-DD, YYYYMMDD, YYYYYMMDDHHMMSS)
209  */
210  for (pos=str;
211  pos != end && (my_charset_utf8_general_ci.isdigit(*pos) || *pos == 'T');
212  pos++)
213  ;
214 
215  digits= (uint32_t) (pos-str);
216  start_loop= 0; /* Start of scan loop */
217  date_len[format_position[0]]= 0; /* Length of year field */
218  if (pos == end || *pos == '.')
219  {
220  /* Found date in internal format (only numbers like YYYYMMDD) */
221  year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
222  field_length= year_length;
223  is_internal_format= 1;
224  format_position= internal_format_positions;
225  }
226  else
227  {
228  if (format_position[0] >= 3) /* If year is after HHMMDD */
229  {
230  /*
231  If year is not in first part then we have to determinate if we got
232  a date field or a datetime field.
233  We do this by checking if there is two numbers separated by
234  space in the input.
235  */
236  while (pos < end && not my_charset_utf8_general_ci.isspace(*pos))
237  pos++;
238  while (pos < end && not my_charset_utf8_general_ci.isdigit(*pos))
239  pos++;
240  if (pos == end)
241  {
242  if (flags & TIME_DATETIME_ONLY)
243  {
244  was_cut= type::CUT;
245  return(type::DRIZZLE_TIMESTAMP_NONE); /* Can't be a full datetime */
246  }
247  /* Date field. Set hour, minutes and seconds to 0 */
248  date[0]= date[1]= date[2]= date[3]= date[4]= 0;
249  start_loop= 5; /* Start with first date part */
250  }
251  }
252 
253  field_length= format_position[0] == 0 ? 4 : 2;
254  }
255 
256  /*
257  Only allow space in the first "part" of the datetime field and:
258  - after days, part seconds
259  - before and after AM/PM (handled by code later)
260 
261  2003-03-03 20:00:20 AM
262  20:00:20.000000 AM 03-03-2000
263  */
264  i= max((uint32_t) format_position[0], (uint32_t) format_position[1]);
265  set_if_bigger(i, (uint32_t) format_position[2]);
266  allow_space= ((1 << i) | (1 << format_position[6]));
267  allow_space&= (1 | 2 | 4 | 8);
268 
269  not_zero_date= 0;
270  for (i = start_loop;
271  i < MAX_DATE_PARTS-1 && str != end &&
272  my_charset_utf8_general_ci.isdigit(*str);
273  i++)
274  {
275  const char *start= str;
276  uint32_t tmp_value= (uint32_t) (unsigned char) (*str++ - '0');
277  while (str != end && my_charset_utf8_general_ci.isdigit(str[0]) &&
278  (!is_internal_format || --field_length))
279  {
280  tmp_value=tmp_value*10 + (uint32_t) (unsigned char) (*str - '0');
281  str++;
282  }
283  date_len[i]= (uint32_t) (str - start);
284  if (tmp_value > 999999) /* Impossible date part */
285  {
286  was_cut= type::CUT;
287  return(type::DRIZZLE_TIMESTAMP_NONE);
288  }
289  date[i]=tmp_value;
290  not_zero_date|= tmp_value;
291 
292  /* Length of next field */
293  field_length= format_position[i+1] == 0 ? 4 : 2;
294 
295  if ((last_field_pos= str) == end)
296  {
297  i++; /* Register last found part */
298  break;
299  }
300  /* Allow a 'T' after day to allow CCYYMMDDT type of fields */
301  if (i == format_position[2] && *str == 'T')
302  {
303  str++; /* ISO8601: CCYYMMDDThhmmss */
304  continue;
305  }
306  if (i == format_position[5]) /* Seconds */
307  {
308  if (*str == '.') /* Followed by part seconds */
309  {
310  str++;
311  field_length= 6; /* 6 digits */
312  }
313  continue;
314  }
315  while (str != end &&
316  (my_charset_utf8_general_ci.ispunct(*str) ||
317  my_charset_utf8_general_ci.isspace(*str)))
318  {
319  if (my_charset_utf8_general_ci.isspace(*str))
320  {
321  if (!(allow_space & (1 << i)))
322  {
323  was_cut= type::CUT;
324  return(type::DRIZZLE_TIMESTAMP_NONE);
325  }
326  found_space= 1;
327  }
328  str++;
329  found_delimitier= 1; /* Should be a 'normal' date */
330  }
331  /* Check if next position is AM/PM */
332  if (i == format_position[6]) /* Seconds, time for AM/PM */
333  {
334  i++; /* Skip AM/PM part */
335  if (format_position[7] != 255) /* If using AM/PM */
336  {
337  if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
338  {
339  if (str[0] == 'p' || str[0] == 'P')
340  add_hours= 12;
341  else if (str[0] != 'a' || str[0] != 'A')
342  continue; /* Not AM/PM */
343  str+= 2; /* Skip AM/PM */
344  /* Skip space after AM/PM */
345  while (str != end && my_charset_utf8_general_ci.isspace(*str))
346  str++;
347  }
348  }
349  }
350  last_field_pos= str;
351  }
352  if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
353  {
354  was_cut= type::CUT;
355  return(type::DRIZZLE_TIMESTAMP_NONE); /* Can't be a datetime */
356  }
357 
358  str= last_field_pos;
359 
360  number_of_fields= i - start_loop;
361  while (i < MAX_DATE_PARTS)
362  {
363  date_len[i]= 0;
364  date[i++]= 0;
365  }
366 
367  do
368  {
369  if (not is_internal_format)
370  {
371  year_length= date_len[(uint32_t) format_position[0]];
372  if (!year_length) /* Year must be specified */
373  {
374  was_cut= type::CUT;
375  return(type::DRIZZLE_TIMESTAMP_NONE);
376  }
377 
378  this->year= date[(uint32_t) format_position[0]];
379  this->month= date[(uint32_t) format_position[1]];
380  this->day= date[(uint32_t) format_position[2]];
381  this->hour= date[(uint32_t) format_position[3]];
382  this->minute= date[(uint32_t) format_position[4]];
383  this->second= date[(uint32_t) format_position[5]];
384 
385  frac_pos= (uint32_t) format_position[6];
386  frac_len= date_len[frac_pos];
387  if (frac_len < 6)
388  date[frac_pos]*= (uint32_t) log_10_int[6 - frac_len];
389  this->second_part= date[frac_pos];
390 
391  if (format_position[7] != (unsigned char) 255)
392  {
393  if (this->hour > 12)
394  {
395  was_cut= type::CUT;
396  break;
397  }
398  this->hour= this->hour%12 + add_hours;
399  }
400  }
401  else
402  {
403  this->year= date[0];
404  this->month= date[1];
405  this->day= date[2];
406  this->hour= date[3];
407  this->minute= date[4];
408  this->second= date[5];
409  if (date_len[6] < 6)
410  date[6]*= (uint32_t) log_10_int[6 - date_len[6]];
411  this->second_part=date[6];
412  }
413  this->neg= 0;
414 
415  if (year_length == 2 && not_zero_date)
416  this->year+= (this->year < YY_PART_YEAR ? 2000 : 1900);
417 
418  if (number_of_fields < 3 ||
419  this->year > 9999 || this->month > 12 ||
420  this->day > 31 || this->hour > 23 ||
421  this->minute > 59 || this->second > 59)
422  {
423  /* Only give warning for a zero date if there is some garbage after */
424  if (!not_zero_date) /* If zero date */
425  {
426  for (; str != end ; str++)
427  {
428  if (not my_charset_utf8_general_ci.isspace(*str))
429  {
430  not_zero_date= 1; /* Give warning */
431  break;
432  }
433  }
434  }
435  was_cut= test(not_zero_date) ? type::CUT : type::VALID;
436  break;
437  }
438 
439  if (check(not_zero_date != 0, flags, was_cut))
440  {
441  break;
442  }
443 
444  this->time_type= (number_of_fields <= 3 ?
445  type::DRIZZLE_TIMESTAMP_DATE : type::DRIZZLE_TIMESTAMP_DATETIME);
446 
447  for (; str != end ; str++)
448  {
449  if (not my_charset_utf8_general_ci.isspace(*str))
450  {
451  was_cut= type::CUT;
452  break;
453  }
454  }
455 
456  return(time_type= (number_of_fields <= 3 ? type::DRIZZLE_TIMESTAMP_DATE : type::DRIZZLE_TIMESTAMP_DATETIME));
457  } while (0);
458 
459  reset();
460 
461  return type::DRIZZLE_TIMESTAMP_ERROR;
462 }
463 
464 type::timestamp_t Time::store(const char *str, uint32_t length, uint32_t flags)
465 {
466  type::cut_t was_cut;
467  return store(str, length, flags, was_cut);
468 }
469 
470 /*
471  Convert a time string to a type::Time struct.
472 
473  SYNOPSIS
474  str_to_time()
475  str A string in full TIMESTAMP format or
476  [-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
477  [M]MSS or [S]S
478  There may be an optional [.second_part] after seconds
479  length Length of str
480  l_time Store result here
481  warning Set DRIZZLE_TIME_WARN_TRUNCATED flag if the input string
482  was cut during conversion, and/or
483  DRIZZLE_TIME_WARN_OUT_OF_RANGE flag, if the value is
484  out of range.
485 
486  NOTES
487  Because of the extra days argument, this function can only
488  work with times where the time arguments are in the above order.
489 
490  RETURN
491  0 ok
492  1 error
493 */
494 
495 bool Time::store(const char *str, uint32_t length, int &warning, type::timestamp_t arg)
496 {
497  uint32_t date[5];
498  uint64_t value;
499  const char *end=str+length, *end_of_days;
500  bool found_days,found_hours;
501  uint32_t state;
502 
503  assert(arg == DRIZZLE_TIMESTAMP_TIME);
504 
505  this->neg=0;
506  warning= 0;
507  for (; str != end && my_charset_utf8_general_ci.isspace(*str) ; str++)
508  length--;
509  if (str != end && *str == '-')
510  {
511  this->neg=1;
512  str++;
513  length--;
514  }
515  if (str == end)
516  return true;
517 
518  /* Check first if this is a full TIMESTAMP */
519  if (length >= 12)
520  { /* Probably full timestamp */
521  type::cut_t was_cut;
522  type::timestamp_t res= this->store(str, length, (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), was_cut);
523  if ((int) res >= (int) type::DRIZZLE_TIMESTAMP_ERROR)
524  {
525  if (was_cut != type::VALID)
526  warning|= DRIZZLE_TIME_WARN_TRUNCATED;
527 
528  return res == type::DRIZZLE_TIMESTAMP_ERROR;
529  }
530  }
531 
532  /* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
533  for (value=0; str != end && my_charset_utf8_general_ci.isdigit(*str) ; str++)
534  value=value*10L + (long) (*str - '0');
535 
536  /* Skip all space after 'days' */
537  end_of_days= str;
538  for (; str != end && my_charset_utf8_general_ci.isspace(str[0]) ; str++)
539  ;
540 
541  found_days=found_hours=0;
542  if ((uint32_t) (end-str) > 1 && str != end_of_days &&
543  my_charset_utf8_general_ci.isdigit(*str))
544  { /* Found days part */
545  date[0]= (uint32_t) value;
546  state= 1; /* Assume next is hours */
547  found_days= 1;
548  }
549  else if ((end-str) > 1 && *str == time_separator &&
550  my_charset_utf8_general_ci.isdigit(str[1]))
551  {
552  date[0]= 0; /* Assume we found hours */
553  date[1]= (uint32_t) value;
554  state=2;
555  found_hours=1;
556  str++; /* skip ':' */
557  }
558  else
559  {
560  /* String given as one number; assume HHMMSS format */
561  date[0]= 0;
562  date[1]= (uint32_t) (value/10000);
563  date[2]= (uint32_t) (value/100 % 100);
564  date[3]= (uint32_t) (value % 100);
565  state=4;
566  goto fractional;
567  }
568 
569  /* Read hours, minutes and seconds */
570  for (;;)
571  {
572  for (value=0; str != end && my_charset_utf8_general_ci.isdigit(*str) ; str++)
573  value=value*10L + (long) (*str - '0');
574  date[state++]= (uint32_t) value;
575  if (state == 4 || (end-str) < 2 || *str != time_separator ||
576  !my_charset_utf8_general_ci.isdigit(str[1]))
577  break;
578  str++; /* Skip time_separator (':') */
579  }
580 
581  if (state != 4)
582  { /* Not HH:MM:SS */
583  /* Fix the date to assume that seconds was given */
584  if (!found_hours && !found_days)
585  {
586  internal::bmove_upp((unsigned char*) (date+4), (unsigned char*) (date+state),
587  sizeof(long)*(state-1));
588  memset(date, 0, sizeof(long)*(4-state));
589  }
590  else
591  memset(date+state, 0, sizeof(long)*(4-state));
592  }
593 
594 fractional:
595  /* Get fractional second part */
596  if ((end-str) >= 2 && *str == '.' && my_charset_utf8_general_ci.isdigit(str[1]))
597  {
598  int field_length= 5;
599  str++; value=(uint32_t) (unsigned char) (*str - '0');
600  while (++str != end && my_charset_utf8_general_ci.isdigit(*str))
601  {
602  if (field_length-- > 0)
603  value= value*10 + (uint32_t) (unsigned char) (*str - '0');
604  }
605  if (field_length > 0)
606  {
607  value*= (long) log_10_int[field_length];
608  }
609  else if (field_length < 0)
610  {
611  warning|= DRIZZLE_TIME_WARN_TRUNCATED;
612  }
613 
614  date[4]= (uint32_t) value;
615  }
616  else
617  {
618  date[4]=0;
619  }
620 
621  /* Check for exponent part: E<gigit> | E<sign><digit> */
622  /* (may occur as result of %g formatting of time value) */
623  if ((end - str) > 1 &&
624  (*str == 'e' || *str == 'E') &&
625  (my_charset_utf8_general_ci.isdigit(str[1]) ||
626  ((str[1] == '-' || str[1] == '+') &&
627  (end - str) > 2 &&
628  my_charset_utf8_general_ci.isdigit(str[2]))))
629  return 1;
630 
631  if (internal_format_positions[7] != 255)
632  {
633  /* Read a possible AM/PM */
634  while (str != end && my_charset_utf8_general_ci.isspace(*str))
635  str++;
636  if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
637  {
638  if (str[0] == 'p' || str[0] == 'P')
639  {
640  str+= 2;
641  date[1]= date[1]%12 + 12;
642  }
643  else if (str[0] == 'a' || str[0] == 'A')
644  str+=2;
645  }
646  }
647 
648  /* Integer overflow checks */
649  if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
650  date[2] > UINT_MAX || date[3] > UINT_MAX ||
651  date[4] > UINT_MAX)
652  return 1;
653 
654  this->year= 0; /* For protocol::store_time */
655  this->month= 0;
656  this->day= date[0];
657  this->hour= date[1];
658  this->minute= date[2];
659  this->second= date[3];
660  this->second_part= date[4];
661  this->time_type= type::DRIZZLE_TIMESTAMP_TIME;
662 
663  /* Check if the value is valid and fits into type::Time range */
664  if (check_time_range(this, &warning))
665  {
666  return 1;
667  }
668 
669  /* Check if there is garbage at end of the type::Time specification */
670  if (str != end)
671  {
672  do
673  {
674  if (not my_charset_utf8_general_ci.isspace(*str))
675  {
676  warning|= DRIZZLE_TIME_WARN_TRUNCATED;
677  break;
678  }
679  } while (++str != end);
680  }
681  return 0;
682 }
683 
684 } // namespace type
685 
686 
687 
688 /*
689  Check 'time' value to lie in the type::Time range
690 
691  SYNOPSIS:
692  check_time_range()
693  time pointer to type::Time value
694  warning set DRIZZLE_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
695 
696  DESCRIPTION
697  If the time value lies outside of the range [-838:59:59, 838:59:59],
698  set it to the closest endpoint of the range and set
699  DRIZZLE_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
700 
701  RETURN
702  0 time value is valid, but was possibly truncated
703  1 time value is invalid
704 */
705 
706 static int check_time_range(type::Time *my_time, int *warning)
707 {
708  int64_t hour;
709 
710  if (my_time->minute >= 60 || my_time->second >= 60)
711  return 1;
712 
713  hour= my_time->hour + (24*my_time->day);
714  if (hour <= TIME_MAX_HOUR &&
715  (hour != TIME_MAX_HOUR || my_time->minute != TIME_MAX_MINUTE ||
716  my_time->second != TIME_MAX_SECOND || !my_time->second_part))
717  return 0;
718 
719  my_time->day= 0;
720  my_time->hour= TIME_MAX_HOUR;
721  my_time->minute= TIME_MAX_MINUTE;
722  my_time->second= TIME_MAX_SECOND;
723  my_time->second_part= 0;
724  *warning|= DRIZZLE_TIME_WARN_OUT_OF_RANGE;
725  return 0;
726 }
727 
728 
729 /*
730  Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
731 
732  SYNOPSIS
733  init_time()
734 */
735 void init_time(void)
736 {
737  type::Time my_time;
738  type::epoch_t epoch;
739 
740  time_t seconds= time(NULL);
741  tm tm_tmp;
742  localtime_r(&seconds, &tm_tmp);
743  tm* l_time= &tm_tmp;
744  my_time_zone= 3600; /* Comp. for -3600 in my_gmt_sec */
745  my_time.year= (uint32_t) l_time->tm_year+1900;
746  my_time.month= (uint32_t) l_time->tm_mon+1;
747  my_time.day= (uint32_t) l_time->tm_mday;
748  my_time.hour= (uint32_t) l_time->tm_hour;
749  my_time.minute= (uint32_t) l_time->tm_min;
750  my_time.second= (uint32_t) l_time->tm_sec;
751  my_time.time_type= type::DRIZZLE_TIMESTAMP_NONE;
752  my_time.second_part= 0;
753  my_time.neg= false;
754  my_time.convert(epoch, &my_time_zone); /* Init my_time_zone */
755 }
756 
757 
758 /*
759  Handle 2 digit year conversions
760 
761  SYNOPSIS
762  year_2000_handling()
763  year 2 digit year
764 
765  RETURN
766  Year between 1970-2069
767 */
768 
769 uint32_t year_2000_handling(uint32_t year)
770 {
771  if ((year=year+1900) < 1900+YY_PART_YEAR)
772  year+=100;
773  return year;
774 }
775 
776 
777 /*
778  Calculate nr of day since year 0 in new date-system (from 1615)
779 
780  SYNOPSIS
781  calc_daynr()
782  year Year (exact 4 digit year, no year conversions)
783  month Month
784  day Day
785 
786  NOTES: 0000-00-00 is a valid date, and will return 0
787 
788  RETURN
789  Days since 0000-00-00
790 */
791 
792 long calc_daynr(uint32_t year,uint32_t month,uint32_t day)
793 {
794  long delsum;
795  int temp;
796 
797  if (year == 0 && month == 0 && day == 0)
798  return 0; /* Skip errors */
799  delsum= (long) (365L * year+ 31*(month-1) +day);
800  if (month <= 2)
801  year--;
802  else
803  delsum-= (long) (month*4+23)/10;
804  temp=(int) ((year/100+1)*3)/4;
805  return(delsum+(int) year/4-temp);
806 } /* calc_daynr */
807 
808 
809 namespace type {
810 /*
811  Convert time in type::Time representation in system time zone to its
812  time_t form (number of seconds in UTC since begginning of Unix Epoch).
813 
814  SYNOPSIS
815  my_system_gmt_sec()
816  t - time value to be converted
817  my_timezone - pointer to long where offset of system time zone
818  from UTC will be stored for caching
819  in_dst_time_gap - set to true if time falls into spring time-gap
820 
821  NOTES
822  The idea is to cache the time zone offset from UTC (including daylight
823  saving time) for the next call to make things faster. But currently we
824  just calculate this offset during startup (by calling init_time()
825  function) and use it all the time.
826  Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
827  is not allowed).
828 
829  RETURN VALUE
830  Time in UTC seconds since Unix Epoch representation.
831 */
832 void Time::convert(epoch_t &epoch, long *my_timezone) const
833 {
834  int shift= 0;
835  struct tm *l_time,tm_tmp;
836  long diff;
837 
838  /*
839  Use temp variable to avoid trashing input data, which could happen in
840  case of shift required for boundary dates processing.
841  */
842  type::Time tmp_time= *this;
843  type::Time* t= &tmp_time;
844 
845  if (not t->isValidEpoch())
846  {
847  epoch= 0;
848  return;
849  }
850 
851  /*
852  Calculate the gmt time based on current time and timezone
853  The -1 on the end is to ensure that if have a date that exists twice
854  (like 2002-10-27 02:00:0 MET), we will find the initial date.
855 
856  By doing -3600 we will have to call localtime_r() several times, but
857  I couldn't come up with a better way to get a repeatable result :(
858 
859  We can't use mktime() as it's buggy on many platforms and not thread safe.
860 
861  Note: this code assumes that our time_t estimation is not too far away
862  from real value (we assume that localtime_r(epoch) will return something
863  within 24 hrs from t) which is probably true for all current time zones.
864 
865  Note2: For the dates, which have time_t representation close to
866  MAX_INT32 (efficient time_t limit for supported platforms), we should
867  do a small trick to avoid overflow. That is, convert the date, which is
868  two days earlier, and then add these days to the final value.
869 
870  The same trick is done for the values close to 0 in time_t
871  representation for platfroms with unsigned time_t (QNX).
872 
873  To be more verbose, here is a sample (extracted from the code below):
874  (calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
875  would return -2147480896 because of the long type overflow. In result
876  we would get 1901 year in localtime_r(), which is an obvious error.
877 
878  Alike problem raises with the dates close to Epoch. E.g.
879  (calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
880  will give -3600.
881 
882  On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
883  wil give us a date around 2106 year. Which is no good.
884 
885  Theoreticaly, there could be problems with the latter conversion:
886  there are at least two timezones, which had time switches near 1 Jan
887  of 1970 (because of political reasons). These are America/Hermosillo and
888  America/Mazatlan time zones. They changed their offset on
889  1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
890  the code below will give incorrect results for dates close to
891  1970-01-01, in the case OS takes into account these historical switches.
892  Luckily, it seems that we support only one platform with unsigned
893  time_t. It's QNX. And QNX does not support historical timezone data at all.
894  E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
895  historical information for localtime_r() etc. That is, the problem is not
896  relevant to QNX.
897 
898  We are safe with shifts close to MAX_INT32, as there are no known
899  time switches on Jan 2038 yet :)
900  */
901 #ifdef TIME_T_UNSIGNED
902  {
903  /*
904  We can get 0 in time_t representaion only on 1969, 31 of Dec or on
905  1970, 1 of Jan. For both dates we use shift, which is added
906  to t->day in order to step out a bit from the border.
907  This is required for platforms, where time_t is unsigned.
908  As far as I know, among the platforms we support it's only QNX.
909  Note: the order of below if-statements is significant.
910  */
911 
912  if ((t->year == TIMESTAMP_MIN_YEAR + 1) && (t->month == 1)
913  && (t->day <= 10))
914  {
915  t->day+= 2;
916  shift= -2;
917  }
918 
919  if ((t->year == TIMESTAMP_MIN_YEAR) && (t->month == 12)
920  && (t->day == 31))
921  {
922  t->year++;
923  t->month= 1;
924  t->day= 2;
925  shift= -2;
926  }
927  }
928 #endif
929 
930  epoch= (type::epoch_t) (((calc_daynr((uint32_t) t->year, (uint32_t) t->month, (uint32_t) t->day) -
931  (long) days_at_timestart)*86400L + (long) t->hour*3600L +
932  (long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
933  3600);
934 
935  long current_timezone= my_time_zone;
936  util::gmtime(epoch, &tm_tmp);
937  l_time= &tm_tmp;
938  int loop= 0;
939  for (; loop < 2 &&
940  (t->hour != (uint32_t) l_time->tm_hour ||
941  t->minute != (uint32_t) l_time->tm_min ||
942  t->second != (uint32_t) l_time->tm_sec);
943  loop++)
944  { /* One check should be enough ? */
945  /* Get difference in days */
946  int days= t->day - l_time->tm_mday;
947  if (days < -1)
948  days= 1; /* Month has wrapped */
949  else if (days > 1)
950  days= -1;
951  diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
952  (long) (60*((int) t->minute - (int) l_time->tm_min)) +
953  (long) ((int) t->second - (int) l_time->tm_sec));
954  current_timezone+= diff+3600; /* Compensate for -3600 above */
955  epoch+= (time_t) diff;
956  util::gmtime(epoch, &tm_tmp);
957  l_time=&tm_tmp;
958  }
959  /*
960  Fix that if we are in the non existing daylight saving time hour
961  we move the start of the next real hour.
962 
963  This code doesn't handle such exotical thing as time-gaps whose length
964  is more than one hour or non-integer (latter can theoretically happen
965  if one of seconds will be removed due leap correction, or because of
966  general time correction like it happened for Africa/Monrovia time zone
967  in year 1972).
968  */
969  if (loop == 2 && t->hour != (uint32_t) l_time->tm_hour)
970  {
971  int days= t->day - l_time->tm_mday;
972  if (days < -1)
973  days=1; /* Month has wrapped */
974  else if (days > 1)
975  days= -1;
976  diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
977  (long) (60*((int) t->minute - (int) l_time->tm_min)) +
978  (long) ((int) t->second - (int) l_time->tm_sec));
979  if (diff == 3600)
980  epoch+=3600 - t->minute*60 - t->second; /* Move to next hour */
981  else if (diff == -3600)
982  epoch-=t->minute*60 + t->second; /* Move to previous hour */
983  }
984  *my_timezone= current_timezone;
985 
986 
987  /* shift back, if we were dealing with boundary dates */
988  epoch+= shift*86400L;
989 
990  /*
991  This is possible for dates, which slightly exceed boundaries.
992  Conversion will pass ok for them, but we don't allow them.
993  First check will pass for platforms with signed time_t.
994  instruction above (epoch+= shift*86400L) could exceed
995  MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
996  So, epoch < TIMESTAMP_MIN_VALUE will be triggered.
997  */
998  if (epoch < TIMESTAMP_MIN_VALUE)
999  {
1000  epoch= 0;
1001  }
1002 } /* my_system_gmt_sec */
1003 
1004 
1005 void Time::store(const struct tm &from)
1006 {
1007  neg= 0;
1008  second_part= 0;
1009  year= (int32_t) ((from.tm_year+1900) % 10000);
1010  month= (int32_t) from.tm_mon+1;
1011  day= (int32_t) from.tm_mday;
1012  hour= (int32_t) from.tm_hour;
1013  minute= (int32_t) from.tm_min;
1014  second= (int32_t) from.tm_sec;
1015 
1016  time_type= DRIZZLE_TIMESTAMP_DATETIME;
1017 }
1018 
1019 void Time::store(const struct timeval &from)
1020 {
1021  store(from.tv_sec, (usec_t)from.tv_usec);
1022  time_type= type::DRIZZLE_TIMESTAMP_DATETIME;
1023 }
1024 
1025 
1026 void Time::store(type::epoch_t from)
1027 {
1028  store(from, 0);
1029 }
1030 
1031 void Time::store(type::epoch_t from_arg, usec_t from_fractional_seconds)
1032 {
1033  epoch_t from= from_arg;
1034  util::gmtime(from, *this);
1035 
1036  // Since time_t/epoch_t doesn't have fractional seconds, we have to
1037  // collect them outside of the gmtime function.
1038  second_part= from_fractional_seconds;
1039  time_type= DRIZZLE_TIMESTAMP_DATETIME;
1040 }
1041 
1042 // Only implemented for one case, extend as needed.
1043 void Time::truncate(const timestamp_t arg)
1044 {
1045  assert(arg == type::DRIZZLE_TIMESTAMP_TIME);
1046  year= month= day= 0;
1047 
1048  time_type= arg;
1049 }
1050 
1051 void Time::convert(String &str, timestamp_t arg)
1052 {
1053  str.alloc(MAX_STRING_LENGTH);
1054  size_t length= MAX_STRING_LENGTH;
1055 
1056  convert(str.c_ptr(), length, arg);
1057 
1058  str.length(length);
1059  str.set_charset(&my_charset_bin);
1060 }
1061 
1062 void Time::convert(char *str, size_t &to_length, timestamp_t arg)
1063 {
1064  int32_t length= 0;
1065  switch (arg) {
1066  case DRIZZLE_TIMESTAMP_DATETIME:
1067  length= snprintf(str, to_length,
1068  "%04" PRIu32 "-%02" PRIu32 "-%02" PRIu32
1069  " %02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ".%06" PRIu32,
1070  year,
1071  month,
1072  day,
1073  hour,
1074  minute,
1075  second,
1076  second_part);
1077  break;
1078 
1079  case DRIZZLE_TIMESTAMP_DATE:
1080  length= snprintf(str, to_length, "%04u-%02u-%02u",
1081  year,
1082  month,
1083  day);
1084  break;
1085 
1086  case DRIZZLE_TIMESTAMP_TIME:
1087  {
1088  uint32_t extra_hours= 0;
1089 
1090  length= snprintf(str, to_length,
1091  "%s%02u:%02u:%02u",
1092  (neg ? "-" : ""),
1093  extra_hours+ hour,
1094  minute,
1095  second);
1096  }
1097  break;
1098 
1099  case DRIZZLE_TIMESTAMP_NONE:
1100  case DRIZZLE_TIMESTAMP_ERROR:
1101  assert(0);
1102  break;
1103  }
1104 
1105  if (length < 0)
1106  {
1107  to_length= 0;
1108  return;
1109  }
1110 
1111  to_length= length;
1112 }
1113 
1114 }
1115 
1116 /*
1117  Convert datetime value specified as number to broken-down TIME
1118  representation and form value of DATETIME type as side-effect.
1119 
1120  SYNOPSIS
1121  number_to_datetime()
1122  nr - datetime value as number
1123  time_res - pointer for structure for broken-down representation
1124  flags - flags to use in validating date, as in store()
1125  was_cut 0 Value ok
1126  1 If value was cut during conversion
1127  2 check(date,flags) considers date invalid
1128 
1129  DESCRIPTION
1130  Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
1131  YYYYMMDDHHMMSS to broken-down type::Time representation. Return value in
1132  YYYYMMDDHHMMSS format as side-effect.
1133 
1134  This function also checks if datetime value fits in DATETIME range.
1135 
1136  RETURN VALUE
1137  -1 Timestamp with wrong values
1138  anything else DATETIME as integer in YYYYMMDDHHMMSS format
1139  Datetime value in YYYYMMDDHHMMSS format.
1140 */
1141 
1142 static int64_t number_to_datetime(int64_t nr, type::Time *time_res,
1143  uint32_t flags, type::cut_t &was_cut)
1144 {
1145  long part1,part2;
1146 
1147  was_cut= type::VALID;
1148  time_res->reset();
1149  time_res->time_type=type::DRIZZLE_TIMESTAMP_DATE;
1150 
1151  if (nr == 0LL || nr >= 10000101000000LL)
1152  {
1153  time_res->time_type= type::DRIZZLE_TIMESTAMP_DATETIME;
1154  goto ok;
1155  }
1156  if (nr < 101)
1157  goto err;
1158  if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
1159  {
1160  nr= (nr+20000000L)*1000000L; /* YYMMDD, year: 2000-2069 */
1161  goto ok;
1162  }
1163  if (nr < (YY_PART_YEAR)*10000L+101L)
1164  goto err;
1165  if (nr <= 991231L)
1166  {
1167  nr= (nr+19000000L)*1000000L; /* YYMMDD, year: 1970-1999 */
1168  goto ok;
1169  }
1170  if (nr < 10000101L)
1171  goto err;
1172  if (nr <= 99991231L)
1173  {
1174  nr= nr*1000000L;
1175  goto ok;
1176  }
1177  if (nr < 101000000L)
1178  goto err;
1179 
1180  time_res->time_type= type::DRIZZLE_TIMESTAMP_DATETIME;
1181 
1182  if (nr <= (YY_PART_YEAR-1) * 10000000000LL + 1231235959LL)
1183  {
1184  nr= nr + 20000000000000LL; /* YYMMDDHHMMSS, 2000-2069 */
1185  goto ok;
1186  }
1187  if (nr < YY_PART_YEAR * 10000000000LL + 101000000LL)
1188  goto err;
1189  if (nr <= 991231235959LL)
1190  nr= nr + 19000000000000LL; /* YYMMDDHHMMSS, 1970-1999 */
1191 
1192  ok:
1193  part1=(long) (nr / 1000000LL);
1194  part2=(long) (nr - (int64_t) part1 * 1000000LL);
1195  time_res->year= (int) (part1/10000L); part1%=10000L;
1196  time_res->month= (int) part1 / 100;
1197  time_res->day= (int) part1 % 100;
1198  time_res->hour= (int) (part2/10000L); part2%=10000L;
1199  time_res->minute=(int) part2 / 100;
1200  time_res->second=(int) part2 % 100;
1201 
1202  if (time_res->year <= 9999 && time_res->month <= 12 &&
1203  time_res->day <= 31 && time_res->hour <= 23 &&
1204  time_res->minute <= 59 && time_res->second <= 59 &&
1205  not time_res->check((nr != 0), flags, was_cut))
1206  {
1207  return nr;
1208  }
1209 
1210  /* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */
1211  if (!nr && (flags & TIME_NO_ZERO_DATE))
1212  return -1LL;
1213 
1214  err:
1215  was_cut= type::CUT;
1216  return -1LL;
1217 }
1218 
1219 
1220 namespace type {
1221 
1222 void Time::convert(datetime_t &ret, int64_t nr, uint32_t flags)
1223 {
1224  type::cut_t was_cut;
1225  ret= number_to_datetime(nr, this, flags, was_cut);
1226 }
1227 
1228 void Time::convert(datetime_t &ret, int64_t nr, uint32_t flags, type::cut_t &was_cut)
1229 {
1230  ret= number_to_datetime(nr, this, flags, was_cut);
1231 }
1232 
1233 /*
1234  Convert struct type::Time (date and time split into year/month/day/hour/...
1235  to a number in format YYYYMMDDHHMMSS (DATETIME),
1236  YYYYMMDD (DATE) or HHMMSS (TIME).
1237 */
1238 
1239 
1240 void Time::convert(datetime_t &datetime, timestamp_t arg)
1241 {
1242  switch (arg)
1243  {
1244  // Convert to YYYYMMDDHHMMSS format
1245  case type::DRIZZLE_TIMESTAMP_DATETIME:
1246  datetime= ((int64_t) (year * 10000UL + month * 100UL + day) * 1000000ULL +
1247  (int64_t) (hour * 10000UL + minute * 100UL + second));
1248  break;
1249 
1250  // Convert to YYYYMMDD
1251  case type::DRIZZLE_TIMESTAMP_DATE:
1252  datetime= (year * 10000UL + month * 100UL + day);
1253  break;
1254 
1255  // Convert to HHMMSS
1256  case type::DRIZZLE_TIMESTAMP_TIME:
1257  datetime= (hour * 10000UL + minute * 100UL + second);
1258  break;
1259 
1260  case type::DRIZZLE_TIMESTAMP_NONE:
1261  case type::DRIZZLE_TIMESTAMP_ERROR:
1262  datetime= 0;
1263  }
1264 }
1265 
1266 } // namespace type
1267 
1268 } /* namespace drizzled */
static const uint32_t * days_in_month(uint32_t y, enum calendar c)
Definition: calendar.cc:56