~ubuntu-branches/ubuntu/trusty/mysql-5.6/trusty

« back to all changes in this revision

Viewing changes to sql-common/my_time.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-02-12 11:54:27 UTC
  • Revision ID: package-import@ubuntu.com-20140212115427-oq6tfsqxl1wuwehi
Tags: upstream-5.6.15
ImportĀ upstreamĀ versionĀ 5.6.15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2004, 2012, Oracle and/or its affiliates. All rights reserved.
 
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
 
15
 
 
16
#include <my_time.h>
 
17
#include <m_string.h>
 
18
#include <m_ctype.h>
 
19
#include <myisampack.h>
 
20
/* Windows version of localtime_r() is declared in my_ptrhead.h */
 
21
#include <my_pthread.h>
 
22
 
 
23
ulonglong log_10_int[20]=
 
24
{
 
25
  1, 10, 100, 1000, 10000UL, 100000UL, 1000000UL, 10000000UL,
 
26
  ULL(100000000), ULL(1000000000), ULL(10000000000), ULL(100000000000),
 
27
  ULL(1000000000000), ULL(10000000000000), ULL(100000000000000),
 
28
  ULL(1000000000000000), ULL(10000000000000000), ULL(100000000000000000),
 
29
  ULL(1000000000000000000), ULL(10000000000000000000)
 
30
};
 
31
 
 
32
 
 
33
const char my_zero_datetime6[]= "0000-00-00 00:00:00.000000";
 
34
 
 
35
 
 
36
/* Position for YYYY-DD-MM HH-MM-DD.FFFFFF AM in default format */
 
37
 
 
38
static uchar internal_format_positions[]=
 
39
{0, 1, 2, 3, 4, 5, 6, (uchar) 255};
 
40
 
 
41
static char time_separator=':';
 
42
 
 
43
static ulong const days_at_timestart=719528;    /* daynr at 1970.01.01 */
 
44
uchar days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
 
45
 
 
46
/*
 
47
  Offset of system time zone from UTC in seconds used to speed up 
 
48
  work of my_system_gmt_sec() function.
 
49
*/
 
50
static long my_time_zone=0;
 
51
 
 
52
 
 
53
/* Calc days in one year. works with 0 <= year <= 99 */
 
54
 
 
55
uint calc_days_in_year(uint year)
 
56
{
 
57
  return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
 
58
          366 : 365);
 
59
}
 
60
 
 
61
 
 
62
/**
 
63
   Set MYSQL_TIME structure to 0000-00-00 00:00:00.000000
 
64
   @param tm[OUT]    The value to set.
 
65
   @param time_type  Timestasmp type
 
66
*/
 
67
inline void set_zero_time(MYSQL_TIME *tm,
 
68
                          enum enum_mysql_timestamp_type time_type)
 
69
{
 
70
  memset(tm, 0, sizeof(*tm));
 
71
  tm->time_type= time_type;
 
72
}
 
73
 
 
74
 
 
75
/**
 
76
  Set hour, minute and second of a MYSQL_TIME variable to maximum time value.
 
77
  Unlike set_max_time(), does not touch the other structure members.
 
78
*/
 
79
inline void set_max_hhmmss(MYSQL_TIME *tm)
 
80
{
 
81
  tm->hour= TIME_MAX_HOUR;
 
82
  tm->minute= TIME_MAX_MINUTE;
 
83
  tm->second= TIME_MAX_SECOND;
 
84
}
 
85
 
 
86
 
 
87
/**
 
88
  Set MYSQL_TIME variable to maximum time value
 
89
  @param tm    OUT  The variable to set.
 
90
  @param neg        Sign: 1 if negative, 0 if positive.
 
91
*/
 
92
void set_max_time(MYSQL_TIME *tm, my_bool neg)
 
93
{
 
94
  set_zero_time(tm, MYSQL_TIMESTAMP_TIME);
 
95
  set_max_hhmmss(tm);
 
96
  tm->neg= neg;
 
97
}
 
98
 
 
99
 
 
100
/**
 
101
  @brief Check datetime value for validity according to flags.
 
102
 
 
103
  @param[in]  ltime          Date to check.
 
104
  @param[in]  not_zero_date  ltime is not the zero date
 
105
  @param[in]  flags          flags to check
 
106
                             (see str_to_datetime() flags in my_time.h)
 
107
  @param[out] was_cut        set to 2 if value was invalid according to flags.
 
108
                             (Feb 29 in non-leap etc.)  This remains unchanged
 
109
                             if value is not invalid.
 
110
  @details Here we assume that year and month is ok!
 
111
    If month is 0 we allow any date. (This only happens if we allow zero
 
112
    date parts in str_to_datetime())
 
113
    Disallow dates with zero year and non-zero month and/or day.
 
114
 
 
115
  @return
 
116
    0  OK
 
117
    1  error
 
118
*/
 
119
 
 
120
my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date,
 
121
                   ulonglong flags, int *was_cut)
 
122
{
 
123
  if (not_zero_date)
 
124
  {
 
125
    if (((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) &&
 
126
        (ltime->month == 0 || ltime->day == 0))
 
127
    {
 
128
      *was_cut= MYSQL_TIME_WARN_ZERO_IN_DATE;
 
129
      return TRUE;
 
130
    }
 
131
    else if ((!(flags & TIME_INVALID_DATES) &&
 
132
              ltime->month && ltime->day > days_in_month[ltime->month-1] &&
 
133
              (ltime->month != 2 || calc_days_in_year(ltime->year) != 366 ||
 
134
               ltime->day != 29)))
 
135
    {
 
136
      *was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
 
137
      return TRUE;
 
138
    }
 
139
  }
 
140
  else if (flags & TIME_NO_ZERO_DATE)
 
141
  {
 
142
    *was_cut= MYSQL_TIME_WARN_ZERO_DATE;
 
143
    return TRUE;
 
144
  }
 
145
  return FALSE;
 
146
}
 
147
 
 
148
 
 
149
/**
 
150
  Check if TIME fields are fatally bad and cannot be further adjusted.
 
151
  @param ltime  Time value.
 
152
  @retval  TRUE   if the value is fatally bad.
 
153
  @retval  FALSE  if the value is Ok.
 
154
*/
 
155
inline my_bool check_time_mmssff_range(const MYSQL_TIME *ltime)
 
156
{
 
157
  return ltime->minute >= 60 || ltime->second >= 60 ||
 
158
         ltime->second_part > 999999;
 
159
}
 
160
 
 
161
 
 
162
/**
 
163
  Check TIME range. The value can include day part,
 
164
  for example:  '1 10:20:30.123456'.
 
165
 
 
166
  minute, second and second_part values are not checked
 
167
  unless hour is equal TIME_MAX_HOUR.
 
168
 
 
169
  @param ltime   Rime value.
 
170
  @returns       Test result.
 
171
  @retval        FALSE if value is Ok.
 
172
  @retval        TRUE if value is out of range. 
 
173
*/
 
174
inline my_bool check_time_range_quick(const MYSQL_TIME *ltime)
 
175
{
 
176
  longlong hour= (longlong) ltime->hour + 24LL * ltime->day;
 
177
  /* The input value should not be fatally bad */
 
178
  DBUG_ASSERT(!check_time_mmssff_range(ltime));
 
179
  if (hour <= TIME_MAX_HOUR &&
 
180
      (hour != TIME_MAX_HOUR || ltime->minute != TIME_MAX_MINUTE ||
 
181
       ltime->second != TIME_MAX_SECOND || !ltime->second_part))
 
182
    return FALSE;
 
183
  return TRUE;
 
184
}
 
185
 
 
186
 
 
187
/**
 
188
  Check datetime, date, or normalized time (i.e. time without days) range.
 
189
  @param ltime   Datetime value.
 
190
  @returns
 
191
  @retval   FALSE on success
 
192
  @retval   TRUE  on error
 
193
*/
 
194
my_bool check_datetime_range(const MYSQL_TIME *ltime)
 
195
{
 
196
  /*
 
197
    In case of MYSQL_TIMESTAMP_TIME hour value can be up to TIME_MAX_HOUR.
 
198
    In case of MYSQL_TIMESTAMP_DATETIME it cannot be bigger than 23.
 
199
  */
 
200
  return
 
201
    ltime->year > 9999 || ltime->month > 12  || ltime->day > 31 || 
 
202
    ltime->minute > 59 || ltime->second > 59 || ltime->second_part > 999999 ||
 
203
    (ltime->hour >
 
204
     (ltime->time_type == MYSQL_TIMESTAMP_TIME ? TIME_MAX_HOUR : 23));
 
205
}
 
206
 
 
207
 
 
208
/*
 
209
  Convert a timestamp string to a MYSQL_TIME value.
 
210
 
 
211
  SYNOPSIS
 
212
    str_to_datetime()
 
213
    str                 String to parse
 
214
    length              Length of string
 
215
    l_time              Date is stored here
 
216
    flags               Bitmap of following items
 
217
                        TIME_FUZZY_DATE    Set if we should allow partial dates
 
218
                        TIME_DATETIME_ONLY Set if we only allow full datetimes.
 
219
                        TIME_NO_ZERO_IN_DATE    Don't allow partial dates
 
220
                        TIME_NO_ZERO_DATE       Don't allow 0000-00-00 date
 
221
                        TIME_INVALID_DATES      Allow 2000-02-31
 
222
    status              Conversion status
 
223
 
 
224
 
 
225
  DESCRIPTION
 
226
    At least the following formats are recogniced (based on number of digits)
 
227
    YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
 
228
    YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
 
229
    YYYYMMDDTHHMMSS  where T is a the character T (ISO8601)
 
230
    Also dates where all parts are zero are allowed
 
231
 
 
232
    The second part may have an optional .###### fraction part.
 
233
 
 
234
  NOTES
 
235
   This function should work with a format position vector as long as the
 
236
   following things holds:
 
237
   - All date are kept together and all time parts are kept together
 
238
   - Date and time parts must be separated by blank
 
239
   - Second fractions must come after second part and be separated
 
240
     by a '.'.  (The second fractions are optional)
 
241
   - AM/PM must come after second fractions (or after seconds if no fractions)
 
242
   - Year must always been specified.
 
243
   - If time is before date, then we will use datetime format only if
 
244
     the argument consist of two parts, separated by space.
 
245
     Otherwise we will assume the argument is a date.
 
246
   - The hour part must be specified in hour-minute-second order.
 
247
 
 
248
    status->warnings is set to:
 
249
    0                            Value OK
 
250
    MYSQL_TIME_WARN_TRUNCATED    If value was cut during conversion
 
251
    MYSQL_TIME_WARN_OUT_OF_RANGE check_date(date,flags) considers date invalid
 
252
 
 
253
    l_time->time_type is set as follows:
 
254
    MYSQL_TIMESTAMP_NONE        String wasn't a timestamp, like
 
255
                                [DD [HH:[MM:[SS]]]].fraction.
 
256
                                l_time is not changed.
 
257
    MYSQL_TIMESTAMP_DATE        DATE string (YY MM and DD parts ok)
 
258
    MYSQL_TIMESTAMP_DATETIME    Full timestamp
 
259
    MYSQL_TIMESTAMP_ERROR       Timestamp with wrong values.
 
260
                                All elements in l_time is set to 0
 
261
  RETURN VALUES
 
262
    0 - Ok
 
263
    1 - Error
 
264
*/
 
265
 
 
266
#define MAX_DATE_PARTS 8
 
267
 
 
268
my_bool
 
269
str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
 
270
                ulonglong flags, MYSQL_TIME_STATUS *status)
 
