~ubuntu-branches/ubuntu/precise/mysql-5.1/precise

« back to all changes in this revision

Viewing changes to sql/time.cc

  • Committer: Bazaar Package Importer
  • Author(s): Norbert Tretkowski
  • Date: 2010-03-17 14:56:02 UTC
  • Revision ID: james.westby@ubuntu.com-20100317145602-x7e30l1b2sb5s6w6
Tags: upstream-5.1.45
ImportĀ upstreamĀ versionĀ 5.1.45

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2000-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
 
 
16
 
 
17
/* Functions to handle date and time */
 
18
 
 
19
#include "mysql_priv.h"
 
20
#include <m_ctype.h>
 
21
 
 
22
 
 
23
        /* Some functions to calculate dates */
 
24
 
 
25
#ifndef TESTTIME
 
26
 
 
27
/*
 
28
  Name description of interval names used in statements.
 
29
 
 
30
  'interval_type_to_name' is ordered and sorted on interval size and
 
31
  interval complexity.
 
32
  Order of elements in 'interval_type_to_name' should correspond to 
 
33
  the order of elements in 'interval_type' enum
 
34
  
 
35
  See also interval_type, interval_names
 
36
*/
 
37
 
 
38
LEX_STRING interval_type_to_name[INTERVAL_LAST] = {
 
39
  { C_STRING_WITH_LEN("YEAR")},
 
40
  { C_STRING_WITH_LEN("QUARTER")},
 
41
  { C_STRING_WITH_LEN("MONTH")},
 
42
  { C_STRING_WITH_LEN("WEEK")},
 
43
  { C_STRING_WITH_LEN("DAY")},
 
44
  { C_STRING_WITH_LEN("HOUR")},
 
45
  { C_STRING_WITH_LEN("MINUTE")},
 
46
  { C_STRING_WITH_LEN("SECOND")},
 
47
  { C_STRING_WITH_LEN("MICROSECOND")},
 
48
  { C_STRING_WITH_LEN("YEAR_MONTH")},
 
49
  { C_STRING_WITH_LEN("DAY_HOUR")},
 
50
  { C_STRING_WITH_LEN("DAY_MINUTE")},
 
51
  { C_STRING_WITH_LEN("DAY_SECOND")},
 
52
  { C_STRING_WITH_LEN("HOUR_MINUTE")},
 
53
  { C_STRING_WITH_LEN("HOUR_SECOND")},
 
54
  { C_STRING_WITH_LEN("MINUTE_SECOND")},
 
55
  { C_STRING_WITH_LEN("DAY_MICROSECOND")},
 
56
  { C_STRING_WITH_LEN("HOUR_MICROSECOND")},
 
57
  { C_STRING_WITH_LEN("MINUTE_MICROSECOND")},
 
58
  { C_STRING_WITH_LEN("SECOND_MICROSECOND")}
 
59
}; 
 
60
 
 
61
        /* Calc weekday from daynr */
 
62
        /* Returns 0 for monday, 1 for tuesday .... */
 
63
 
 
64
int calc_weekday(long daynr,bool sunday_first_day_of_week)
 
65
{
 
66
  DBUG_ENTER("calc_weekday");
 
67
  DBUG_RETURN ((int) ((daynr + 5L + (sunday_first_day_of_week ? 1L : 0L)) % 7));
 
68
}
 
69
 
 
70
/*
 
71
  The bits in week_format has the following meaning:
 
72
   WEEK_MONDAY_FIRST (0)  If not set    Sunday is first day of week
 
73
                          If set        Monday is first day of week
 
74
   WEEK_YEAR (1)          If not set    Week is in range 0-53
 
75
 
 
76
        Week 0 is returned for the the last week of the previous year (for
 
77
        a date at start of january) In this case one can get 53 for the
 
78
        first week of next year.  This flag ensures that the week is
 
79
        relevant for the given year. Note that this flag is only
 
80
        releveant if WEEK_JANUARY is not set.
 
81
 
 
82
                          If set         Week is in range 1-53.
 
83
 
 
84
        In this case one may get week 53 for a date in January (when
 
85
        the week is that last week of previous year) and week 1 for a
 
86
        date in December.
 
87
 
 
88
  WEEK_FIRST_WEEKDAY (2)  If not set    Weeks are numbered according
 
89
                                        to ISO 8601:1988
 
90
                          If set        The week that contains the first
 
91
                                        'first-day-of-week' is week 1.
 
92
        
 
93
        ISO 8601:1988 means that if the week containing January 1 has
 
94
        four or more days in the new year, then it is week 1;
 
95
        Otherwise it is the last week of the previous year, and the
 
96
        next week is week 1.
 
97
*/
 
98
 
 
99
uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year)
 
