~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to mysys/my_time.cc

Merge Stewart's dead code removal

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