271
{
 
272
  uint field_length, UNINIT_VAR(year_length), digits, i, number_of_fields;
 
273
  uint date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
 
274
  uint add_hours= 0, start_loop;
 
275
  ulong not_zero_date, allow_space;
 
276
  my_bool is_internal_format;
 
277
  const char *pos, *UNINIT_VAR(last_field_pos);
 
278
  const char *end=str+length;
 
279
  const uchar *format_position;
 
280
  my_bool found_delimitier= 0, found_space= 0;
 
281
  uint frac_pos, frac_len;
 
282
  DBUG_ENTER("str_to_datetime");
 
283
  DBUG_PRINT("ENTER",("str: %.*s",length,str));
 
284
 
 
285
  LINT_INIT(field_length);
 
286
 
 
287
  my_time_status_init(status);
 
288
 
 
289
  /* Skip space at start */
 
290
  for (; str != end && my_isspace(&my_charset_latin1, *str) ; str++)
 
291
    ;
 
292
  if (str == end || ! my_isdigit(&my_charset_latin1, *str))
 
293
  {
 
294
    status->warnings= MYSQL_TIME_WARN_TRUNCATED;
 
295
    l_time->time_type= MYSQL_TIMESTAMP_NONE;
 
296
    DBUG_RETURN(1);
 
297
  }
 
298
 
 
299
  is_internal_format= 0;
 
300
  /* This has to be changed if want to activate different timestamp formats */
 
301
  format_position= internal_format_positions;
 
302
 
 
303
  /*
 
304
    Calculate number of digits in first part.
 
305
    If length= 8 or >= 14 then year is of format YYYY.
 
306
    (YYYY-MM-DD,  YYYYMMDD, YYYYYMMDDHHMMSS)
 
307
  */
 
308
  for (pos=str;
 
309
       pos != end && (my_isdigit(&my_charset_latin1,*pos) || *pos == 'T');
 
310
       pos++)
 
311
    ;
 
312
 
 
313
  digits= (uint) (pos-str);
 
314
  start_loop= 0;                                /* Start of scan loop */
 
315
  date_len[format_position[0]]= 0;              /* Length of year field */
 
316
  if (pos == end || *pos == '.')
 
317
  {
 
318
    /* Found date in internal format (only numbers like YYYYMMDD) */
 
319
    year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
 
320
    field_length= year_length;
 
321
    is_internal_format= 1;
 
322
    format_position= internal_format_positions;
 
323
  }
 
324
  else
 
325
  {
 
326
    if (format_position[0] >= 3)                /* If year is after HHMMDD */
 
327
    {
 
328
      /*
 
329
        If year is not in first part then we have to determinate if we got
 
330
        a date field or a datetime field.
 
331
        We do this by checking if there is two numbers separated by
 
332
        space in the input.
 
333
      */
 
334
      while (pos < end && !my_isspace(&my_charset_latin1, *pos))
 
335
        pos++;
 
336
      while (pos < end && !my_isdigit(&my_charset_latin1, *pos))
 
337
        pos++;
 
338
      if (pos == end)
 
339
      {
 
340
        if (flags & TIME_DATETIME_ONLY)
 
341
        {
 
342
          status->warnings= MYSQL_TIME_WARN_TRUNCATED;
 
343
          l_time->time_type= MYSQL_TIMESTAMP_NONE;
 
344
          DBUG_RETURN(1); /* Can't be a full datetime */
 
345
        }
 
346
        /* Date field.  Set hour, minutes and seconds to 0 */
 
347
        date[0]= date[1]= date[2]= date[3]= date[4]= 0;
 
348
        start_loop= 5;                         /* Start with first date part */
 
349
      }
 
350
    }
 
351
 
 
352
    field_length= format_position[0] == 0 ? 4 : 2;
 
353
  }
 
354
 
 
355
  /*
 
356
    Only allow space in the first "part" of the datetime field and:
 
357
    - after days, part seconds
 
358
    - before and after AM/PM (handled by code later)
 
359
 
 
360
    2003-03-03 20:00:20 AM
 
361
    20:00:20.000000 AM 03-03-2000
 
362
  */
 
363
  i= MY_MAX((uint) format_position[0], (uint) format_position[1]);
 
364
  set_if_bigger(i, (uint) format_position[2]);
 
365
  allow_space= ((1 << i) | (1 << format_position[6]));
 
366
  allow_space&= (1 | 2 | 4 | 8 | 64);
 
367
 
 
368
  not_zero_date= 0;
 
369
  for (i = start_loop;
 
370
       i < MAX_DATE_PARTS-1 && str != end &&
 
371
         my_isdigit(&my_charset_latin1,*str);
 
372
       i++)
 
373
  {
 
374
    const char *start= str;
 
375
    ulong tmp_value= (uint) (uchar) (*str++ - '0');
 
376
 
 
377
    /*
 
378
      Internal format means no delimiters; every field has a fixed
 
379
      width. Otherwise, we scan until we find a delimiter and discard
 
380
      leading zeroes -- except for the microsecond part, where leading
 
381
      zeroes are significant, and where we never process more than six
 
382
      digits.
 
383
    */
 
384
    my_bool     scan_until_delim= !is_internal_format &&
 
385
                                  ((i != format_position[6]));
 
386
 
 
387
    while (str != end && my_isdigit(&my_charset_latin1,str[0]) &&
 
388
           (scan_until_delim || --field_length))
 
389
    {
 
390
      tmp_value=tmp_value*10 + (ulong) (uchar) (*str - '0');
 
391
      str++;
 
392
    }
 
393
    date_len[i]= (uint) (str - start);
 
394
    if (tmp_value > 999999)                     /* Impossible date part */
 
395
    {
 
396
      status->warnings= MYSQL_TIME_WARN_TRUNCATED;
 
397
      l_time->time_type= MYSQL_TIMESTAMP_NONE;
 
398
      DBUG_RETURN(1);
 
399
    }
 
400
    date[i]=tmp_value;
 
401
    not_zero_date|= tmp_value;
 
402
 
 
403
    /* Length of next field */
 
404
    field_length= format_position[i+1] == 0 ? 4 : 2;
 
405
 
 
406
    if ((last_field_pos= str) == end)
 
407
    {
 
408
      i++;                                      /* Register last found part */
 
409
      break;
 
410
    }
 
411
    /* Allow a 'T' after day to allow CCYYMMDDT type of fields */
 
412
    if (i == format_position[2] && *str == 'T')
 
413
    {
 
414
      str++;                                    /* ISO8601:  CCYYMMDDThhmmss */
 
415
      continue;
 
416
    }
 
417
    if (i == format_position[5])                /* Seconds */
 
418
    {
 
419
      if (*str == '.')                          /* Followed by part seconds */
 
420
      {
 
421
        str++;
 
422
        /*
 
423
          Shift last_field_pos, so '2001-01-01 00:00:00.'
 
424
          is treated as a valid value
 
425
        */
 
426
        last_field_pos= str;
 
427
        field_length= 6;                        /* 6 digits */
 
428
      }
 
429
      continue;
 
430
    }
 
431
    while (str != end &&
 
432
           (my_ispunct(&my_charset_latin1,*str) ||
 
433
            my_isspace(&my_charset_latin1,*str)))
 
434
    {
 
435
      if (my_isspace(&my_charset_latin1,*str))
 
436
      {
 
437
        if (!(allow_space & (1 << i)))
 
438
        {
 
439
          status->warnings= MYSQL_TIME_WARN_TRUNCATED;
 
440
          l_time->time_type= MYSQL_TIMESTAMP_NONE;
 
441
          DBUG_RETURN(1);
 
442
        }
 
443
        found_space= 1;
 
444
      }
 
445
      str++;
 
446
      found_delimitier= 1;                      /* Should be a 'normal' date */
 
447
    }
 
448
    /* Check if next position is AM/PM */
 
449
    if (i == format_position[6])                /* Seconds, time for AM/PM */
 
450
    {
 
451
      i++;                                      /* Skip AM/PM part */
 
452
      if (format_position[7] != 255)            /* If using AM/PM */
 
453
      {
 
454
        if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
 
455
        {
 
456
          if (str[0] == 'p' || str[0] == 'P')
 
457
            add_hours= 12;
 
458
          else if (str[0] != 'a' || str[0] != 'A')
 
459
            continue;                           /* Not AM/PM */
 
460
          str+= 2;                              /* Skip AM/PM */
 
461
          /* Skip space after AM/PM */
 
462
          while (str != end && my_isspace(&my_charset_latin1,*str))
 
463
            str++;
 
464
        }
 
465
      }
 
466
    }
 
467
    last_field_pos= str;
 
468
  }
 
469
  if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
 
470
  {
 
471
    status->warnings= MYSQL_TIME_WARN_TRUNCATED;
 
472
    l_time->time_type= MYSQL_TIMESTAMP_NONE;
 
473
    DBUG_RETURN(1);  /* Can't be a datetime */
 
474
  }
 
475
 
 
476
  str= last_field_pos;
 
477
 
 
478
  number_of_fields= i - start_loop;
 
479
  while (i < MAX_DATE_PARTS)
 
480
  {
 
481
    date_len[i]= 0;
 
482
    date[i++]= 0;
 
483
  }
 
484
 
 
485
  if (!is_internal_format)
 
486
  {
 
487
    year_length= date_len[(uint) format_position[0]];
 
488
    if (!year_length)                           /* Year must be specified */
 
489
    {
 
490
      status->warnings= MYSQL_TIME_WARN_TRUNCATED;
 
491
      l_time->time_type= MYSQL_TIMESTAMP_NONE;
 
492
      DBUG_RETURN(1);
 
493
    }
 
494
 
 
495
    l_time->year=               date[(uint) format_position[0]];
 
496
    l_time->month=              date[(uint) format_position[1]];
 
497
    l_time->day=                date[(uint) format_position[2]];
 
498
    l_time->hour=               date[(uint) format_position[3]];
 
499
    l_time->minute=             date[(uint) format_position[4]];
 
500
    l_time->second=             date[(uint) format_position[5]];
 
501
 
 
502
    frac_pos= (uint) format_position[6];
 
503
    frac_len= date_len[frac_pos];
 
504
    status->fractional_digits= frac_len;
 
505
    if (frac_len < 6)
 
506
      date[frac_pos]*= (uint) log_10_int[DATETIME_MAX_DECIMALS - frac_len];
 
507
    l_time->second_part= date[frac_pos];
 
508
 
 
509
    if (format_position[7] != (uchar) 255)
 
510
    {
 
511
      if (l_time->hour > 12)
 
512
      {
 
513
        status->warnings= MYSQL_TIME_WARN_TRUNCATED;
 
514
        goto err;
 
515
      }
 
516
      l_time->hour= l_time->hour%12 + add_hours;
 
517
    }
 
518
  }
 
519
  else
 
520
  {
 
521
    l_time->year=       date[0];
 
522
    l_time->month=      date[1];
 
523
    l_time->day=        date[2];
 
524
    l_time->hour=       date[3];
 
525
    l_time->minute=     date[4];
 
526
    l_time->second=     date[5];
 
527
    if (date_len[6] < 6)
 
528
      date[6]*= (uint) log_10_int[DATETIME_MAX_DECIMALS - date_len[6]];
 
529
    l_time->second_part=date[6];
 
530
    status->fractional_digits= date_len[6];
 
531
  }
 
532
  l_time->neg= 0;
 
533
 
 
534
  if (year_length == 2 && not_zero_date)
 
535
    l_time->year+= (l_time->year < YY_PART_YEAR ? 2000 : 1900);
 
536
 
 
537
  /*
 
538
    Set time_type before check_datetime_range(),
 
539
    as the latter relies on initialized time_type value.
 
540
  */
 
541
  l_time->time_type= (number_of_fields <= 3 ?
 
542
                      MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME);
 
543
 
 
544
  if (number_of_fields < 3 || check_datetime_range(l_time))
 
545
  {
 
546
    /* Only give warning for a zero date if there is some garbage after */
 
547
    if (!not_zero_date)                         /* If zero date */
 
548
    {
 
549
      for (; str != end ; str++)
 
550
      {
 
551
        if (!my_isspace(&my_charset_latin1, *str))
 
552
        {
 
553
          not_zero_date= 1;                     /* Give warning */
 
554
          break;
 
555
        }
 
556
      }
 
557
    }
 
558
    status->warnings|= not_zero_date ? MYSQL_TIME_WARN_TRUNCATED :
 
559
                                       MYSQL_TIME_WARN_ZERO_DATE;
 
560
    goto err;
 
561
  }
 
562
 
 
563
  if (check_date(l_time, not_zero_date != 0, flags, &status->warnings))
 
564
    goto err;
 
565
 
 
566
  /* Scan all digits left after microseconds */
 
567
  if (status->fractional_digits == 6 && str != end)
 
568
  {
 
569
    if (my_isdigit(&my_charset_latin1, *str))
 
570
    {
 
571
      /*
 
572
        We don't need the exact nanoseconds value.
 
573
        Knowing the first digit is enough for rounding.
 
574
      */
 
575
      status->nanoseconds= 100 * (int) (*str++ - '0');
 
576
      for (; str != end && my_isdigit(&my_charset_latin1, *str); str++)
 
577
      { }
 
578
    }
 
579
  }
 
580
 
 
581
  for (; str != end ; str++)
 
582
  {
 
583
    if (!my_isspace(&my_charset_latin1,*str))
 
584
    {
 
585
      status->warnings= MYSQL_TIME_WARN_TRUNCATED;
 
586
      break;
 
587
    }
 
588
  }
 
589
 
 
590
  DBUG_RETURN(0);
 
591
 
 
592
err:
 
593
  set_zero_time(l_time, MYSQL_TIMESTAMP_ERROR);
 
594
  DBUG_RETURN(1);
 
595
}
 
