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