~drizzle-trunk/drizzle/development

1 by brian
clean slate
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) && ((first_weekday && weekday != 0) || (!first_weekday && weekday >= 4)))
114
      return 0;
115
    week_year= 1;
116
    (*year)--;
117
    first_daynr-= (days=calc_days_in_year(*year));
118
    weekday= (weekday + 53*7- days) % 7;
119
  }
120
121
  if ((first_weekday && weekday != 0) ||
122
      (!first_weekday && weekday >= 4))
123
    days= daynr - (first_daynr+ (7-weekday));
124
  else
125
    days= daynr - (first_daynr - weekday);
126
127
  if (week_year && days >= 52*7)
128
  {
129
    weekday= (weekday + calc_days_in_year(*year)) % 7;
130
    if ((!first_weekday && weekday < 4) || (first_weekday && weekday == 0))
131
    {
132
      (*year)++;
133
      return 1;
134
    }
135
  }
136
  return days/7+1;
137
}
138
139
	/* Change a daynr to year, month and day */
140
	/* Daynr 0 is returned as date 00.00.00 */
141
142
void get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month,
143
			 uint *ret_day)
144
{
145
  uint year,temp,leap_day,day_of_year,days_in_year;
146
  uchar *month_pos;
147
  DBUG_ENTER("get_date_from_daynr");
148
149
  if (daynr <= 365L || daynr >= 3652500)
150
  {						/* Fix if wrong daynr */
151
    *ret_year= *ret_month = *ret_day =0;
152
  }
153
  else
154
  {
155
    year= (uint) (daynr*100 / 36525L);
156
    temp=(((year-1)/100+1)*3)/4;
157
    day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp;
158
    while (day_of_year > (days_in_year= calc_days_in_year(year)))
159
    {
160
      day_of_year-=days_in_year;
161
      (year)++;
162
    }
163
    leap_day=0;
164
    if (days_in_year == 366)
165
    {
166
      if (day_of_year > 31+28)
167
      {
168
	day_of_year--;
169
	if (day_of_year == 31+28)
170
	  leap_day=1;		/* Handle leapyears leapday */
171
      }
172
    }
173
    *ret_month=1;
174
    for (month_pos= days_in_month ;
175
	 day_of_year > (uint) *month_pos ;
176
	 day_of_year-= *(month_pos++), (*ret_month)++)
177
      ;
178
    *ret_year=year;
179
    *ret_day=day_of_year+leap_day;
180
  }
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
212
213
/*
214
  Convert a timestamp string to a MYSQL_TIME value and produce a warning 
215
  if string was truncated during conversion.
216
217
  NOTE
218
    See description of str_to_datetime() for more information.
219
*/
220
221
timestamp_type
222
str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time,
223
                          uint flags)
224
{
225
  int was_cut;
226
  THD *thd= current_thd;
227
  timestamp_type ts_type;
228
  
229
  ts_type= str_to_datetime(str, length, l_time,
230
                           (flags | (thd->variables.sql_mode &
231
                                     (MODE_INVALID_DATES |
232
                                      MODE_NO_ZERO_DATE))),
233
                           &was_cut);
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);
237
  return ts_type;
238
}
239
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
278
  if string was cut during conversion.
279
280
  NOTE
281
    See str_to_time() for more info.
282
*/
283
bool
284
str_to_time_with_warn(const char *str, uint length, MYSQL_TIME *l_time)
285
{
286
  int warning;
287
  bool ret_val= str_to_time(str, length, l_time, &warning);
288
  if (ret_val || warning)
289
    make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
290
                                 str, length, MYSQL_TIMESTAMP_TIME, NullS);
291
  return ret_val;
292
}
293
294
295
/*
296
  Convert a system time structure to TIME
297
*/
298
299
void localtime_to_TIME(MYSQL_TIME *to, struct tm *from)
300
{
301
  to->neg=0;
302
  to->second_part=0;
303
  to->year=	(int) ((from->tm_year+1900) % 10000);
304
  to->month=	(int) from->tm_mon+1;
305
  to->day=	(int) from->tm_mday;
306
  to->hour=	(int) from->tm_hour;
307
  to->minute=	(int) from->tm_min;
308
  to->second=   (int) from->tm_sec;
309
}
310
311
void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds)
312
{
313
  long t_seconds;
314
  // to->neg is not cleared, it may already be set to a useful value
315
  to->time_type= MYSQL_TIMESTAMP_TIME;
316
  to->year= 0;
317
  to->month= 0;
318
  to->day= 0;
319
  to->hour= seconds/3600L;
320
  t_seconds= seconds%3600L;
321
  to->minute= t_seconds/60L;
322
  to->second= t_seconds%60L;
323
  to->second_part= microseconds;
324
}
325
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,
719
                                  const char *str_val,