596
 
 
597
 
 
598
/*
 
599
 Convert a time string to a MYSQL_TIME struct.
 
600
 
 
601
  SYNOPSIS
 
602
   str_to_time()
 
603
   str                  A string in full TIMESTAMP format or
 
604
                        [-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS,
 
605
                        [M]MSS or [S]S
 
606
                        There may be an optional [.second_part] after seconds
 
607
   length               Length of str
 
608
   l_time               Store result here
 
609
   status               Conversion status
 
610
 
 
611
   status.warning is set to:
 
612
     MYSQL_TIME_WARN_TRUNCATED flag if the input string
 
613
                        was cut during conversion, and/or
 
614
     MYSQL_TIME_WARN_OUT_OF_RANGE flag, if the value is out of range.
 
615
 
 
616
   NOTES
 
617
     Because of the extra days argument, this function can only
 
618
     work with times where the time arguments are in the above order.
 
619
 
 
620
   RETURN
 
621
     0  ok
 
622
     1  error
 
623
*/
 
624
 
 
625
my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
 
626
                    MYSQL_TIME_STATUS *status)
 
627
{
 
628
  ulong date[5];
 
629
  ulonglong value;
 
630
  const char *end=str+length, *end_of_days;
 
631
  my_bool found_days,found_hours;
 
632
  uint state;
 
633
 
 
634
  my_time_status_init(status);
 
635
  l_time->neg=0;
 
636
  for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++)
 
637
    length--;
 
638
  if (str != end && *str == '-')
 
639
  {
 
640
    l_time->neg=1;
 
641
    str++;
 
642
    length--;
 
643
  }
 
644
  if (str == end)
 
645
    return 1;
 
646
 
 
647
  /* Check first if this is a full TIMESTAMP */
 
648
  if (length >= 12)
 
649
  {                                             /* Probably full timestamp */
 
650
    (void) str_to_datetime(str, length, l_time,
 
651
                           (TIME_FUZZY_DATE | TIME_DATETIME_ONLY), status);
 
652
    if (l_time->time_type >= MYSQL_TIMESTAMP_ERROR)
 
653
      return l_time->time_type == MYSQL_TIMESTAMP_ERROR;
 
654
    my_time_status_init(status);
 
655
  }
 
656
 
 
657
  /* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
 
658
  for (value=0; str != end && my_isdigit(&my_charset_latin1,*str) ; str++)
 
659
    value=value*10L + (long) (*str - '0');
 
660
 
 
661
  if (value > UINT_MAX)
 
662
    return 1;
 
663
 
 
664
  /* Skip all space after 'days' */
 
665
  end_of_days= str;
 
666
  for (; str != end && my_isspace(&my_charset_latin1, str[0]) ; str++)
 
667
    ;
 
668
 
 
669
  LINT_INIT(state);
 
670
  found_days=found_hours=0;
 
671
  if ((uint) (end-str) > 1 && str != end_of_days &&
 
672
      my_isdigit(&my_charset_latin1, *str))
 
673
  {                                             /* Found days part */
 
674
    date[0]= (ulong) value;
 
675
    state= 1;                                   /* Assume next is hours */
 
676
    found_days= 1;
 
677
  }
 
678
  else if ((end-str) > 1 &&  *str == time_separator &&
 
679
           my_isdigit(&my_charset_latin1, str[1]))
 
680
  {
 
681
    date[0]= 0;                                 /* Assume we found hours */
 
682
    date[1]= (ulong) value;
 
683
    state=2;
 
684
    found_hours=1;
 
685
    str++;                                      /* skip ':' */
 
686
  }
 
687
  else
 
688
  {
 
689
    /* String given as one number; assume HHMMSS format */
 
690
    date[0]= 0;
 
691
    date[1]= (ulong) (value/10000);
 
692
    date[2]= (ulong) (value/100 % 100);
 
693
    date[3]= (ulong) (value % 100);
 
694
    state=4;
 
695
    goto fractional;
 
696
  }
 
697
 
 
698
  /* Read hours, minutes and seconds */
 
699
  for (;;)
 
700
  {
 
701
    for (value=0; str != end && my_isdigit(&my_charset_latin1,*str) ; str++)
 
702
      value=value*10L + (long) (*str - '0');
 
703
    date[state++]= (ulong) value;
 
704
    if (state == 4 || (end-str) < 2 || *str != time_separator ||
 
705
        !my_isdigit(&my_charset_latin1,str[1]))
 
706
      break;
 
707
    str++;                                      /* Skip time_separator (':') */
 
708
  }
 
709
 
 
710
  if (state != 4)
 
711
  {                                             /* Not HH:MM:SS */
 
712
    /* Fix the date to assume that seconds was given */
 
713
    if (!found_hours && !found_days)
 
714
    {
 
715
      bmove_upp((uchar*) (date+4), (uchar*) (date+state),
 
716
                sizeof(long)*(state-1));
 
717
      memset(date, 0, sizeof(long)*(4-state));
 
718
    }
 
719
    else
 
720
      memset((date+state), 0, sizeof(long)*(4-state));
 
721
  }
 
722
 
 
723
fractional:
 
724
  /* Get fractional second part */
 
725
  if ((end-str) >= 2 && *str == '.' && my_isdigit(&my_charset_latin1,str[1]))
 
726
  {
 
727
    int field_length= 5;
 
728
    str++; value=(uint) (uchar) (*str - '0');
 
729
    while (++str != end && my_isdigit(&my_charset_latin1, *str))
 
730
    {
 
731
      if (field_length-- > 0)
 
732
        value= value*10 + (uint) (uchar) (*str - '0');
 
733
    }
 
734
    if (field_length >= 0)
 
735
    {
 
736
      status->fractional_digits= DATETIME_MAX_DECIMALS - field_length;
 
737
      if (field_length > 0)
 
738
        value*= (long) log_10_int[field_length];
 
739
    }
 
740
    else
 
741
    {
 
742
      /* Scan digits left after microseconds */
 
743
      status->fractional_digits= 6;
 
744
      status->nanoseconds= 100 * (int) (str[-1] - '0');
 
745
      for ( ; str != end && my_isdigit(&my_charset_latin1, *str); str++)
 
746
      { }
 
747
    }
 
748
    date[4]= (ulong) value;
 
749
  }
 
750
  else if ((end - str) == 1 && *str == '.')
 
751
  {
 
752
    str++;
 
753
    date[4]= 0;
 
754
  }
 
755
  else
 
756
    date[4]=0;
 
757
    
 
758
  /* Check for exponent part: E<gigit> | E<sign><digit> */
 
759
  /* (may occur as result of %g formatting of time value) */
 
760
  if ((end - str) > 1 &&
 
761
      (*str == 'e' || *str == 'E') &&
 
762
      (my_isdigit(&my_charset_latin1, str[1]) ||
 
763
       ((str[1] == '-' || str[1] == '+') &&
 
764
        (end - str) > 2 &&
 
765
        my_isdigit(&my_charset_latin1, str[2]))))
 
766
    return 1;
 
767
 
 
768
  if (internal_format_positions[7] != 255)
 
769
  {
 
770
    /* Read a possible AM/PM */
 
771
    while (str != end && my_isspace(&my_charset_latin1, *str))
 
772
      str++;
 
773
    if (str+2 <= end && (str[1] == 'M' || str[1] == 'm'))
 
774
    {
 
775
      if (str[0] == 'p' || str[0] == 'P')
 
776
      {
 
777
        str+= 2;
 
778
        date[1]= date[1]%12 + 12;
 
779
      }
 
780
      else if (str[0] == 'a' || str[0] == 'A')
 
781
        str+=2;
 
782
    }
 
783
  }
 
784
 
 
785
  /* Integer overflow checks */
 
786
  if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
 
787
      date[2] > UINT_MAX || date[3] > UINT_MAX ||
 
788
      date[4] > UINT_MAX)
 
789
    return 1;
 
790
  
 
791
  l_time->year=         0;                      /* For protocol::store_time */
 
792
  l_time->month=        0;
 
793
  
 
794
  l_time->day=  0;
 
795
  l_time->hour= date[1] + date[0] * 24; /* Mix days and hours */
 
796
 
 
797
  l_time->minute=       date[2];
 
798
  l_time->second=       date[3];
 
799
  l_time->second_part=  date[4];
 
