~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to libdrizzle/my_time.c

  • Committer: Brian Aker
  • Date: 2010-12-18 18:24:57 UTC
  • mfrom: (1999.6.3 trunk)
  • Revision ID: brian@tangent.org-20101218182457-yi1wd0so2hml1k1w
Merge in Lee's copyright header fix

Show diffs side-by-side

added added

removed removed

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