~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to mysys/my_time.c

  • Committer: Brian Aker
  • Date: 2008-10-06 06:47:29 UTC
  • Revision ID: brian@tangent.org-20081006064729-2i9mhjkzyvow9xsm
Remove uint.

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 unsigned char internal_format_positions[]=
 
34
{0, 1, 2, 3, 4, 5, 6, (unsigned char) 255};
 
35
 
 
36
static char time_separator=':';
 
37
 
 
38
static uint32_t const days_at_timestart=719528; /* daynr at 1970.01.01 */
 
39
unsigned char 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
uint32_t calc_days_in_year(uint32_t 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, uint32_t length, DRIZZLE_TIME *l_time,
 
160
                uint32_t flags, int *was_cut)
 
161
{
 
162
  uint32_t field_length, year_length=4, digits, i, number_of_fields;
 
163
  uint32_t date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
 
164
  uint32_t 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 unsigned char *format_position;
 
170
  bool found_delimitier= 0, found_space= 0;
 
171
  uint32_t frac_pos, frac_len;
 
172
 
 
173
  *was_cut= 0;
 
174
 
 
175
  /* Skip space at start */
 
176
  for (; str != end && my_isspace(&my_charset_utf8_general_ci, *str) ; str++)
 
177
    ;
 
178
  if (str == end || ! my_isdigit(&my_charset_utf8_general_ci, *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_utf8_general_ci,*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_utf8_general_ci, *pos))
 
220
        pos++;
 
221
      while (pos < end && !my_isdigit(&my_charset_utf8_general_ci, *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= cmax((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_utf8_general_ci,*str);
 
256
       i++)
 
257
  {
 
258
    const char *start= str;
 
259
    uint32_t tmp_value= (uint) (unsigned char) (*str++ - '0');
 
260
    while (str != end && my_isdigit(&my_charset_utf8_general_ci,str[0]) &&
 
261
           (!is_internal_format || --field_length))
 
262
    {
 
263
      tmp_value=tmp_value*10 + (uint32_t) (unsigned char) (*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_utf8_general_ci,*str) ||
 
300
            my_isspace(&my_charset_utf8_general_ci,*str)))
 
301
    {
 
302
      if (my_isspace(&my_charset_utf8_general_ci,*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_utf8_general_ci,*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] != (unsigned char) 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_utf8_general_ci, *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_utf8_general_ci,*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, uint32_t 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
  uint32_t state;
 
478
 
 
479
  l_time->neg=0;
 
480
  *warning= 0;
 
481
  for (; str != end && my_isspace(&my_charset_utf8_general_ci,*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_utf8_general_ci,*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_utf8_general_ci, 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_utf8_general_ci, *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_utf8_general_ci, 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_utf8_general_ci,*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_utf8_general_ci,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((unsigned char*) (date+4), (unsigned char*) (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_utf8_general_ci,str[1]))
 
572
  {
 
573
    int field_length= 5;
 
574
    str++; value=(uint) (unsigned char) (*str - '0');
 
575
    while (++str != end && my_isdigit(&my_charset_utf8_general_ci, *str))
 
576
    {
 
577
      if (field_length-- > 0)
 
578
        value= value*10 + (uint) (unsigned char) (*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_utf8_general_ci, str[1]) ||
 
594
       ((str[1] == '-' || str[1] == '+') &&
 
595
        (end - str) > 2 &&
 
596
        my_isdigit(&my_charset_utf8_general_ci, 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_utf8_general_ci, *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_utf8_general_ci,*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
uint32_t year_2000_handling(uint32_t 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(uint32_t year,uint32_t month,uint32_t 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
  uint32_t 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
  uint32_t 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
                            uint32_t 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