800
  l_time->time_type= MYSQL_TIMESTAMP_TIME;
 
801
 
 
802
  if (check_time_mmssff_range(l_time))
 
803
  {
 
804
    status->warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
 
805
    return TRUE;
 
806
  }
 
807
 
 
808
  /* Adjust the value into supported MYSQL_TIME range */
 
809
  adjust_time_range(l_time, &status->warnings);
 
810
 
 
811
  /* Check if there is garbage at end of the MYSQL_TIME specification */
 
812
  if (str != end)
 
813
  {
 
814
    do
 
815
    {
 
816
      if (!my_isspace(&my_charset_latin1,*str))
 
817
      {
 
818
        status->warnings|= MYSQL_TIME_WARN_TRUNCATED;
 
819
        break;
 
820
      }
 
821
    } while (++str != end);
 
822
  }
 
823
  return 0;
 
824
}
 
825
 
 
826
 
 
827
/**
 
828
  Convert number to TIME
 
829
  @param nr            Number to convert.
 
830
  @param OUT ltime     Variable to convert to.
 
831
  @param OUT warnings  Warning vector.
 
832
 
 
833
  @retval false OK
 
834
  @retval true No. is out of range
 
835
*/
 
836
my_bool
 
837
number_to_time(longlong nr, MYSQL_TIME *ltime, int *warnings)
 
838
{
 
839
  if (nr > TIME_MAX_VALUE)
 
840
  {
 
841
    /* For huge numbers try full DATETIME, like str_to_time does. */
 
842
    if (nr >= 10000000000LL) /* '0001-00-00 00-00-00' */
 
843
    {
 
844
      int warnings_backup= *warnings;
 
845
      if (number_to_datetime(nr, ltime, 0, warnings) != LL(-1))
 
846
        return FALSE;
 
847
      *warnings= warnings_backup;
 
848
    }
 
849
    set_max_time(ltime, 0);
 
850
    *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
 
851
    return TRUE;
 
852
  }
 
853
  else if (nr < -TIME_MAX_VALUE)
 
854
  {
 
855
    set_max_time(ltime, 1);
 
856
    *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
 
857
    return TRUE;
 
858
  }
 
859
  if ((ltime->neg= (nr < 0)))
 
860
    nr= -nr;
 
861
  if (nr % 100 >= 60 || nr / 100 % 100 >= 60) /* Check hours and minutes */
 
862
  {
 
863
    set_zero_time(ltime, MYSQL_TIMESTAMP_TIME);
 
864
    *warnings|= MYSQL_TIME_WARN_OUT_OF_RANGE;
 
865
    return TRUE;
 
866
  }
 
867
  ltime->time_type= MYSQL_TIMESTAMP_TIME;
 
868
  ltime->year= ltime->month= ltime->day= 0;
 
869
  TIME_set_hhmmss(ltime, nr);
 
870
  ltime->second_part= 0;
 
871
  return FALSE;
 
872
}
 
873
 
 
874
 
 
875
/**
 
876
  Adjust 'time' value to lie in the MYSQL_TIME range.
 
877
  If the time value lies outside of the range [-838:59:59, 838:59:59],
 
878
  set it to the closest endpoint of the range and set
 
879
  MYSQL_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
 
880
 
 
881
  @param  time     pointer to MYSQL_TIME value
 
882
  @param  warning  set MYSQL_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
 
883
*/
 
884
void adjust_time_range(struct st_mysql_time *my_time, int *warning) 
 
885
{
 
886
  DBUG_ASSERT(!check_time_mmssff_range(my_time));
 
887
  if (check_time_range_quick(my_time))
 
888
  {
 
889
    my_time->day= my_time->second_part= 0;
 
890
    set_max_hhmmss(my_time);
 
891
    *warning|= MYSQL_TIME_WARN_OUT_OF_RANGE;
 
892
  }
 
893
}
 
894
 
 
895
 
 
896
/*
 
897
  Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
 
898
 
 
899
  SYNOPSIS
 
900
    my_init_time()
 
901
*/
 
902
void my_init_time(void)
 
903
{
 
904
  time_t seconds;
 
905
  struct tm *l_time,tm_tmp;
 
906
  MYSQL_TIME my_time;
 
907
  my_bool not_used;
 
908
 
 
909
  seconds= (time_t) time((time_t*) 0);
 
910
  localtime_r(&seconds,&tm_tmp);
 
911
  l_time= &tm_tmp;
 
912
  my_time_zone=         3600;           /* Comp. for -3600 in my_gmt_sec */
 
913
  my_time.year=         (uint) l_time->tm_year+1900;
 
914
  my_time.month=        (uint) l_time->tm_mon+1;
 
915
  my_time.day=          (uint) l_time->tm_mday;
 
916
  my_time.hour=         (uint) l_time->tm_hour;
 
917
  my_time.minute=       (uint) l_time->tm_min;
 
918
  my_time.second=       (uint) l_time->tm_sec;
 
919
  my_time.time_type= MYSQL_TIMESTAMP_DATETIME;
 
920
  my_time.neg= 0;
 
921
  my_time.second_part= 0;
 
922
  my_system_gmt_sec(&my_time, &my_time_zone, &not_used); /* Init my_time_zone */
 
923
}
 
924
 
 
925
 
 
926
/*
 
927
  Handle 2 digit year conversions
 
928
 
 
929
  SYNOPSIS
 
930
  year_2000_handling()
 
931
  year     2 digit year
 
932
 
 
933
  RETURN
 
934
    Year between 1970-2069
 
935
*/
 
936
 
 
937
uint year_2000_handling(uint year)
 
938
{
 
939
  if ((year=year+1900) < 1900+YY_PART_YEAR)
 
940
    year+=100;
 
941
  return year;
 
942
}
 
943
 
 
944
 
 
945
/*
 
946
  Calculate nr of day since year 0 in new date-system (from 1615)
 
947
 
 
948
  SYNOPSIS
 
949
    calc_daynr()
 
950
    year                 Year (exact 4 digit year, no year conversions)
 
951
    month                Month
 
952
    day                  Day
 
953
 
 
954
  NOTES: 0000-00-00 is a valid date, and will return 0
 
955
 
 
956
  RETURN
 
957
    Days since 0000-00-00
 
958
*/
 
959
 
 
960
long calc_daynr(uint year,uint month,uint day)
 
961
{
 
962
  long delsum;
 
963
  int temp;
 
964
  int y= year;                                  /* may be < 0 temporarily */
 
965
  DBUG_ENTER("calc_daynr");
 
966
 
 
967
  if (y == 0 && month == 0)
 
968
    DBUG_RETURN(0);                             /* Skip errors */
 
969
  /* Cast to int to be able to handle month == 0 */
 
970
  delsum= (long) (365 * y + 31 *((int) month - 1) + (int) day);
 
971
  if (month <= 2)
 
972
      y--;
 
973
  else
 
974
    delsum-= (long) ((int) month * 4 + 23) / 10;
 
975
  temp=(int) ((y/100+1)*3)/4;
 
976
  DBUG_PRINT("exit",("year: %d  month: %d  day: %d -> daynr: %ld",
 
977
                     y+(month <= 2),month,day,delsum+y/4-temp));
 
978
  DBUG_ASSERT(delsum+(int) y/4-temp >= 0);
 
979
  DBUG_RETURN(delsum+(int) y/4-temp);
 
980
} /* calc_daynr */
 
981
 
 
982
 
 
983
/*
 
984
  Convert time in MYSQL_TIME representation in system time zone to its
 
985
  my_time_t form (number of seconds in UTC since begginning of Unix Epoch).
 
986
 
 
987
  SYNOPSIS
 
988
    my_system_gmt_sec()
 
989
      t               - time value to be converted
 
990
      my_timezone     - pointer to long where offset of system time zone
 
991
                        from UTC will be stored for caching
 
992
      in_dst_time_gap - set to true if time falls into spring time-gap
 
993
 
 
994
  NOTES
 
995
    The idea is to cache the time zone offset from UTC (including daylight 
 
996
    saving time) for the next call to make things faster. But currently we 
 
997
    just calculate this offset during startup (by calling my_init_time() 
 
998
    function) and use it all the time.
 
999
    Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
 
1000
    is not allowed).
 
1001
 
 
1002
  RETURN VALUE
 
1003
    Time in UTC seconds since Unix Epoch representation.
 
1004
*/
 
1005
my_time_t
 
1006
my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone,
 
1007
                  my_bool *in_dst_time_gap)
 
1008
{
 
1009
  uint loop;
 
1010
  time_t tmp= 0;
 
1011
  int shift= 0;
 
1012
  MYSQL_TIME tmp_time;
 
1013
  MYSQL_TIME *t= &tmp_time;
 
1014
  struct tm *l_time,tm_tmp;
 
1015
  long diff, current_timezone;
 
1016
 
 
1017
  /*
 
1018
    Use temp variable to avoid trashing input data, which could happen in
 
1019
    case of shift required for boundary dates processing.
 
1020
  */
 
1021
  memcpy(&tmp_time, t_src, sizeof(MYSQL_TIME));
 
1022
 
 
1023
  if (!validate_timestamp_range(t))
 
1024
    return 0;
 
1025
 
 
1026
  /*
 
1027
    Calculate the gmt time based on current time and timezone
 
1028
    The -1 on the end is to ensure that if have a date that exists twice
 
1029
    (like 2002-10-27 02:00:0 MET), we will find the initial date.
 
1030
 
 
1031
    By doing -3600 we will have to call localtime_r() several times, but
 
1032
    I couldn't come up with a better way to get a repeatable result :(
 
1033
 
 
1034
    We can't use mktime() as it's buggy on many platforms and not thread safe.
 
1035
 
 
1036
    Note: this code assumes that our time_t estimation is not too far away
 
1037
    from real value (we assume that localtime_r(tmp) will return something
 
1038
    within 24 hrs from t) which is probably true for all current time zones.
 
1039
 
 
1040
    Note2: For the dates, which have time_t representation close to
 
1041
    MAX_INT32 (efficient time_t limit for supported platforms), we should
 
1042
    do a small trick to avoid overflow. That is, convert the date, which is
 
1043
    two days earlier, and then add these days to the final value.
 
1044
 
 
1045
    The same trick is done for the values close to 0 in time_t
 
1046
    representation for platfroms with unsigned time_t (QNX).
 
1047
 
 
1048
    To be more verbose, here is a sample (extracted from the code below):
 
1049
    (calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
 
1050
    would return -2147480896 because of the long type overflow. In result
 
1051
    we would get 1901 year in localtime_r(), which is an obvious error.
 
1052
 
 
1053
    Alike problem raises with the dates close to Epoch. E.g.
 
1054
    (calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
 
1055
    will give -3600.
 
1056
 
 
1057
    On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
 
1058
    wil give us a date around 2106 year. Which is no good.
 
1059
 
 
1060
    Theoreticaly, there could be problems with the latter conversion:
 
1061
    there are at least two timezones, which had time switches near 1 Jan
 
1062
    of 1970 (because of political reasons). These are America/Hermosillo and
 
1063
    America/Mazatlan time zones. They changed their offset on
 
1064
    1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
 
1065
    the code below will give incorrect results for dates close to
 
1066
    1970-01-01, in the case OS takes into account these historical switches.
 
1067
    Luckily, it seems that we support only one platform with unsigned
 
1068
    time_t. It's QNX. And QNX does not support historical timezone data at all.
 
1069
    E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
 
1070
    historical information for localtime_r() etc. That is, the problem is not
 
1071
    relevant to QNX.
 
1072
 
 
1073
    We are safe with shifts close to MAX_INT32, as there are no known
 
1074
    time switches on Jan 2038 yet :)
 
1075
  */
 
1076
  if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && (t->day > 4))
 
