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