~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to mysys/my_time.cc

  • Committer: Brian Aker
  • Date: 2009-01-08 22:38:15 UTC
  • Revision ID: brian@tangent.org-20090108223815-i7p6uaajqxapge8a
func_timestamp now working.

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