1077
  {
 
1078
    /*
 
1079
      Below we will pass (uint) (t->day - shift) to calc_daynr.
 
1080
      As we don't want to get an overflow here, we will shift
 
1081
      only safe dates. That's why we have (t->day > 4) above.
 
1082
    */
 
1083
    t->day-= 2;
 
1084
    shift= 2;
 
1085
  }
 
1086
#ifdef TIME_T_UNSIGNED
 
1087
  else
 
1088
  {
 
1089
    /*
 
1090
      We can get 0 in time_t representaion only on 1969, 31 of Dec or on
 
1091
      1970, 1 of Jan. For both dates we use shift, which is added
 
1092
      to t->day in order to step out a bit from the border.
 
1093
      This is required for platforms, where time_t is unsigned.
 
1094
      As far as I know, among the platforms we support it's only QNX.
 
1095
      Note: the order of below if-statements is significant.
 
1096
    */
 
1097
 
 
1098
    if ((t->year == TIMESTAMP_MIN_YEAR + 1) && (t->month == 1)
 
1099
        && (t->day <= 10))
 
1100
    {
 
1101
      t->day+= 2;
 
1102
      shift= -2;
 
1103
    }
 
1104
 
 
1105
    if ((t->year == TIMESTAMP_MIN_YEAR) && (t->month == 12)
 
1106
        && (t->day == 31))
 
1107
    {
 
1108
      t->year++;
 
1109
      t->month= 1;
 
1110
      t->day= 2;
 
1111
      shift= -2;
 
1112
    }
 
1113
  }
 
1114
#endif
 
1115
 
 
1116
  tmp= (time_t) (((calc_daynr((uint) t->year, (uint) t->month, (uint) t->day) -
 
1117
                   (long) days_at_timestart) * SECONDS_IN_24H +
 
1118
                   (long) t->hour*3600L +
 
1119
                  (long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
 
1120
                 3600);
 
1121
 
 
1122
  current_timezone= my_time_zone;
 
1123
  localtime_r(&tmp,&tm_tmp);
 
1124
  l_time=&tm_tmp;
 
1125
  for (loop=0;
 
1126
       loop < 2 &&
 
1127
         (t->hour != (uint) l_time->tm_hour ||
 
1128
          t->minute != (uint) l_time->tm_min ||
 
1129
          t->second != (uint) l_time->tm_sec);
 
1130
       loop++)
 
1131
  {                                     /* One check should be enough ? */
 
1132
    /* Get difference in days */
 
1133
    int days= t->day - l_time->tm_mday;
 
1134
    if (days < -1)
 
1135
      days= 1;                                  /* Month has wrapped */
 
1136
    else if (days > 1)
 
1137
      days= -1;
 
1138
    diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
 
1139
          (long) (60*((int) t->minute - (int) l_time->tm_min)) +
 
1140
          (long) ((int) t->second - (int) l_time->tm_sec));
 
1141
    current_timezone+= diff+3600;               /* Compensate for -3600 above */
 
1142
    tmp+= (time_t) diff;
 
1143
    localtime_r(&tmp,&tm_tmp);
 
1144
    l_time=&tm_tmp;
 
1145
  }
 
1146
  /*
 
1147
    Fix that if we are in the non existing daylight saving time hour
 
1148
    we move the start of the next real hour.
 
1149
 
 
1150
    This code doesn't handle such exotical thing as time-gaps whose length
 
1151
    is more than one hour or non-integer (latter can theoretically happen
 
1152
    if one of seconds will be removed due leap correction, or because of
 
1153
    general time correction like it happened for Africa/Monrovia time zone
 
1154
    in year 1972).
 
1155
  */
 
1156
  if (loop == 2 && t->hour != (uint) l_time->tm_hour)
 
1157
  {
 
1158
    int days= t->day - l_time->tm_mday;
 
1159
    if (days < -1)
 
1160
      days=1;                                   /* Month has wrapped */
 
1161
    else if (days > 1)
 
1162
      days= -1;
 
1163
    diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
 
1164
          (long) (60*((int) t->minute - (int) l_time->tm_min)) +
 
1165
          (long) ((int) t->second - (int) l_time->tm_sec));
 
1166
    if (diff == 3600)
 
1167
      tmp+=3600 - t->minute*60 - t->second;     /* Move to next hour */
 
1168
    else if (diff == -3600)
 
1169
      tmp-=t->minute*60 + t->second;            /* Move to previous hour */
 
1170
 
 
1171
    *in_dst_time_gap= 1;
 
1172
  }
 
1173
  *my_timezone= current_timezone;
 
1174
 
 
1175
 
 
1176
  /* shift back, if we were dealing with boundary dates */
 
1177
  tmp+= shift * SECONDS_IN_24H;
 
1178
 
 
1179
  /*
 
1180
    This is possible for dates, which slightly exceed boundaries.
 
1181
    Conversion will pass ok for them, but we don't allow them.
 
1182
    First check will pass for platforms with signed time_t.
 
1183
    instruction above (tmp+= shift*86400L) could exceed
 
1184
    MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
 
1185
    So, tmp < TIMESTAMP_MIN_VALUE will be triggered. On platfroms
 
1186
    with unsigned time_t tmp+= shift*86400L might result in a number,
 
1187
    larger then TIMESTAMP_MAX_VALUE, so another check will work.
 
1188
  */
 
1189
  if (!IS_TIME_T_VALID_FOR_TIMESTAMP(tmp))
 
1190
    tmp= 0;
 
1191
 
 
1192
  return (my_time_t) tmp;
 
1193
} /* my_system_gmt_sec */
 
1194
 
 
1195
 
 
1196
/**
 
1197
  Print the microsecond part: ".NNN"
 
1198
  @param to        OUT The string pointer to print at
 
1199
  @param useconds      The microseconds value.
 
1200
  @param dec           Precision, between 1 and 6.
 
1201
  @return              The length of the result string.
 
1202
*/
 
1203
static inline int
 
1204
my_useconds_to_str(char *to, ulong useconds, uint dec)
 
1205
{
 
1206
  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
 
1207
  return sprintf(to, ".%0*lu", (int) dec,
 
1208
                 useconds / (ulong) log_10_int[DATETIME_MAX_DECIMALS - dec]);
 
1209
}
 
1210
 
 
1211
 
 
1212
/*
 
1213
  Functions to convert time/date/datetime value to a string,
 
1214
  using default format.
 
1215
  This functions don't check that given MYSQL_TIME structure members are
 
1216
  in valid range. If they are not, return value won't reflect any
 
1217
  valid date either. Additionally, make_time doesn't take into
 
1218
  account time->day member: it's assumed that days have been converted
 
1219
  to hours already.
 
1220
 
 
1221
  RETURN
 
1222
    number of characters written to 'to'
 
1223
*/
 
1224
 
 
1225
int my_time_to_str(const MYSQL_TIME *l_time, char *to, uint dec)
 
1226
{
 
1227
  uint extra_hours= 0;
 
1228
  int len= sprintf(to, "%s%02u:%02u:%02u", (l_time->neg ? "-" : ""),
 
1229
                   extra_hours + l_time->hour, l_time->minute, l_time->second);
 
1230
  if (dec)
 
1231
    len+= my_useconds_to_str(to + len, l_time->second_part, dec);
 
1232
  return len;
 
1233
}
 
1234
 
 
1235
int my_date_to_str(const MYSQL_TIME *l_time, char *to)
 
1236
{
 
1237
  return sprintf(to, "%04u-%02u-%02u",
 
1238
                 l_time->year, l_time->month, l_time->day);
 
1239
}
 
1240
 
 
1241
 
 
1242
/*
 
1243
  Convert datetime to a string 'YYYY-MM-DD hh:mm:ss'.
 
1244
  Open coded for better performance.
 
1245
  This code previously resided in field.cc, in Field_timestamp::val_str().
 
1246
 
 
1247
  @param  to     OUT  The string pointer to print at.
 
1248
  @param  ltime       The MYSQL_TIME value.
 
1249
  @return             The length of the result string.
 
1250
*/
 
1251
static inline int
 
1252
TIME_to_datetime_str(char *to, const MYSQL_TIME *ltime)
 
1253
{
 
1254
  uint32 temp, temp2;
 
1255
  /* Year */
 
1256
  temp= ltime->year / 100;
 
1257
  *to++= (char) ('0' + temp / 10);
 
1258
  *to++= (char) ('0' + temp % 10);
 
1259
  temp= ltime->year % 100;
 
1260
  *to++= (char) ('0' + temp / 10);
 
1261
  *to++= (char) ('0' + temp % 10);
 
1262
  *to++= '-';
 
1263
  /* Month */
 
1264
  temp= ltime->month;
 
1265
  temp2= temp / 10;
 
1266
  temp= temp-temp2 * 10;
 
1267
  *to++= (char) ('0' + (char) (temp2));
 
1268
  *to++= (char) ('0' + (char) (temp));
 
1269
  *to++= '-';
 
1270
  /* Day */ 
 
1271
  temp= ltime->day;
 
1272
  temp2= temp / 10;
 
1273
  temp= temp - temp2 * 10;
 
1274
  *to++= (char) ('0' + (char) (temp2));
 
1275
  *to++= (char) ('0' + (char) (temp));
 
1276
  *to++= ' ';
 
1277
  /* Hour */
 
1278
  temp= ltime->hour;
 
1279
  temp2= temp / 10;
 
1280
  temp= temp - temp2 * 10;
 
1281
  *to++= (char) ('0' + (char) (temp2));
 
1282
  *to++= (char) ('0' + (char) (temp));
 
1283
  *to++= ':';
 
1284
  /* Minute */
 
1285
  temp= ltime->minute;
 
1286
  temp2= temp / 10;
 
1287
  temp= temp - temp2 * 10;
 
1288
  *to++= (char) ('0' + (char) (temp2));
 
1289
  *to++= (char) ('0' + (char) (temp));
 
1290
  *to++= ':';
 
1291
  /* Second */
 
1292
  temp= ltime->second;
 
1293
  temp2=temp / 10;
 
1294
  temp= temp - temp2 * 10;
 
1295
  *to++= (char) ('0' + (char) (temp2));
 
1296
  *to++= (char) ('0' + (char) (temp));
 
1297
  return 19;
 
1298
}
 
1299
 
 
1300
 
 
1301
/**
 
1302
  Print a datetime value with an optional fractional part.
 
1303
 
 
1304
  @l_time       The MYSQL_TIME value to print.
 
1305
  @to      OUT  The string pointer to print at.
 
1306
  @return       The length of the result string.  
 
1307
*/
 
1308
int my_datetime_to_str(const MYSQL_TIME *l_time, char *to, uint dec)
 