100
{
 
101
  uint days;
 
102
  ulong daynr=calc_daynr(l_time->year,l_time->month,l_time->day);
 
103
  ulong first_daynr=calc_daynr(l_time->year,1,1);
 
104
  bool monday_first= test(week_behaviour & WEEK_MONDAY_FIRST);
 
105
  bool week_year= test(week_behaviour & WEEK_YEAR);
 
106
  bool first_weekday= test(week_behaviour & WEEK_FIRST_WEEKDAY);
 
107
 
 
108
  uint weekday=calc_weekday(first_daynr, !monday_first);
 
109
  *year=l_time->year;
 
110
 
 
111
  if (l_time->month == 1 && l_time->day <= 7-weekday)
 
112
  {
 
113
    if (!week_year && 
 
114
        ((first_weekday && weekday != 0) ||
 
115
         (!first_weekday && weekday >= 4)))
 
116
      return 0;
 
117
    week_year= 1;
 
118
    (*year)--;
 
119
    first_daynr-= (days=calc_days_in_year(*year));
 
120
    weekday= (weekday + 53*7- days) % 7;
 
121
  }
 
122
 
 
123
  if ((first_weekday && weekday != 0) ||
 
124
      (!first_weekday && weekday >= 4))
 
125
    days= daynr - (first_daynr+ (7-weekday));
 
126
  else
 
127
    days= daynr - (first_daynr - weekday);
 
128
 
 
129
  if (week_year && days >= 52*7)
 
130
  {
 
131
    weekday= (weekday + calc_days_in_year(*year)) % 7;
 
132
    if ((!first_weekday && weekday < 4) ||
 
133
        (first_weekday && weekday == 0))
 
134
    {
 
135
      (*year)++;
 
136
      return 1;
 
137
    }
 
138
  }
 
139
  return days/7+1;
 
140
}
 
141
 
 
142
        /* Change a daynr to year, month and day */
 
143
        /* Daynr 0 is returned as date 00.00.00 */
 
144
 
 
145
void get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month,
 
146
                         uint *ret_day)
 
147
{
 
148
  uint year,temp,leap_day,day_of_year,days_in_year;
 
149
  uchar *month_pos;
 
150
  DBUG_ENTER("get_date_from_daynr");
 
151
 
 
152
  if (daynr <= 365L || daynr >= 3652500)
 
153
  {                                             /* Fix if wrong daynr */
 
154
    *ret_year= *ret_month = *ret_day =0;
 
155
  }
 
156
  else
 
157
  {
 
158
    year= (uint) (daynr*100 / 36525L);
 
159
    temp=(((year-1)/100+1)*3)/4;
 
160
    day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp;
 
161
    while (day_of_year > (days_in_year= calc_days_in_year(year)))
 
162
    {
 
163
      day_of_year-=days_in_year;
 
164
      (year)++;
 
165
    }
 
166
    leap_day=0;
 
167
    if (days_in_year == 366)
 
168
    {
 
169
      if (day_of_year > 31+28)
 
170
      {
 
171
        day_of_year--;
 
172
        if (day_of_year == 31+28)
 
173
          leap_day=1;           /* Handle leapyears leapday */
 
174
      }
 
175
    }
 
176
    *ret_month=1;
 
177
    for (month_pos= days_in_month ;
 
178
         day_of_year > (uint) *month_pos ;
 
179
         day_of_year-= *(month_pos++), (*ret_month)++)
 
180
      ;
 
181
    *ret_year=year;
 
182
    *ret_day=day_of_year+leap_day;
 
183
  }
 
184
  DBUG_VOID_RETURN;
 
185
}
 
186
 
 
187
        /* Functions to handle periods */
 
188
 
 
189
ulong convert_period_to_month(ulong period)
 
190
{
 
191
  ulong a,b;
 
192
  if (period == 0)
 
193
    return 0L;
 
194
  if ((a=period/100) < YY_PART_YEAR)
 
195
    a+=2000;
 
196
  else if (a < 100)
 
197
    a+=1900;
 
198
  b=period%100;
 
199
  return a*12+b-1;
 
200
}
 
201
 
 
202
 
 
203
ulong convert_month_to_period(ulong month)
 
204
{
 
205
  ulong year;
 
206
  if (month == 0L)
 
207
    return 0L;
 
208
  if ((year=month/12) < 100)
 
209
  {
 
210
    year+=(year < YY_PART_YEAR) ? 2000 : 1900;
 
211
  }
 
212
  return year*100+month%12+1;
 
213
}
 
214
 
 
215
 
 
216
/*
 
217
  Convert a timestamp string to a MYSQL_TIME value and produce a warning 
 
218
  if string was truncated during conversion.
 
219
 
 
220
  NOTE
 
221
    See description of str_to_datetime() for more information.
 
222
*/
 
223
 
 
224
timestamp_type
 
225
str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time,
 
226
                          uint flags)
 
227
{
 
228
  int was_cut;
 
229
  THD *thd= current_thd;
 
230
  timestamp_type ts_type;
 
231
  
 
232
  ts_type= str_to_datetime(str, length, l_time,
 
233
                           (flags | (thd->variables.sql_mode &
 
234
                                     (MODE_INVALID_DATES |
 
235
                                      MODE_NO_ZERO_DATE))),
 
236
                           &was_cut);
 
237
  if (was_cut || ts_type <= MYSQL_TIMESTAMP_ERROR)
 
238
    make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
239
                                 str, length, ts_type,  NullS);
 
240
  return ts_type;
 
