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