1309
{
 
1310
  int len= TIME_to_datetime_str(to, l_time);
 
1311
  if (dec)
 
1312
    len+= my_useconds_to_str(to + len, l_time->second_part, dec);
 
1313
  else
 
1314
    to[len]= '\0';
 
1315
  return len;
 
1316
}
 
1317
 
 
1318
 
 
1319
/*
 
1320
  Convert struct DATE/TIME/DATETIME value to string using built-in
 
1321
  MySQL time conversion formats.
 
1322
 
 
1323
  SYNOPSIS
 
1324
    my_TIME_to_string()
 
1325
 
 
1326
  NOTE
 
1327
    The string must have at least MAX_DATE_STRING_REP_LENGTH bytes reserved.
 
1328
*/
 
1329
 
 
1330
int my_TIME_to_str(const MYSQL_TIME *l_time, char *to, uint dec)
 
1331
{
 
1332
  switch (l_time->time_type) {
 
1333
  case MYSQL_TIMESTAMP_DATETIME:
 
1334
    return my_datetime_to_str(l_time, to, dec);
 
1335
  case MYSQL_TIMESTAMP_DATE:
 
1336
    return my_date_to_str(l_time, to);
 
1337
  case MYSQL_TIMESTAMP_TIME:
 
1338
    return my_time_to_str(l_time, to, dec);
 
1339
  case MYSQL_TIMESTAMP_NONE:
 
1340
  case MYSQL_TIMESTAMP_ERROR:
 
1341
    to[0]='\0';
 
1342
    return 0;
 
1343
  default:
 
1344
    DBUG_ASSERT(0);
 
1345
    return 0;
 
1346
  }
 
1347
}
 
1348
 
 
1349
 
 
1350
/**
 
1351
  Print a timestamp with an oprional fractional part: XXXXX[.YYYYY]
 
1352
 
 
1353
  @param      tm  The timestamp value to print.
 
1354
  @param  OUT to  The string pointer to print at. 
 
1355
  @param      dec Precision, in the range 0..6.
 
1356
  @return         The length of the result string.
 
1357
*/
 
1358
int my_timeval_to_str(const struct timeval *tm, char *to, uint dec)
 
1359
{
 
1360
  int len= sprintf(to, "%d", (int) tm->tv_sec);
 
1361
  if (dec)
 
1362
    len+= my_useconds_to_str(to + len, tm->tv_usec, dec);
 
1363
  return len;
 
1364
}
 
1365
 
 
1366
 
 
1367
/*
 
1368
  Convert datetime value specified as number to broken-down TIME
 
1369
  representation and form value of DATETIME type as side-effect.
 
1370
 
 
1371
  SYNOPSIS
 
1372
    number_to_datetime()
 
1373
      nr         - datetime value as number
 
1374
      time_res   - pointer for structure for broken-down representation
 
1375
      flags      - flags to use in validating date, as in str_to_datetime()
 
1376
      was_cut    0      Value ok
 
1377
                 1      If value was cut during conversion
 
1378
                 2      check_date(date,flags) considers date invalid
 
1379
 
 
1380
  DESCRIPTION
 
1381
    Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
 
1382
    YYYYMMDDHHMMSS to broken-down MYSQL_TIME representation. Return value in
 
1383
    YYYYMMDDHHMMSS format as side-effect.
 
1384
 
 
1385
    This function also checks if datetime value fits in DATETIME range.
 
1386
 
 
1387
  RETURN VALUE
 
1388
    -1              Timestamp with wrong values
 
1389
    anything else   DATETIME as integer in YYYYMMDDHHMMSS format
 
1390
    Datetime value in YYYYMMDDHHMMSS format.
 
1391
 
 
1392
    was_cut         if return value -1: one of
 
1393
                      - MYSQL_TIME_WARN_OUT_OF_RANGE
 
1394
                      - MYSQL_TIME_WARN_ZERO_DATE
 
1395
                      - MYSQL_TIME_WARN_TRUNCATED
 
1396
                    otherwise 0.
 
1397
*/
 
1398
 
 
1399
longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res,
 
1400
                            ulonglong flags, int *was_cut)
 
1401
{
 
1402
  long part1,part2;
 
1403
 
 
1404
  *was_cut= 0;
 
1405
  memset(time_res, 0, sizeof(*time_res));
 
1406
  time_res->time_type=MYSQL_TIMESTAMP_DATE;
 
1407
 
 
1408
  if (nr == LL(0) || nr >= LL(10000101000000))
 
1409
  {
 
1410
    time_res->time_type=MYSQL_TIMESTAMP_DATETIME;
 
1411
    if (nr > 99999999999999LL) /* 9999-99-99 99:99:99 */
 
1412
    {
 
1413
      *was_cut= MYSQL_TIME_WARN_OUT_OF_RANGE;
 
1414
      return LL(-1);
 
1415
    }
 
1416
    goto ok;
 
1417
  }
 
1418
  if (nr < 101)
 
1419
    goto err;
 
1420
  if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
 
1421
  {
 
1422
    nr= (nr+20000000L)*1000000L;                 /* YYMMDD, year: 2000-2069 */
 
1423
    goto ok;
 
1424
  }
 
1425
  if (nr < (YY_PART_YEAR)*10000L+101L)
 
1426
    goto err;
 
1427
  if (nr <= 991231L)
 
1428
  {
 
1429
    nr= (nr+19000000L)*1000000L;                 /* YYMMDD, year: 1970-1999 */
 
1430
    goto ok;
 
1431
  }
 
1432
  /*
 
1433
    Though officially we support DATE values from 1000-01-01 only, one can
 
1434
    easily insert a value like 1-1-1. So, for consistency reasons such dates
 
1435
    are allowed when TIME_FUZZY_DATE is set.
 
1436
  */
 
1437
  if (nr < 10000101L && !(flags & TIME_FUZZY_DATE))
 
1438
    goto err;
 
1439
  if (nr <= 99991231L)
 
1440
  {
 
1441
    nr= nr*1000000L;
 
1442
    goto ok;
 
1443
  }
 
1444
  if (nr < 101000000L)
 
1445
    goto err;
 
1446
 
 
1447
  time_res->time_type=MYSQL_TIMESTAMP_DATETIME;
 
1448
 
 
1449
  if (nr <= (YY_PART_YEAR-1)*LL(10000000000)+LL(1231235959))
 
1450
  {
 
1451
    nr= nr+LL(20000000000000);                   /* YYMMDDHHMMSS, 2000-2069 */
 
1452
    goto ok;
 
1453
  }
 
1454
  if (nr <  YY_PART_YEAR*LL(10000000000)+ LL(101000000))
 
1455
    goto err;
 
1456
  if (nr <= LL(991231235959))
 
1457
    nr= nr+LL(19000000000000);          /* YYMMDDHHMMSS, 1970-1999 */
 
1458
 
 
1459
 ok:
 
1460
  part1=(long) (nr/LL(1000000));
 
1461
  part2=(long) (nr - (longlong) part1*LL(1000000));
 
1462
  time_res->year=  (int) (part1/10000L);  part1%=10000L;
 
1463
  time_res->month= (int) part1 / 100;
 
1464
  time_res->day=   (int) part1 % 100;
 
1465
  time_res->hour=  (int) (part2/10000L);  part2%=10000L;
 
1466
  time_res->minute=(int) part2 / 100;
 
1467
  time_res->second=(int) part2 % 100;
 
1468
 
 
1469
  if (!check_datetime_range(time_res) &&
 
1470
      !check_date(time_res, (nr != 0), flags, was_cut))
 
1471
    return nr;
 
1472
 
 
1473
  /* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */
 
1474
  if (!nr && (flags & TIME_NO_ZERO_DATE))
 
1475
    return LL(-1);
 
1476
 
 
1477
 err:
 
1478
  *was_cut= MYSQL_TIME_WARN_TRUNCATED;
 
1479
  return LL(-1);
 
1480
}
 
1481
 
 
1482
 
 
1483
/**
 
1484
  Convert time value to integer in YYYYMMDDHHMMSS.
 
1485
  @param  my_time  The MYSQL_TIME value to convert.
 
1486
  @return          A number in format YYYYMMDDHHMMSS.
 
1487
*/
 
1488
ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *my_time)
 
1489
{
 
1490
  return ((ulonglong) (my_time->year * 10000UL +
 
1491
                       my_time->month * 100UL +
 
1492
                       my_time->day) * ULL(1000000) +
 
1493
          (ulonglong) (my_time->hour * 10000UL +
 
1494
                       my_time->minute * 100UL +
 
1495
                       my_time->second));
 
1496
}
 
1497
 
 
1498
 
 
1499
 
 
1500
/**
 
1501
  Convert MYSQL_TIME value to integer in YYYYMMDD format
 
1502
  @param my_time  The MYSQL_TIME value to convert.
 
1503
  @return         A number in format YYYYMMDD.
 
1504
*/
 
1505
ulonglong TIME_to_ulonglong_date(const MYSQL_TIME *my_time)
 
1506
{
 
1507
  return (ulonglong) (my_time->year * 10000UL + my_time->month * 100UL +
 
1508
                      my_time->day);
 
1509
}
 
1510
 
 
1511
 
 
1512
/**
 
1513
  Convert MYSQL_TIME value to integer in HHMMSS format.
 
1514
  This function doesn't take into account time->day member:
 
1515
  it's assumed that days have been converted to hours already.
 
1516
  @param my_time  The TIME value to convert.
 
1517
  @return         The number in HHMMSS format.
 
1518
*/
 
1519
ulonglong TIME_to_ulonglong_time(const MYSQL_TIME *my_time)
 
1520
{
 
1521
  return (ulonglong) (my_time->hour * 10000UL +
 
1522
                      my_time->minute * 100UL +
 
1523
                      my_time->second);
 
1524
}
 
1525
 
 
1526
 
 
1527
/**
 
1528
  Set day, month and year from a number
 
1529
  @param ltime    MYSQL_TIME variable
 
1530
  @param yymmdd   Number in YYYYMMDD format
 
1531
*/
 
1532
void TIME_set_yymmdd(MYSQL_TIME *ltime, uint yymmdd)
 
1533
{
 
1534
  ltime->day=   (int) (yymmdd % 100);
 
1535
  ltime->month= (int) (yymmdd / 100) % 100;
 
1536
  ltime->year=  (int) (yymmdd / 10000);
 
1537
}
 
1538
 
 
1539
 
 
1540
/**
 
1541
  Set hour, minute and secondr from a number
 
1542
  @param ltime    MYSQL_TIME variable
 
1543
  @param hhmmss   Number in HHMMSS format
 
1544
*/
 
1545
void TIME_set_hhmmss(MYSQL_TIME *ltime, uint hhmmss)
 
1546
{
 
1547
  ltime->second=  (int) (hhmmss % 100);
 
1548
  ltime->minute=  (int) (hhmmss / 100) % 100;
 
1549
  ltime->hour=    (int) (hhmmss / 10000);
 
1550
}
 