241
}
 
242
 
 
243
 
 
244
/*
 
245
  Convert a datetime from broken-down MYSQL_TIME representation to corresponding 
 
246
  TIMESTAMP value.
 
247
 
 
248
  SYNOPSIS
 
249
    TIME_to_timestamp()
 
250
      thd             - current thread
 
251
      t               - datetime in broken-down representation, 
 
252
      in_dst_time_gap - pointer to bool which is set to true if t represents
 
253
                        value which doesn't exists (falls into the spring 
 
254
                        time-gap) or to false otherwise.
 
255
   
 
256
  RETURN
 
257
     Number seconds in UTC since start of Unix Epoch corresponding to t.
 
258
     0 - t contains datetime value which is out of TIMESTAMP range.
 
259
     
 
260
*/
 
261
my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, my_bool *in_dst_time_gap)
 
262
{
 
263
  my_time_t timestamp;
 
264
 
 
265
  *in_dst_time_gap= 0;
 
266
  thd->time_zone_used= 1;
 
267
 
 
268
  timestamp= thd->variables.time_zone->TIME_to_gmt_sec(t, in_dst_time_gap);
 
269
  if (timestamp)
 
270
  {
 
271
    return timestamp;
 
272
  }
 
273
 
 
274
  /* If we are here we have range error. */
 
275
  return(0);
 
276
}
 
277
 
 
278
 
 
279
/*
 
280
  Convert a time string to a MYSQL_TIME struct and produce a warning
 
281
  if string was cut during conversion.
 
282
 
 
283
  NOTE
 
284
    See str_to_time() for more info.
 
285
*/
 
286
bool
 
287
str_to_time_with_warn(const char *str, uint length, MYSQL_TIME *l_time)
 
288
{
 
289
  int warning;
 
290
  bool ret_val= str_to_time(str, length, l_time, &warning);
 
291
  if (ret_val || warning)
 
292
    make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
293
                                 str, length, MYSQL_TIMESTAMP_TIME, NullS);
 
294
  return ret_val;
 
295
}
 
296
 
 
297
 
 
298
/*
 
299
  Convert a system time structure to TIME
 
300
*/
 
301
 
 
302
void localtime_to_TIME(MYSQL_TIME *to, struct tm *from)
 
303
{
 
304
  to->neg=0;
 
305
  to->second_part=0;
 
306
  to->year=     (int) ((from->tm_year+1900) % 10000);
 
307
  to->month=    (int) from->tm_mon+1;
 
308
  to->day=      (int) from->tm_mday;
 
309
  to->hour=     (int) from->tm_hour;
 
310
  to->minute=   (int) from->tm_min;
 
311
  to->second=   (int) from->tm_sec;
 
312
}
 
313
 
 
314
void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds)
 
315
{
 
316
  long t_seconds;
 
317
  // to->neg is not cleared, it may already be set to a useful value
 
318
  to->time_type= MYSQL_TIMESTAMP_TIME;
 
319
  to->year= 0;
 
320
  to->month= 0;
 
321
  to->day= 0;
 
322
  to->hour= seconds/3600L;
 
323
  t_seconds= seconds%3600L;
 
324
  to->minute= t_seconds/60L;
 
325
  to->second= t_seconds%60L;
 
326
  to->second_part= microseconds;
 
327
}
 
328
 
 
329
 
 
330
/*
 
331
  Parse a format string specification
 
332
 
 
333
  SYNOPSIS
 
334
    parse_date_time_format()
 
335
    format_type         Format of string (time, date or datetime)
 
336
    format_str          String to parse
 
337
    format_length       Length of string
 
338
    date_time_format    Format to fill in
 
339
 
 
340
  NOTES
 
341
    Fills in date_time_format->positions for all date time parts.
 
342
 
 
343
    positions marks the position for a datetime element in the format string.
 
344
    The position array elements are in the following order:
 
345
    YYYY-DD-MM HH-MM-DD.FFFFFF AM
 
346
    0    1  2  3  4  5  6      7
 
347
 
 
348
    If positions[0]= 5, it means that year will be the forth element to
 
349
    read from the parsed date string.
 
350
 
 
351
  RETURN
 
352
    0   ok
 
353
    1   error
 
354
*/
 
355
 
 
356
bool parse_date_time_format(timestamp_type format_type, 
 
357
                            const char *format, uint format_length,
 
358
                            DATE_TIME_FORMAT *date_time_format)
 