720
				  uint str_length, timestamp_type time_type,
721
                                  const char *field_name)
722
{
723
  char warn_buff[MYSQL_ERRMSG_SIZE];
724
  const char *type_str;
725
  CHARSET_INFO *cs= &my_charset_latin1;
726
  char buff[128];
727
  String str(buff,(uint32) sizeof(buff), system_charset_info);
728
  str.copy(str_val, str_length, system_charset_info);
729
  str[str_length]= 0;               // Ensure we have end 0 for snprintf
730
731
  switch (time_type) {
732
    case MYSQL_TIMESTAMP_DATE: 
733
      type_str= "date";
734
      break;
735
    case MYSQL_TIMESTAMP_TIME:
736
      type_str= "time";
737
      break;
738
    case MYSQL_TIMESTAMP_DATETIME:  // FALLTHROUGH
739
    default:
740
      type_str= "datetime";
741
      break;
742
  }
743
  if (field_name)
744
    cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
745
                       ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
746
                       type_str, str.c_ptr(), field_name,
747
                       (ulong) thd->row_count);
748
  else
749
  {
750
    if (time_type > MYSQL_TIMESTAMP_ERROR)
751
      cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
752
                         ER(ER_TRUNCATED_WRONG_VALUE),
753
                         type_str, str.c_ptr());
754
    else
755
      cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
756
                         ER(ER_WRONG_VALUE), type_str, str.c_ptr());
757
  }
758
  push_warning(thd, level,
759
               ER_TRUNCATED_WRONG_VALUE, warn_buff);
760
}
761
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)
766
{
767
  long period, sign;
768
769
  ltime->neg= 0;
770
771
  sign= (interval.neg ? -1 : 1);
772
773
  switch (int_type) {
774
  case INTERVAL_SECOND:
775
  case INTERVAL_SECOND_MICROSECOND:
776
  case INTERVAL_MICROSECOND:
777
  case INTERVAL_MINUTE:
778
  case INTERVAL_HOUR:
779
  case INTERVAL_MINUTE_MICROSECOND:
780
  case INTERVAL_MINUTE_SECOND:
781
  case INTERVAL_HOUR_MICROSECOND:
782
  case INTERVAL_HOUR_SECOND:
783
  case INTERVAL_HOUR_MINUTE:
784
  case INTERVAL_DAY_MICROSECOND:
785
  case INTERVAL_DAY_SECOND:
786
  case INTERVAL_DAY_MINUTE:
787
  case INTERVAL_DAY_HOUR:
788
  {
789
    longlong sec, days, daynr, microseconds, extra_sec;
790
    ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date
791
    microseconds= ltime->second_part + sign*interval.second_part;
792
    extra_sec= microseconds/1000000L;
793
    microseconds= microseconds%1000000L;
794
795
    sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
796
	 ltime->second +
797
	 sign* (longlong) (interval.day*3600*24L +
798
                           interval.hour*LL(3600)+interval.minute*LL(60)+
799
                           interval.second))+ extra_sec;
800
    if (microseconds < 0)
801
    {
802
      microseconds+= LL(1000000);
803
      sec--;
804
    }
805
    days= sec/(3600*LL(24));
806
    sec-= days*3600*LL(24);
807
    if (sec < 0)
808
    {
809
      days--;
810
      sec+= 3600*LL(24);
811
    }
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);
816
    daynr= calc_daynr(ltime->year,ltime->month,1) + days;
817
    /* Day number from year 0 to 9999-12-31 */
818
    if ((ulonglong) daynr > MAX_DAY_NUMBER)
819
      goto invalid_date;
820
    get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
821
                        &ltime->day);
822
    break;
823
  }
824
  case INTERVAL_DAY:
825
  case INTERVAL_WEEK:
826
    period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
827
             sign * (long) interval.day);
828
    /* Daynumber from year 0 to 9999-12-31 */
829
    if ((ulong) period > MAX_DAY_NUMBER)
830
      goto invalid_date;
831
    get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
832
    break;
833
  case INTERVAL_YEAR:
834
    ltime->year+= sign * (long) interval.year;
835
    if ((ulong) ltime->year >= 10000L)
836
      goto invalid_date;
837
    if (ltime->month == 2 && ltime->day == 29 &&
838
	calc_days_in_year(ltime->year) != 366)
