~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to sql/time.cc

Fixed depend problem

Show diffs side-by-side

added added

removed removed

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