~ubuntu-branches/ubuntu/precise/mysql-5.1/precise

« back to all changes in this revision

Viewing changes to sql-common/my_time.c

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Tretkowski
  • Date: 2010-03-17 14:56:02 UTC
  • Revision ID: james.westby@ubuntu.com-20100317145602-x7e30l1b2sb5s6w6
Tags: upstream-5.1.45
ImportĀ upstreamĀ versionĀ 5.1.45

Show diffs side-by-side

added added

removed removed

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