1551
 
 
1552
 
 
1553
/*
 
1554
  Convert struct MYSQL_TIME (date and time split into year/month/day/hour/...
 
1555
  to a number in format YYYYMMDDHHMMSS (DATETIME),
 
1556
  YYYYMMDD (DATE)  or HHMMSS (TIME).
 
1557
 
 
1558
  SYNOPSIS
 
1559
    TIME_to_ulonglong()
 
1560
 
 
1561
  DESCRIPTION
 
1562
    The function is used when we need to convert value of time item
 
1563
    to a number if it's used in numeric context, i. e.:
 
1564
    SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0;
 
1565
    SELECT ?+1;
 
1566
 
 
1567
  NOTE
 
1568
    This function doesn't check that given MYSQL_TIME structure members are
 
1569
    in valid range. If they are not, return value won't reflect any
 
1570
    valid date either.
 
1571
*/
 
1572
 
 
1573
ulonglong TIME_to_ulonglong(const MYSQL_TIME *my_time)
 
1574
{
 
1575
  switch (my_time->time_type) {
 
1576
  case MYSQL_TIMESTAMP_DATETIME:
 
1577
    return TIME_to_ulonglong_datetime(my_time);
 
1578
  case MYSQL_TIMESTAMP_DATE:
 
1579
    return TIME_to_ulonglong_date(my_time);
 
1580
  case MYSQL_TIMESTAMP_TIME:
 
1581
    return TIME_to_ulonglong_time(my_time);
 
1582
  case MYSQL_TIMESTAMP_NONE:
 
1583
  case MYSQL_TIMESTAMP_ERROR:
 
1584
    return ULL(0);
 
1585
  default:
 
1586
    DBUG_ASSERT(0);
 
1587
  }
 
1588
  return 0;
 
1589
}
 
1590
 
 
1591
 
 
1592
/*** TIME low-level memory and disk representation routines ***/
 
1593
 
 
1594
/*
 
1595
  In-memory format:
 
1596
 
 
1597
   1  bit sign          (Used for sign, when on disk)
 
1598
   1  bit unused        (Reserved for wider hour range, e.g. for intervals)
 
1599
   10 bit hour          (0-836)
 
1600
   6  bit minute        (0-59)
 
1601
   6  bit second        (0-59)
 
1602
  24  bits microseconds (0-999999)
 
1603
 
 
1604
 Total: 48 bits = 6 bytes
 
1605
   Suhhhhhh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
 
1606
*/
 
1607
 
 
1608
 
 
1609
/**
 
1610
  Convert time value to numeric packed representation.
 
1611
  
 
1612
  @param    ltime   The value to convert.
 
1613
  @return           Numeric packed representation.
 
1614
*/
 
1615
longlong TIME_to_longlong_time_packed(const MYSQL_TIME *ltime)
 
1616
{
 
1617
  /* If month is 0, we mix day with hours: "1 00:10:10" -> "24:00:10" */
 
1618
  long hms= (((ltime->month ? 0 : ltime->day * 24) + ltime->hour) << 12) |
 
1619
            (ltime->minute << 6) | ltime->second;
 
1620
  longlong tmp= MY_PACKED_TIME_MAKE(hms, ltime->second_part);
 
1621
  return ltime->neg ? -tmp : tmp;
 
1622
}
 
1623
 
 
1624
 
 
1625
/**
 
1626
  Convert time packed numeric representation to time.
 
1627
 
 
1628
  @param  OUT ltime  The MYSQL_TIME variable to set.
 
1629
  @param      tmp    The packed numeric representation.
 
1630
*/
 
1631
void TIME_from_longlong_time_packed(MYSQL_TIME *ltime, longlong tmp)
 
1632
{
 
1633
  long hms;
 
1634
  if ((ltime->neg= (tmp < 0)))
 
1635
    tmp= -tmp;
 
1636
  hms= MY_PACKED_TIME_GET_INT_PART(tmp);
 
1637
  ltime->year=   (uint) 0;
 
1638
  ltime->month=  (uint) 0;
 
1639
  ltime->day=    (uint) 0;
 
1640
  ltime->hour=   (uint) (hms >> 12) % (1 << 10); /* 10 bits starting at 12th */
 
1641
  ltime->minute= (uint) (hms >> 6)  % (1 << 6);  /* 6 bits starting at 6th   */
 
1642
  ltime->second= (uint)  hms        % (1 << 6);  /* 6 bits starting at 0th   */
 
1643
  ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
 
1644
  ltime->time_type= MYSQL_TIMESTAMP_TIME;
 
1645
}
 
1646
 
 
1647
 
 
1648
/**
 
1649
  Calculate binary size of packed numeric time representation.
 
1650
  
 
1651
  @param   dec   Precision.
 
1652
*/
 
1653
uint my_time_binary_length(uint dec)
 
1654
{
 
1655
  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
 
1656
  return 3 + (dec + 1) / 2;
 
1657
}
 
1658
 
 
1659
 
 
1660
/*
 
1661
  On disk we convert from signed representation to unsigned
 
1662
  representation using TIMEF_OFS, so all values become binary comparable.
 
1663
*/
 
1664
#define TIMEF_OFS 0x800000000000LL
 
1665
#define TIMEF_INT_OFS 0x800000LL
 
1666
 
 
1667
 
 
1668
/**
 
1669
  Convert in-memory numeric time representation to on-disk representation
 
1670
  
 
1671
  @param       nr   Value in packed numeric time format.
 
1672
  @param   OUT ptr  The buffer to put value at.
 
1673
  @param       dec  Precision.
 
1674
*/
 
1675
void my_time_packed_to_binary(longlong nr, uchar *ptr, uint dec)
 
