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