839
      ltime->day=28;				// Was leap-year
840
    break;
841
  case INTERVAL_YEAR_MONTH:
842
  case INTERVAL_QUARTER:
843
  case INTERVAL_MONTH:
844
    period= (ltime->year*12 + sign * (long) interval.year*12 +
845
	     ltime->month-1 + sign * (long) interval.month);
846
    if ((ulong) period >= 120000L)
847
      goto invalid_date;
848
    ltime->year= (uint) (period / 12);
849
    ltime->month= (uint) (period % 12L)+1;
850
    /* Adjust day if the new month doesn't have enough days */
851
    if (ltime->day > days_in_month[ltime->month-1])
852
    {
853
      ltime->day = days_in_month[ltime->month-1];
854
      if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
855
	ltime->day++;				// Leap-year
856
    }
857
    break;
858
  default:
859
    goto null_date;
860
  }
861
862
  return 0;					// Ok
863
864
invalid_date:
865
  push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
866
                      ER_DATETIME_FUNCTION_OVERFLOW,
867
                      ER(ER_DATETIME_FUNCTION_OVERFLOW),
868
                      "datetime");
869
null_date:
870
  return 1;
871
}
872
873
874
/*
875
  Calculate difference between two datetime values as seconds + microseconds.
876
877
  SYNOPSIS
878
    calc_time_diff()
879
      l_time1         - TIME/DATE/DATETIME value
880
      l_time2         - TIME/DATE/DATETIME value
881
      l_sign          - 1 absolute values are substracted,
882
                        -1 absolute values are added.
883
      seconds_out     - Out parameter where difference between
884
                        l_time1 and l_time2 in seconds is stored.
885
      microseconds_out- Out parameter where microsecond part of difference
886
                        between l_time1 and l_time2 is stored.
887
888
  NOTE
889
    This function calculates difference between l_time1 and l_time2 absolute
890
    values. So one should set l_sign and correct result if he want to take
891
    signs into account (i.e. for MYSQL_TIME values).
892
893
  RETURN VALUES
894
    Returns sign of difference.
895
    1 means negative result
896
    0 means positive result
897
898
*/
899
900
bool
901
calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *seconds_out,
902
               long *microseconds_out)
903
{
904
  long days;
905
  bool neg;
906
  longlong microseconds;
907
908
  /*
909
    We suppose that if first argument is MYSQL_TIMESTAMP_TIME
910
    the second argument should be TIMESTAMP_TIME also.
911
    We should check it before calc_time_diff call.
912
  */
913
  if (l_time1->time_type == MYSQL_TIMESTAMP_TIME)  // Time value
914
    days= (long)l_time1->day - l_sign * (long)l_time2->day;
915
  else
916
  {
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)
921
      days-= l_sign * (long)l_time2->day;
922
    else
923
      days-= l_sign*calc_daynr((uint) l_time2->year,
924
			       (uint) l_time2->month,
925
			       (uint) l_time2->day);
926
  }
927
928
  microseconds= ((longlong)days*LL(86400) +
929
                 (longlong)(l_time1->hour*3600L +
930
                            l_time1->minute*60L +
931
                            l_time1->second) -
932
                 l_sign*(longlong)(l_time2->hour*3600L +
933
                                   l_time2->minute*60L +
934
                                   l_time2->second)) * LL(1000000) +
935
                (longlong)l_time1->second_part -
936
                l_sign*(longlong)l_time2->second_part;
937
938
  neg= 0;
939
  if (microseconds < 0)
940
  {
941
    microseconds= -microseconds;
942
    neg= 1;
943
  }
944
  *seconds_out= microseconds/1000000L;
945
  *microseconds_out= (long) (microseconds%1000000L);
946
  return neg;
947
}
948
949
950
/*
951
  Compares 2 MYSQL_TIME structures
952
953
  SYNOPSIS
954
    my_time_compare()
955
956
      a - first time
957
      b - second time
958
959
  RETURN VALUE
960
   -1   - a < b
961
    0   - a == b
962
    1   - a > b
963
964
  NOTES
965
    TIME.second_part is not considered during comparison
966
*/
967
968
int
969
my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b)
970
{
971
  my_ulonglong a_t= TIME_to_ulonglong_datetime(a);
972
  my_ulonglong b_t= TIME_to_ulonglong_datetime(b);
973
974
  if (a_t > b_t)
975
    return 1;
976
  else if (a_t < b_t)
977
    return -1;
978
979
  return 0;
980
}
981
982
#endif