359
{
 
360
  uint offset= 0, separators= 0;
 
361
  const char *ptr= format, *format_str;
 
362
  const char *end= ptr+format_length;
 
363
  uchar *dt_pos= date_time_format->positions;
 
364
  /* need_p is set if we are using AM/PM format */
 
365
  bool need_p= 0, allow_separator= 0;
 
366
  ulong part_map= 0, separator_map= 0;
 
367
  const char *parts[16];
 
368
 
 
369
  date_time_format->time_separator= 0;
 
370
  date_time_format->flag= 0;                    // For future
 
371
 
 
372
  /*
 
373
    Fill position with 'dummy' arguments to found out if a format tag is
 
374
    used twice (This limit's the format to 255 characters, but this is ok)
 
375
  */
 
376
  dt_pos[0]= dt_pos[1]= dt_pos[2]= dt_pos[3]=
 
377
    dt_pos[4]= dt_pos[5]= dt_pos[6]= dt_pos[7]= 255;
 
378
 
 
379
  for (; ptr != end; ptr++)
 
380
  {
 
381
    if (*ptr == '%' && ptr+1 != end)
 
382
    {
 
383
      uint position;
 
384
      LINT_INIT(position);
 
385
      switch (*++ptr) {
 
386
      case 'y':                                 // Year
 
387
      case 'Y':
 
388
        position= 0;
 
389
        break;
 
390
      case 'c':                                 // Month
 
391
      case 'm':
 
392
        position= 1;
 
393
        break;
 
394
      case 'd':
 
395
      case 'e':
 
396
        position= 2;
 
397
        break;
 
398
      case 'h':
 
399
      case 'I':
 
400
      case 'l':
 
401
        need_p= 1;                              // Need AM/PM
 
402
        /* Fall through */
 
403
      case 'k':
 
404
      case 'H':
 
405
        position= 3;
 
406
        break;
 
407
      case 'i':
 
408
        position= 4;
 
409
        break;
 
410
      case 's':
 
411
      case 'S':
 
412
        position= 5;
 
413
        break;
 
414
      case 'f':
 
415
        position= 6;
 
416
        if (dt_pos[5] != offset-1 || ptr[-2] != '.')
 
417
          return 1;                             // Wrong usage of %f
 
418
        break;
 
419
      case 'p':                                 // AM/PM
 
420
        if (offset == 0)                        // Can't be first
 
421
          return 0;
 
422
        position= 7;
 
423
        break;
 
424
      default:
 
425
        return 1;                               // Unknown controll char
 
426
      }
 
427
      if (dt_pos[position] != 255)              // Don't allow same tag twice
 
428
        return 1;
 
429
      parts[position]= ptr-1;
 
430
 
 
431
      /*
 
432
        If switching from time to date, ensure that all time parts
 
433
        are used
 
434
      */
 
435
      if (part_map && position <= 2 && !(part_map & (1 | 2 | 4)))
 
436
        offset=5;
 
437
      part_map|= (ulong) 1 << position;
 
438
      dt_pos[position]= offset++;
 
439
      allow_separator= 1;
 
440
    }
 
441
    else
 
442
    {
 
443
      /*
 
444
        Don't allow any characters in format as this could easily confuse
 
445
        the date reader
 
446
      */
 
447
      if (!allow_separator)
 
448
        return 1;                               // No separator here
 
449
      allow_separator= 0;                       // Don't allow two separators
 
450
      separators++;
 
451
      /* Store in separator_map which parts are punct characters */
 
452
      if (my_ispunct(&my_charset_latin1, *ptr))
 
453
        separator_map|= (ulong) 1 << (offset-1);
 
454
      else if (!my_isspace(&my_charset_latin1, *ptr))
 
455
        return 1;
 
456
    }
 
457
  }
 
458
 
 
459
  /* If no %f, specify it after seconds.  Move %p up, if necessary */
 
460
  if ((part_map & 32) && !(part_map & 64))
 
461
  {
 
462
    dt_pos[6]= dt_pos[5] +1;
 
463
    parts[6]= parts[5];                         // For later test in (need_p)
 
464
    if (dt_pos[6] == dt_pos[7])                 // Move %p one step up if used
 
465
      dt_pos[7]++;
 
466
  }
 
467
 
 
468
  /*
 
469
    Check that we have not used a non legal format specifier and that all
 
470
    format specifiers have been used
 
471
 
 
472
    The last test is to ensure that %p is used if and only if
 
473
    it's needed.
 
474
  */
 
475
  if ((format_type == MYSQL_TIMESTAMP_DATETIME &&
 
476
       !test_all_bits(part_map, (1 | 2 | 4 | 8 | 16 | 32))) ||
 
477
      (format_type == MYSQL_TIMESTAMP_DATE && part_map != (1 | 2 | 4)) ||
 
478
      (format_type == MYSQL_TIMESTAMP_TIME &&
 
479
       !test_all_bits(part_map, 8 | 16 | 32)) ||
 
480
      !allow_separator ||                       // %option should be last
 
481
      (need_p && dt_pos[6] +1 != dt_pos[7]) ||
 
482
      (need_p ^ (dt_pos[7] != 255)))
 
483
    return 1;
 
484
 
 
485
  if (dt_pos[6] != 255)                         // If fractional seconds
 
486
  {
 
487
    /* remove fractional seconds from later tests */
 
488
    uint pos= dt_pos[6] -1;
 
489
    /* Remove separator before %f from sep map */
 
490
    separator_map= ((separator_map & ((ulong) (1 << pos)-1)) |
 
491
                    ((separator_map & ~((ulong) (1 << pos)-1)) >> 1));
 
492
    if (part_map & 64)                        
 
493
    {
 
494
      separators--;                             // There is always a separator
 
495
      need_p= 1;                                // force use of separators
 
496
    }
 
497
  }
 
498
 
 
499
  /*
 
500
    Remove possible separator before %p from sep_map
 
501
    (This can either be at position 3, 4, 6 or 7) h.m.d.%f %p
 
502
  */
 
503
  if (dt_pos[7] != 255)
 
504
  {
 
505
    if (need_p && parts[7] != parts[6]+2)
 
506
      separators--;
 
507
  }     
 
508
  /*
 
509
    Calculate if %p is in first or last part of the datetime field
 
510
 
 
511
    At this point we have either %H-%i-%s %p 'year parts' or
 
512
    'year parts' &H-%i-%s %p" as %f was removed above
 
513
  */
 
514
  offset= dt_pos[6] <= 3 ? 3 : 6;
 
515
  /* Remove separator before %p from sep map */
 
516
  separator_map= ((separator_map & ((ulong) (1 << offset)-1)) |
 
517
                  ((separator_map & ~((ulong) (1 << offset)-1)) >> 1));
 
518
 
 
519
  format_str= 0;
 
520
  switch (format_type) {
 
521
  case MYSQL_TIMESTAMP_DATE:
 
522
    format_str= known_date_time_formats[INTERNAL_FORMAT].date_format;
 
523
    /* fall through */
 
524
  case MYSQL_TIMESTAMP_TIME:
 
525
    if (!format_str)
 
526
      format_str=known_date_time_formats[INTERNAL_FORMAT].time_format;
 
527
 
 
528
    /*
 
529
      If there is no separators, allow the internal format as we can read
 
530
      this.  If separators are used, they must be between each part
 
531
    */
 
532
    if (format_length == 6 && !need_p &&
 
533
        !my_strnncoll(&my_charset_bin,
 
534
                      (const uchar *) format, 6, 
 
535
                      (const uchar *) format_str, 6))
 
536
      return 0;
 
537
    if (separator_map == (1 | 2))
 
538
    {
 
539
      if (format_type == MYSQL_TIMESTAMP_TIME)
 
540
      {
 
541
        if (*(format+2) != *(format+5))
 
542
          break;                                // Error
 
543
        /* Store the character used for time formats */
 
544
        date_time_format->time_separator= *(format+2);
 
545
      }
 
546
      return 0;
 
547
    }
 
548
    break;
 
549
  case MYSQL_TIMESTAMP_DATETIME:
 
550
    /*
 
551
      If there is no separators, allow the internal format as we can read
 
552
      this.  If separators are used, they must be between each part.
 
553
      Between DATE and TIME we also allow space as separator
 
554
    */
 
555
    if ((format_length == 12 && !need_p &&
 
556
         !my_strnncoll(&my_charset_bin, 
 
557
                       (const uchar *) format, 12,
 
558
                       (const uchar*) known_date_time_formats[INTERNAL_FORMAT].datetime_format,
 
559
                       12)) ||
 
560
        (separators == 5 && separator_map == (1 | 2 | 8 | 16)))
 
561
      return 0;
 
562
    break;
 
563
  default:
 
564
    DBUG_ASSERT(1);
 
565
    break;
 
566
  }
 
567
  return 1;                                     // Error
 
568
}
 