1676
{
 
1677
  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
 
1678
  /* Make sure the stored value was previously properly rounded or truncated */
 
1679
  DBUG_ASSERT((MY_PACKED_TIME_GET_FRAC_PART(nr) % 
 
1680
              (int) log_10_int[DATETIME_MAX_DECIMALS - dec]) == 0);
 
1681
 
 
1682
  switch (dec)
 
1683
  {
 
1684
  case 0:
 
1685
  default:
 
1686
    mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
 
1687
    break;
 
1688
 
 
1689
  case 1:
 
1690
  case 2:
 
1691
    mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
 
1692
    ptr[3]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
 
1693
    break;
 
1694
 
 
1695
  case 4:
 
1696
  case 3:
 
1697
    mi_int3store(ptr, TIMEF_INT_OFS + MY_PACKED_TIME_GET_INT_PART(nr));
 
1698
    mi_int2store(ptr + 3, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
 
1699
    break;
 
1700
 
 
1701
  case 5:
 
1702
  case 6:
 
1703
    mi_int6store(ptr, nr + TIMEF_OFS);
 
1704
    break;
 
1705
  }
 
1706
}
 
1707
 
 
1708
 
 
1709
/**
 
1710
  Convert on-disk time representation to in-memory packed numeric 
 
1711
  representation.
 
1712
  
 
1713
  @param   ptr  The pointer to read the value at.
 
1714
  @param   dec  Precision.
 
1715
  @return       Packed numeric time representation.
 
1716
*/
 
1717
longlong my_time_packed_from_binary(const uchar *ptr, uint dec)
 
1718
{
 
1719
  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
 
1720
 
 
1721
  switch (dec)
 
1722
  {
 
1723
  case 0:
 
1724
  default:
 
1725
    {
 
1726
      longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
 
1727
      return MY_PACKED_TIME_MAKE_INT(intpart);
 
1728
    }
 
1729
  case 1:
 
1730
  case 2:
 
1731
    {
 
1732
      longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
 
1733
      int frac= (uint) ptr[3];
 
1734
      if (intpart < 0 && frac)
 
1735
      {
 
1736
        /*
 
1737
          Negative values are stored with reverse fractional part order,
 
1738
          for binary sort compatibility.
 
1739
 
 
1740
            Disk value  intpart frac   Time value   Memory value
 
1741
            800000.00    0      0      00:00:00.00  0000000000.000000
 
1742
            7FFFFF.FF   -1      255   -00:00:00.01  FFFFFFFFFF.FFD8F0
 
1743
            7FFFFF.9D   -1      99    -00:00:00.99  FFFFFFFFFF.F0E4D0
 
1744
            7FFFFF.00   -1      0     -00:00:01.00  FFFFFFFFFF.000000
 
1745
            7FFFFE.FF   -1      255   -00:00:01.01  FFFFFFFFFE.FFD8F0
 
1746
            7FFFFE.F6   -2      246   -00:00:01.10  FFFFFFFFFE.FE7960
 
1747
 
 
1748
            Formula to convert fractional part from disk format
 
1749
            (now stored in "frac" variable) to absolute value: "0x100 - frac".
 
1750
            To reconstruct in-memory value, we shift
 
1751
            to the next integer value and then substruct fractional part.
 
1752
        */
 
1753
        intpart++;    /* Shift to the next integer value */
 
1754
        frac-= 0x100; /* -(0x100 - frac) */
 
1755
      }
 
1756
      return MY_PACKED_TIME_MAKE(intpart, frac * 10000);
 
1757
    }
 
1758
 
 
1759
  case 3:
 
1760
  case 4:
 
1761
    {
 
1762
      longlong intpart= mi_uint3korr(ptr) - TIMEF_INT_OFS;
 
1763
      int frac= mi_uint2korr(ptr + 3);
 
1764
      if (intpart < 0 && frac)
 
1765
      {
 
1766
        /*
 
1767
          Fix reverse fractional part order: "0x10000 - frac".
 
1768
          See comments for FSP=1 and FSP=2 above.
 
1769
        */
 
1770
        intpart++;      /* Shift to the next integer value */
 
1771
        frac-= 0x10000; /* -(0x10000-frac) */
 
1772
      }
 
1773
      return MY_PACKED_TIME_MAKE(intpart, frac * 100);
 
1774
    }
 
1775
 
 
1776
  case 5:
 
1777
  case 6:
 
1778
    return ((longlong) mi_uint6korr(ptr)) - TIMEF_OFS;
 
1779
  }
 
1780
}
 
1781
 
 
1782
 
 
1783
/*** DATETIME and DATE low-level memory and disk representation routines ***/
 
1784
 
 
1785
/*
 
1786
    1 bit  sign            (used when on disk)
 
1787
   17 bits year*13+month   (year 0-9999, month 0-12)
 
1788
    5 bits day             (0-31)
 
1789
    5 bits hour            (0-23)
 
1790
    6 bits minute          (0-59)
 
1791
    6 bits second          (0-59)
 
1792
   24 bits microseconds    (0-999999)
 
1793
 
 
1794
   Total: 64 bits = 8 bytes
 
1795
 
 
1796
   SYYYYYYY.YYYYYYYY.YYdddddh.hhhhmmmm.mmssssss.ffffffff.ffffffff.ffffffff
 
1797
*/
 
1798
 
 
1799
/**
 
1800
  Convert datetime to packed numeric datetime representation.
 
1801
  @param ltime  The value to convert.
 
1802
  @return       Packed numeric representation of ltime.
 
1803
*/
 
1804
longlong TIME_to_longlong_datetime_packed(const MYSQL_TIME *ltime)
 
1805
{
 
1806
  longlong ymd= ((ltime->year * 13 + ltime->month) << 5) | ltime->day;
 
1807
  longlong hms= (ltime->hour << 12) | (ltime->minute << 6) | ltime->second;
 
1808
  longlong tmp= MY_PACKED_TIME_MAKE(((ymd << 17) | hms), ltime->second_part);
 
1809
  DBUG_ASSERT(!check_datetime_range(ltime)); /* Make sure no overflow */
 
1810
  return ltime->neg ? -tmp : tmp;
 
1811
}
 
1812
 
 
1813
 
 
1814
/**
 
1815
  Convert date to packed numeric date representation.
 
1816
  Numeric packed date format is similar to numeric packed datetime
 
1817
  representation, with zero hhmmss part.
 
1818
  
 
1819
  @param ltime The value to convert.
 
1820
  @return      Packed numeric representation of ltime.
 
1821
*/
 
1822
longlong TIME_to_longlong_date_packed(const MYSQL_TIME *ltime)
 
1823
{
 
1824
  longlong ymd= ((ltime->year * 13 + ltime->month) << 5) | ltime->day;
 
1825
  return MY_PACKED_TIME_MAKE_INT(ymd << 17);
 
1826
}
 
1827
 
 
1828
 
 
1829
/**
 
1830
  Convert year to packed numeric date representation.
 
1831
  Packed value for YYYY is the same to packed value for date YYYY-00-00.
 
1832
*/
 
1833
longlong year_to_longlong_datetime_packed(long year)
 
1834
{
 
1835
  longlong ymd= ((year * 13) << 5);
 
1836
  return MY_PACKED_TIME_MAKE_INT(ymd << 17);
 
1837
}
 
1838
 
 
1839
 
 
1840
/**
 
1841
  Convert packed numeric datetime representation to MYSQL_TIME.
 
1842
  @param OUT  ltime The datetime variable to convert to.
 
1843
  @param      tmp   The packed numeric datetime value.
 
1844
*/
 
1845
void TIME_from_longlong_datetime_packed(MYSQL_TIME *ltime, longlong tmp)
 
1846
{
 
1847
  longlong ymd, hms;
 
1848
  longlong ymdhms, ym;
 
1849
  if ((ltime->neg= (tmp < 0)))
 
1850
    tmp= -tmp;
 
1851
 
 
1852
  ltime->second_part= MY_PACKED_TIME_GET_FRAC_PART(tmp);
 
1853
  ymdhms= MY_PACKED_TIME_GET_INT_PART(tmp);
 
1854
 
 
1855
  ymd= ymdhms >> 17;
 
1856
  ym= ymd >> 5;
 
1857
  hms= ymdhms % (1 << 17);
 
1858
 
 
1859
  ltime->day= ymd % (1 << 5);
 
1860
  ltime->month= ym % 13;
 
1861
  ltime->year= ym / 13;
 
1862
 
 
1863
  ltime->second= hms % (1 << 6);
 
1864
  ltime->minute= (hms >> 6) % (1 << 6);
 
1865
  ltime->hour= (hms >> 12);
 
1866
  
 
1867
  ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
 
1868
}
 
1869
 
 
1870
 
 
1871
/**
 
1872
  Convert packed numeric date representation to MYSQL_TIME.
 
1873
  @param OUT  ltime The date variable to convert to.
 
1874
  @param      tmp   The packed numeric date value.
 
1875
*/
 
1876
void TIME_from_longlong_date_packed(MYSQL_TIME *ltime, longlong tmp)
 
1877
{
 
1878
  TIME_from_longlong_datetime_packed(ltime, tmp);
 
1879
  ltime->time_type= MYSQL_TIMESTAMP_DATE;
 
1880
}
 
1881
 
 
1882
 
 
1883
/**
 
1884
  Calculate binary size of packed datetime representation.
 
1885
  @param dec  Precision.
 
1886
*/
 
1887
uint my_datetime_binary_length(uint dec)
 
1888
{
 
1889
  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
 
1890
  return 5 + (dec + 1) / 2;
 
1891
}
 
1892
 
 
1893
 
 
1894
/*
 
1895
  On disk we store as unsigned number with DATETIMEF_INT_OFS offset,
 
1896
  for HA_KETYPE_BINARY compatibilty purposes.
 
1897
*/
 
1898
#define DATETIMEF_INT_OFS 0x8000000000LL
 
1899
 
 
1900
 
 
1901
/**
 
1902
  Convert on-disk datetime representation
 
1903
  to in-memory packed numeric representation.
 
1904
 
 
1905
  @param ptr   The pointer to read value at.
 
1906
  @param dec   Precision.
 
1907
  @return      In-memory packed numeric datetime representation.
 
1908
*/
 
1909
longlong my_datetime_packed_from_binary(const uchar *ptr, uint dec)
 
1910
{
 
1911
  longlong intpart= mi_uint5korr(ptr) - DATETIMEF_INT_OFS;
 
1912
  int frac;
 
1913
  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
 
1914
  switch (dec)
 
1915
  {
 
1916
  case 0:
 
1917
  default:
 
1918
    return MY_PACKED_TIME_MAKE_INT(intpart);
 
1919
  case 1:
 
1920
  case 2:
 
1921
    frac= ((int) (signed char) ptr[5]) * 10000;
 
1922
    break;
 
1923
  case 3:
 
1924
  case 4:
 
1925
    frac= mi_sint2korr(ptr + 5) * 100;
 
1926
    break;
 
1927
  case 5:
 
1928
  case 6:
 
1929
    frac= mi_sint3korr(ptr + 5);
 
1930
    break;
 
1931
  }
 
1932
  return MY_PACKED_TIME_MAKE(intpart, frac);
 
1933
}
 
1934
 
 
1935
 
 
1936
/**
 
1937
  Store in-memory numeric packed datetime representation to disk.
 
1938
 
 
1939
  @param      nr  In-memory numeric packed datetime representation.
 
1940
  @param OUT  ptr The pointer to store at.
 
1941
  @param      dec Precision, 1-6.
 
1942
*/
 
1943
void my_datetime_packed_to_binary(longlong nr, uchar *ptr, uint dec)
 
1944
{
 
1945
  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
 
1946
  /* The value being stored must have been properly rounded or truncated */
 
1947
  DBUG_ASSERT((MY_PACKED_TIME_GET_FRAC_PART(nr) %
 
1948
              (int) log_10_int[DATETIME_MAX_DECIMALS - dec]) == 0);
 
1949
 
 
1950
  mi_int5store(ptr, MY_PACKED_TIME_GET_INT_PART(nr) + DATETIMEF_INT_OFS);
 
1951
  switch (dec)
 
1952
  {
 
1953
  case 0:
 
1954
  default:
 
1955
    break;
 
1956
  case 1:
 
1957
  case 2:
 
1958
    ptr[5]= (unsigned char) (char) (MY_PACKED_TIME_GET_FRAC_PART(nr) / 10000);
 
1959
    break;
 
1960
  case 3:
 
1961
  case 4:
 
1962
    mi_int2store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr) / 100);
 
1963
    break;
 
1964
  case 5:
 
1965
  case 6:
 
1966
    mi_int3store(ptr + 5, MY_PACKED_TIME_GET_FRAC_PART(nr));
 
1967
  }
 
1968
}
 
1969
 
 
1970
 
 
1971
/*** TIMESTAMP low-level memory and disk representation routines ***/
 
1972
 
 
1973
/**
 
1974
  Calculate on-disk size of a timestamp value.
 
1975
 
 
1976
  @param  dec  Precision.
 
1977
*/
 
1978
uint my_timestamp_binary_length(uint dec)
 
1979
{
 
1980
  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
 
1981
  return 4 + (dec + 1) / 2;
 
1982
}
 
1983
 
 
1984
 
 
1985
/**
 
1986
  Convert binary timestamp representation to in-memory representation.
 
1987
 
 
1988
  @param  OUT tm  The variable to convert to.
 
1989
  @param      ptr The pointer to read the value from.
 
1990
  @param      dec Precision.
 
1991
*/
 
1992
void my_timestamp_from_binary(struct timeval *tm, const uchar *ptr, uint dec)
 
1993
{
 
1994
  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
 
1995
  tm->tv_sec= mi_uint4korr(ptr);
 
1996
  switch (dec)
 
1997
  {
 
1998
    case 0:
 
1999
    default:
 
2000
      tm->tv_usec= 0;
 
2001
      break;
 
2002
    case 1:
 
2003
    case 2:
 
2004
      tm->tv_usec= ((int) ptr[4]) * 10000;
 
2005
      break;
 
2006
    case 3:
 
2007
    case 4:
 
2008
      tm->tv_usec= mi_sint2korr(ptr + 4) * 100;
 
2009
      break;
 
2010
    case 5:
 
2011
    case 6:
 
2012
      tm->tv_usec= mi_sint3korr(ptr + 4);
 
2013
  }
 
2014
}
 
2015
 
 
2016
 
 
2017
/**
 
2018
  Convert in-memory timestamp representation to on-disk representation.
 
2019
 
 
2020
  @param        tm   The value to convert.
 
2021
  @param  OUT   ptr  The pointer to store the value to.
 
2022
  @param        dec  Precision.
 
2023
*/
 
2024
void my_timestamp_to_binary(const struct timeval *tm, uchar *ptr, uint dec)
 
2025
{
 
2026
  DBUG_ASSERT(dec <= DATETIME_MAX_DECIMALS);
 
2027
  /* Stored value must have been previously properly rounded or truncated */
 
2028
  DBUG_ASSERT((tm->tv_usec %
 
2029
               (int) log_10_int[DATETIME_MAX_DECIMALS - dec]) == 0);
 
2030
  mi_int4store(ptr, tm->tv_sec);
 
2031
  switch (dec)
 
2032
  {
 
2033
    case 0:
 
2034
    default:
 
2035
      break;
 
2036
    case 1:
 
2037
    case 2:
 
2038
      ptr[4]= (unsigned char) (char) (tm->tv_usec / 10000);
 
2039
      break;
 
2040
    case 3:
 
2041
    case 4:
 
2042
      mi_int2store(ptr + 4, tm->tv_usec / 100);
 
2043
      break;
 
2044
      /* Impossible second precision. Fall through */
 
2045
    case 5:
 
2046
    case 6:
 
2047
      mi_int3store(ptr + 4, tm->tv_usec);
 
2048
  }
 
2049
}
 
2050
 
 
2051
/****************************************/
 
2052
 
 
2053
 
 
2054
/**
 
2055
  Convert a temporal value to packed numeric temporal representation,
 
2056
  depending on its time_type.
 
2057
 
 
2058
  @ltime   The value to convert.
 
2059
  @return  Packed numeric time/date/datetime representation.
 
2060
*/
 
2061
longlong TIME_to_longlong_packed(const MYSQL_TIME *ltime)
 
2062
{
 
2063
  switch (ltime->time_type) {
 
2064
  case MYSQL_TIMESTAMP_DATE:
 
2065
    return TIME_to_longlong_date_packed(ltime);
 
2066
  case MYSQL_TIMESTAMP_DATETIME:
 
2067
    return TIME_to_longlong_datetime_packed(ltime);
 
2068
  case MYSQL_TIMESTAMP_TIME:
 
2069
    return TIME_to_longlong_time_packed(ltime);   
 
2070
  case MYSQL_TIMESTAMP_NONE:
 
2071
  case MYSQL_TIMESTAMP_ERROR:
 
2072
    return 0;
 
2073
  }
 
2074
  DBUG_ASSERT(0);
 
2075
  return 0;
 
2076
}
 
2077
 
 
2078
/*** End of low level format routines ***/