~drizzle-trunk/drizzle/development

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