569
 
 
570
 
 
571
/*
 
572
  Create a DATE_TIME_FORMAT object from a format string specification
 
573
 
 
574
  SYNOPSIS
 
575
    date_time_format_make()
 
576
    format_type         Format to parse (time, date or datetime)
 
577
    format_str          String to parse
 
578
    format_length       Length of string
 
579
 
 
580
  NOTES
 
581
    The returned object should be freed with my_free()
 
582
 
 
583
  RETURN
 
584
    NULL ponter:        Error
 
585
    new object
 
586
*/
 
587
 
 
588
DATE_TIME_FORMAT
 
589
*date_time_format_make(timestamp_type format_type,
 
590
                       const char *format_str, uint format_length)
 
591
{
 
592
  DATE_TIME_FORMAT tmp;
 
593
 
 
594
  if (format_length && format_length < 255 &&
 
595
      !parse_date_time_format(format_type, format_str,
 
596
                              format_length, &tmp))
 
597
  {
 
598
    tmp.format.str=    (char*) format_str;
 
599
    tmp.format.length= format_length;
 
600
    return date_time_format_copy((THD *)0, &tmp);
 
601
  }
 
602
  return 0;
 
603
}
 
604
 
 
605
 
 
606
/*
 
607
  Create a copy of a DATE_TIME_FORMAT object
 
608
 
 
609
  SYNOPSIS
 
610
    date_and_time_format_copy()
 
611
    thd                 Set if variable should be allocated in thread mem
 
612
    format              format to copy
 
613
 
 
614
  NOTES
 
615
    The returned object should be freed with my_free()
 
616
 
 
617
  RETURN
 
618
    NULL ponter:        Error
 
619
    new object
 
620
*/
 
621
 
 
622
DATE_TIME_FORMAT *date_time_format_copy(THD *thd, DATE_TIME_FORMAT *format)
 
