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