623
{
 
624
  DATE_TIME_FORMAT *new_format;
 
625
  ulong length= sizeof(*format) + format->format.length + 1;
 
626
 
 
627
  if (thd)
 
628
    new_format= (DATE_TIME_FORMAT *) thd->alloc(length);
 
629
  else
 
630
    new_format=  (DATE_TIME_FORMAT *) my_malloc(length, MYF(MY_WME));
 
631
  if (new_format)
 
632
  {
 
633
    /* Put format string after current pos */
 
634
    new_format->format.str= (char*) (new_format+1);
 
635
    memcpy((char*) new_format->positions, (char*) format->positions,
 
636
           sizeof(format->positions));
 
637
    new_format->time_separator= format->time_separator;
 
638
    /* We make the string null terminated for easy printf in SHOW VARIABLES */
 
639
    memcpy((char*) new_format->format.str, format->format.str,
 
640
           format->format.length);
 
641
    new_format->format.str[format->format.length]= 0;
 
642
    new_format->format.length= format->format.length;
 
643
  }
 
644
  return new_format;
 
645
}
 
646
 
 
647
 
 
648
KNOWN_DATE_TIME_FORMAT known_date_time_formats[6]=
 
649
{
 
650
  {"USA", "%m.%d.%Y", "%Y-%m-%d %H.%i.%s", "%h:%i:%s %p" },
 
651
  {"JIS", "%Y-%m-%d", "%Y-%m-%d %H:%i:%s", "%H:%i:%s" },
 
652
  {"ISO", "%Y-%m-%d", "%Y-%m-%d %H:%i:%s", "%H:%i:%s" },
 
653
  {"EUR", "%d.%m.%Y", "%Y-%m-%d %H.%i.%s", "%H.%i.%s" },
 
654
  {"INTERNAL", "%Y%m%d",   "%Y%m%d%H%i%s", "%H%i%s" },
 
655
  { 0, 0, 0, 0 }
 
656
};
 
657
 
 
658
 
 
659
/*
 
660
   Return format string according format name.
 
661
   If name is unknown, result is NULL
 
662
*/
 
663
 
 
664
const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
 
665
                                     timestamp_type type)
 
666
{
 
667
  switch (type) {
 
668
  case MYSQL_TIMESTAMP_DATE:
 
669
    return format->date_format;
 
670
  case MYSQL_TIMESTAMP_DATETIME:
 
671
    return format->datetime_format;
 
672
  case MYSQL_TIMESTAMP_TIME:
 
673
    return format->time_format;
 
674
  default:
 
675
    DBUG_ASSERT(0);                             // Impossible
 
676
    return 0;
 
677
  }
 
678
}
 
679
 
 
680
/****************************************************************************
 
681
  Functions to create default time/date/datetime strings
 
682
 
 
683
  NOTE:
 
684
    For the moment the DATE_TIME_FORMAT argument is ignored becasue
 
685
    MySQL doesn't support comparing of date/time/datetime strings that
 
686
    are not in arbutary order as dates are compared as strings in some
 
687
    context)
 
688
    This functions don't check that given MYSQL_TIME structure members are
 
689
    in valid range. If they are not, return value won't reflect any 
 
690
    valid date either. Additionally, make_time doesn't take into
 
691
    account time->day member: it's assumed that days have been converted
 
692
    to hours already.
 
693
****************************************************************************/
 
694
 
 
695
void make_time(const DATE_TIME_FORMAT *format __attribute__((unused)),
 
696
               const MYSQL_TIME *l_time, String *str)
 
697
{
 
698
  uint length= (uint) my_time_to_str(l_time, (char*) str->ptr());
 
699
  str->length(length);
 
700
  str->set_charset(&my_charset_bin);
 
701
}
 
702
 
 
703
 
 
704
void make_date(const DATE_TIME_FORMAT *format __attribute__((unused)),
 
705
               const MYSQL_TIME *l_time, String *str)
 
706
{
 
707
  uint length= (uint) my_date_to_str(l_time, (char*) str->ptr());
 
708
  str->length(length);
 
709
  str->set_charset(&my_charset_bin);
 
710
}
 
711
 
 
712
 
 
713
void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)),
 
714
                   const MYSQL_TIME *l_time, String *str)
 
715
{
 
716
  uint length= (uint) my_datetime_to_str(l_time, (char*) str->ptr());
 
717
  str->length(length);
 
718
  str->set_charset(&my_charset_bin);
 
719
}
 
720
 
 
721
 
 
722
void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
 
723
                                  const char *str_val,
 
724
                                  uint str_length, timestamp_type time_type,
 
725
                                  const char *field_name)
 
726
{
 
727
  char warn_buff[MYSQL_ERRMSG_SIZE];
 
728
  const char *type_str;
 
729
  CHARSET_INFO *cs= &my_charset_latin1;
 
730
  char buff[128];
 
731
  String str(buff,(uint32) sizeof(buff), system_charset_info);
 
732
  str.copy(str_val, str_length, system_charset_info);
 
733
  str[str_length]= 0;               // Ensure we have end 0 for snprintf
 
734
 
 
735
  switch (time_type) {
 
736
    case MYSQL_TIMESTAMP_DATE: 
 
737
      type_str= "date";
 
738
      break;
 
739
    case MYSQL_TIMESTAMP_TIME:
 
740
      type_str= "time";
 
741
      break;
 
742
    case MYSQL_TIMESTAMP_DATETIME:  // FALLTHROUGH
 
743
    default:
 
744
      type_str= "datetime";
 
745
      break;
 
746
  }
 
747
  if (field_name)
 
748
    cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
 
749
                       ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
 
750
                       type_str, str.c_ptr(), field_name,
 
751
                       (ulong) thd->row_count);
 
752
  else
 
753
  {
 
754
    if (time_type > MYSQL_TIMESTAMP_ERROR)
 
755
      cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
 
756
                         ER(ER_TRUNCATED_WRONG_VALUE),
 
757
                         type_str, str.c_ptr());
 
758
    else
 
759
      cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
 
760
                         ER(ER_WRONG_VALUE), type_str, str.c_ptr());
 
761
  }
 
762
  push_warning(thd, level,
 
763
               ER_TRUNCATED_WRONG_VALUE, warn_buff);
 
764
}
 
765
 
 
766
/* Daynumber from year 0 to 9999-12-31 */
 
767
#define MAX_DAY_NUMBER 3652424L
 
768
 
 
769
bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL interval)
 
770
{
 
771
  long period, sign;
 
772
 
 
773
  ltime->neg= 0;
 
774
 
 
775
  sign= (interval.neg ? -1 : 1);
 
776
 
 
777
  switch (int_type) {
 
778
  case INTERVAL_SECOND:
 
779
  case INTERVAL_SECOND_MICROSECOND:
 
780
  case INTERVAL_MICROSECOND:
 
781
  case INTERVAL_MINUTE:
 
782
  case INTERVAL_HOUR:
 
783
  case INTERVAL_MINUTE_MICROSECOND:
 
784
  case INTERVAL_MINUTE_SECOND:
 
785
  case INTERVAL_HOUR_MICROSECOND:
 
786
  case INTERVAL_HOUR_SECOND:
 
787
  case INTERVAL_HOUR_MINUTE:
 
788
  case INTERVAL_DAY_MICROSECOND:
 
789
  case INTERVAL_DAY_SECOND:
 
790
  case INTERVAL_DAY_MINUTE:
 
791
  case INTERVAL_DAY_HOUR:
 
792
  {
 
793
    longlong sec, days, daynr, microseconds, extra_sec;
 
794
    ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date
 
795
    microseconds= ltime->second_part + sign*interval.second_part;
 
796
    extra_sec= microseconds/1000000L;
 
797
    microseconds= microseconds%1000000L;
 
798
 
 
799
    sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
 
800
         ltime->second +
 
801
         sign* (longlong) (interval.day*3600*24L +
 
802
                           interval.hour*LL(3600)+interval.minute*LL(60)+
 
803
                           interval.second))+ extra_sec;
 
804
    if (microseconds < 0)
 
805
    {
 
806
      microseconds+= LL(1000000);
 
807
      sec--;
 
808
    }
 
809
    days= sec/(3600*LL(24));
 
810
    sec-= days*3600*LL(24);
 
811
    if (sec < 0)
 
812
    {
 
813
      days--;
 
814
      sec+= 3600*LL(24);
 
815
    }
 
816
    ltime->second_part= (uint) microseconds;
 
817
    ltime->second= (uint) (sec % 60);
 
818
    ltime->minute= (uint) (sec/60 % 60);
 
819
    ltime->hour=   (uint) (sec/3600);
 
820
    daynr= calc_daynr(ltime->year,ltime->month,1) + days;
 
821
    /* Day number from year 0 to 9999-12-31 */
 
822
    if ((ulonglong) daynr > MAX_DAY_NUMBER)
 
823
      goto invalid_date;
 
824
    get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
 
825
                        &ltime->day);
 
826
    break;
 
827
  }
 
828
  case INTERVAL_DAY:
 
829
  case INTERVAL_WEEK:
 
830
    period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
 
831
             sign * (long) interval.day);
 
832
    /* Daynumber from year 0 to 9999-12-31 */
 
833
    if ((ulong) period > MAX_DAY_NUMBER)
 
834
      goto invalid_date;
 
835
    get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
 
836
    break;
 
837
  case INTERVAL_YEAR:
 
838
    ltime->year+= sign * (long) interval.year;
 
839
    if ((ulong) ltime->year >= 10000L)
 
840
      goto invalid_date;
 
841
    if (ltime->month == 2 && ltime->day == 29 &&
 
842
        calc_days_in_year(ltime->year) != 366)
 
843
      ltime->day=28;                            // Was leap-year
 
844
    break;
 
845
  case INTERVAL_YEAR_MONTH:
 
846
  case INTERVAL_QUARTER:
 
847
  case INTERVAL_MONTH:
 
848
    period= (ltime->year*12 + sign * (long) interval.year*12 +
 
849
             ltime->month-1 + sign * (long) interval.month);
 
850
    if ((ulong) period >= 120000L)
 
851
      goto invalid_date;
 
852
    ltime->year= (uint) (period / 12);
 
853
    ltime->month= (uint) (period % 12L)+1;
 
854
    /* Adjust day if the new month doesn't have enough days */
 
855
    if (ltime->day > days_in_month[ltime->month-1])
 
856
    {
 
857
      ltime->day = days_in_month[ltime->month-1];
 
858
      if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
 
859
        ltime->day++;                           // Leap-year
 
860
    }
 
861
    break;
 
862
  default:
 
863
    goto null_date;
 
864
  }
 
865
 
 
866
  return 0;                                     // Ok
 
867
 
 
868
invalid_date:
 
869
  push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
870
                      ER_DATETIME_FUNCTION_OVERFLOW,
 
871
                      ER(ER_DATETIME_FUNCTION_OVERFLOW),
 
872
                      "datetime");
 
873
null_date:
 
874
  return 1;
 
875
}
 
876
 
 
877
 
 
878
/*
 
879
  Calculate difference between two datetime values as seconds + microseconds.
 
880
 
 
881
  SYNOPSIS
 
882
    calc_time_diff()
 
883
      l_time1         - TIME/DATE/DATETIME value
 
884
      l_time2         - TIME/DATE/DATETIME value
 
885
      l_sign          - 1 absolute values are substracted,
 
886
                        -1 absolute values are added.
 
887
      seconds_out     - Out parameter where difference between
 
888
                        l_time1 and l_time2 in seconds is stored.
 
889
      microseconds_out- Out parameter where microsecond part of difference
 
890
                        between l_time1 and l_time2 is stored.
 
891
 
 
892
  NOTE
 
893
    This function calculates difference between l_time1 and l_time2 absolute
 
894
    values. So one should set l_sign and correct result if he want to take
 
895
    signs into account (i.e. for MYSQL_TIME values).
 
896
 
 
897
  RETURN VALUES
 
898
    Returns sign of difference.
 
899
    1 means negative result
 
900
    0 means positive result
 
901
 
 
902
*/
 
903
 
 
904
bool
 
905
calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *seconds_out,
 
906
               long *microseconds_out)
 
907
{
 
908
  long days;
 
909
  bool neg;
 
910
  longlong microseconds;
 
911
 
 
912
  /*
 
913
    We suppose that if first argument is MYSQL_TIMESTAMP_TIME
 
914
    the second argument should be TIMESTAMP_TIME also.
 
915
    We should check it before calc_time_diff call.
 
916
  */
 
917
  if (l_time1->time_type == MYSQL_TIMESTAMP_TIME)  // Time value
 
918
    days= (long)l_time1->day - l_sign * (long)l_time2->day;
 
919
  else
 
920
  {
 
921
    days= calc_daynr((uint) l_time1->year,
 
922
                     (uint) l_time1->month,
 
923
                     (uint) l_time1->day);
 
924
    if (l_time2->time_type == MYSQL_TIMESTAMP_TIME)
 
925
      days-= l_sign * (long)l_time2->day;
 
926
    else
 
927
      days-= l_sign*calc_daynr((uint) l_time2->year,
 
928
                               (uint) l_time2->month,
 
929
                               (uint) l_time2->day);
 
930
  }
 
931
 
 
932
  microseconds= ((longlong)days*LL(86400) +
 
933
                 (longlong)(l_time1->hour*3600L +
 
934
                            l_time1->minute*60L +
 
935
                            l_time1->second) -
 
936
                 l_sign*(longlong)(l_time2->hour*3600L +
 
937
                                   l_time2->minute*60L +
 
938
                                   l_time2->second)) * LL(1000000) +
 
939
                (longlong)l_time1->second_part -
 
940
                l_sign*(longlong)l_time2->second_part;
 
941
 
 
942
  neg= 0;
 
943
  if (microseconds < 0)
 
944
  {
 
945
    microseconds= -microseconds;
 
946
    neg= 1;
 
947
  }
 
948
  *seconds_out= microseconds/1000000L;
 
949
  *microseconds_out= (long) (microseconds%1000000L);
 
950
  return neg;
 
951
}
 
952
 
 
953
 
 
954
/*
 
955
  Compares 2 MYSQL_TIME structures
 
956
 
 
957
  SYNOPSIS
 
958
    my_time_compare()
 
959
 
 
960
      a - first time
 
961
      b - second time
 
962
 
 
963
  RETURN VALUE
 
964
   -1   - a < b
 
965
    0   - a == b
 
966
    1   - a > b
 
967
 
 
968
*/
 
969
 
 
970
int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b)
 
971
{
 
972
  ulonglong a_t= TIME_to_ulonglong_datetime(a);
 
973
  ulonglong b_t= TIME_to_ulonglong_datetime(b);
 
974
 
 
975
  if (a_t < b_t)
 
976
    return -1;
 
977
  if (a_t > b_t)
 
978
    return 1;
 
979
 
 
980
  if (a->second_part < b->second_part)
 
981
    return -1;
 
982
  if (a->second_part > b->second_part)
 
983
    return 1;
 
984
 
 
985
  return 0;
 
986
}
 
987
 
 
988
#endif