~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
243.1.17 by Jay Pipes
FINAL PHASE removal of mysql_priv.h (Bye, bye my friend.)
19
#include <drizzled/server_includes.h>
202.3.6 by Monty Taylor
First pass at gettexizing the error messages.
20
#include <drizzled/drizzled_error_messages.h>
492.1.7 by Monty Taylor
Moved test() to its own file.
21
#include <drizzled/util/test.h>
1 by brian
clean slate
22
23
24
	/* Some functions to calculate dates */
25
26
#ifndef TESTTIME
27
28
/*
29
  Name description of interval names used in statements.
30
31
  'interval_type_to_name' is ordered and sorted on interval size and
32
  interval complexity.
33
  Order of elements in 'interval_type_to_name' should correspond to 
34
  the order of elements in 'interval_type' enum
35
  
36
  See also interval_type, interval_names
37
*/
38
39
LEX_STRING interval_type_to_name[INTERVAL_LAST] = {
40
  { C_STRING_WITH_LEN("YEAR")},
41
  { C_STRING_WITH_LEN("QUARTER")},
42
  { C_STRING_WITH_LEN("MONTH")},
43
  { C_STRING_WITH_LEN("WEEK")},
44
  { C_STRING_WITH_LEN("DAY")},
45
  { C_STRING_WITH_LEN("HOUR")},
46
  { C_STRING_WITH_LEN("MINUTE")},
47
  { C_STRING_WITH_LEN("SECOND")},
48
  { C_STRING_WITH_LEN("MICROSECOND")},
49
  { C_STRING_WITH_LEN("YEAR_MONTH")},
50
  { C_STRING_WITH_LEN("DAY_HOUR")},
51
  { C_STRING_WITH_LEN("DAY_MINUTE")},
52
  { C_STRING_WITH_LEN("DAY_SECOND")},
53
  { C_STRING_WITH_LEN("HOUR_MINUTE")},
54
  { C_STRING_WITH_LEN("HOUR_SECOND")},
55
  { C_STRING_WITH_LEN("MINUTE_SECOND")},
56
  { C_STRING_WITH_LEN("DAY_MICROSECOND")},
57
  { C_STRING_WITH_LEN("HOUR_MICROSECOND")},
58
  { C_STRING_WITH_LEN("MINUTE_MICROSECOND")},
59
  { C_STRING_WITH_LEN("SECOND_MICROSECOND")}
60
}; 
61
62
	/* Calc weekday from daynr */
63
	/* Returns 0 for monday, 1 for tuesday .... */
64
65
int calc_weekday(long daynr,bool sunday_first_day_of_week)
66
{
51.1.71 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
67
  return ((int) ((daynr + 5L + (sunday_first_day_of_week ? 1L : 0L)) % 7));
1 by brian
clean slate
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
482 by Brian Aker
Remove uint.
99
uint32_t calc_week(DRIZZLE_TIME *l_time, uint32_t week_behaviour, uint32_t *year)
1 by brian
clean slate
100
{
482 by Brian Aker
Remove uint.
101
  uint32_t days;
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
102
  uint32_t daynr= calc_daynr(l_time->year,l_time->month,l_time->day);
103
  uint32_t first_daynr= calc_daynr(l_time->year,1,1);
1 by brian
clean slate
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
482 by Brian Aker
Remove uint.
108
  uint32_t weekday=calc_weekday(first_daynr, !monday_first);
1 by brian
clean slate
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
482 by Brian Aker
Remove uint.
142
void get_date_from_daynr(long daynr,uint32_t *ret_year,uint32_t *ret_month,
143
			 uint32_t *ret_day)
1 by brian
clean slate
144
{
482 by Brian Aker
Remove uint.
145
  uint32_t year,temp,leap_day,day_of_year,days_in_year;
481 by Brian Aker
Remove all of uchar.
146
  unsigned char *month_pos;
1 by brian
clean slate
147
148
  if (daynr <= 365L || daynr >= 3652500)
149
  {						/* Fix if wrong daynr */
150
    *ret_year= *ret_month = *ret_day =0;
151
  }
152
  else
153
  {
154
    year= (uint) (daynr*100 / 36525L);
155
    temp=(((year-1)/100+1)*3)/4;
156
    day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp;
157
    while (day_of_year > (days_in_year= calc_days_in_year(year)))
158
    {
159
      day_of_year-=days_in_year;
160
      (year)++;
161
    }
162
    leap_day=0;
163
    if (days_in_year == 366)
164
    {
165
      if (day_of_year > 31+28)
166
      {
167
	day_of_year--;
168
	if (day_of_year == 31+28)
169
	  leap_day=1;		/* Handle leapyears leapday */
170
      }
171
    }
172
    *ret_month=1;
173
    for (month_pos= days_in_month ;
174
	 day_of_year > (uint) *month_pos ;
175
	 day_of_year-= *(month_pos++), (*ret_month)++)
176
      ;
177
    *ret_year=year;
178
    *ret_day=day_of_year+leap_day;
179
  }
51.1.71 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
180
  return;
1 by brian
clean slate
181
}
182
183
	/* Functions to handle periods */
184
520.1.6 by Brian Aker
Fixed ulong in time calculation.
185
uint32_t convert_period_to_month(uint32_t period)
1 by brian
clean slate
186
{
520.1.6 by Brian Aker
Fixed ulong in time calculation.
187
  uint32_t a,b;
1 by brian
clean slate
188
  if (period == 0)
189
    return 0L;
190
  if ((a=period/100) < YY_PART_YEAR)
191
    a+=2000;
192
  else if (a < 100)
193
    a+=1900;
194
  b=period%100;
195
  return a*12+b-1;
196
}
197
198
520.1.6 by Brian Aker
Fixed ulong in time calculation.
199
uint32_t convert_month_to_period(uint32_t month)
1 by brian
clean slate
200
{
520.1.6 by Brian Aker
Fixed ulong in time calculation.
201
  uint32_t year;
1 by brian
clean slate
202
  if (month == 0L)
203
    return 0L;
204
  if ((year=month/12) < 100)
205
  {
206
    year+=(year < YY_PART_YEAR) ? 2000 : 1900;
207
  }
208
  return year*100+month%12+1;
209
}
210
211
212
/*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
213
  Convert a timestamp string to a DRIZZLE_TIME value and produce a warning 
1 by brian
clean slate
214
  if string was truncated during conversion.
215
216
  NOTE
217
    See description of str_to_datetime() for more information.
218
*/
219
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
220
enum enum_drizzle_timestamp_type
482 by Brian Aker
Remove uint.
221
str_to_datetime_with_warn(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
222
                          uint32_t flags)
1 by brian
clean slate
223
{
224
  int was_cut;
520.1.22 by Brian Aker
Second pass of thd cleanup
225
  Session *session= current_session;
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
226
  enum enum_drizzle_timestamp_type ts_type;
1 by brian
clean slate
227
  
228
  ts_type= str_to_datetime(str, length, l_time,
520.1.22 by Brian Aker
Second pass of thd cleanup
229
                           (flags | (session->variables.sql_mode &
1 by brian
clean slate
230
                                     (MODE_INVALID_DATES |
231
                                      MODE_NO_ZERO_DATE))),
232
                           &was_cut);
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
233
  if (was_cut || ts_type <= DRIZZLE_TIMESTAMP_ERROR)
520.1.22 by Brian Aker
Second pass of thd cleanup
234
    make_truncated_value_warning(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
461 by Monty Taylor
Removed NullS. bu-bye.
235
                                 str, length, ts_type,  NULL);
1 by brian
clean slate
236
  return ts_type;
237
}
238
239
240
/*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
241
  Convert a datetime from broken-down DRIZZLE_TIME representation to corresponding 
1 by brian
clean slate
242
  TIMESTAMP value.
243
244
  SYNOPSIS
245
    TIME_to_timestamp()
520.1.22 by Brian Aker
Second pass of thd cleanup
246
      session             - current thread
1 by brian
clean slate
247
      t               - datetime in broken-down representation, 
248
      in_dst_time_gap - pointer to bool which is set to true if t represents
249
                        value which doesn't exists (falls into the spring 
250
                        time-gap) or to false otherwise.
251
   
252
  RETURN
253
     Number seconds in UTC since start of Unix Epoch corresponding to t.
254
     0 - t contains datetime value which is out of TIMESTAMP range.
255
     
256
*/
520.1.22 by Brian Aker
Second pass of thd cleanup
257
my_time_t TIME_to_timestamp(Session *session, const DRIZZLE_TIME *t, bool *in_dst_time_gap)
1 by brian
clean slate
258
{
259
  my_time_t timestamp;
260
261
  *in_dst_time_gap= 0;
520.1.22 by Brian Aker
Second pass of thd cleanup
262
  session->time_zone_used= 1;
1 by brian
clean slate
263
520.1.22 by Brian Aker
Second pass of thd cleanup
264
  timestamp= session->variables.time_zone->TIME_to_gmt_sec(t, in_dst_time_gap);
1 by brian
clean slate
265
  if (timestamp)
266
  {
267
    return timestamp;
268
  }
269
270
  /* If we are here we have range error. */
271
  return(0);
272
}
273
274
275
/*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
276
  Convert a time string to a DRIZZLE_TIME struct and produce a warning
1 by brian
clean slate
277
  if string was cut during conversion.
278
279
  NOTE
280
    See str_to_time() for more info.
281
*/
282
bool
482 by Brian Aker
Remove uint.
283
str_to_time_with_warn(const char *str, uint32_t length, DRIZZLE_TIME *l_time)
1 by brian
clean slate
284
{
285
  int warning;
286
  bool ret_val= str_to_time(str, length, l_time, &warning);
287
  if (ret_val || warning)
520.1.22 by Brian Aker
Second pass of thd cleanup
288
    make_truncated_value_warning(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
461 by Monty Taylor
Removed NullS. bu-bye.
289
                                 str, length, DRIZZLE_TIMESTAMP_TIME, NULL);
1 by brian
clean slate
290
  return ret_val;
291
}
292
293
294
/*
295
  Convert a system time structure to TIME
296
*/
297
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
298
void localtime_to_TIME(DRIZZLE_TIME *to, struct tm *from)
1 by brian
clean slate
299
{
300
  to->neg=0;
301
  to->second_part=0;
302
  to->year=	(int) ((from->tm_year+1900) % 10000);
303
  to->month=	(int) from->tm_mon+1;
304
  to->day=	(int) from->tm_mday;
305
  to->hour=	(int) from->tm_hour;
306
  to->minute=	(int) from->tm_min;
307
  to->second=   (int) from->tm_sec;
308
}
309
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
310
void calc_time_from_sec(DRIZZLE_TIME *to, long seconds, long microseconds)
1 by brian
clean slate
311
{
312
  long t_seconds;
313
  // to->neg is not cleared, it may already be set to a useful value
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
314
  to->time_type= DRIZZLE_TIMESTAMP_TIME;
1 by brian
clean slate
315
  to->year= 0;
316
  to->month= 0;
317
  to->day= 0;
318
  to->hour= seconds/3600L;
319
  t_seconds= seconds%3600L;
320
  to->minute= t_seconds/60L;
321
  to->second= t_seconds%60L;
322
  to->second_part= microseconds;
323
}
324
325
326
/*
327
  Parse a format string specification
328
329
  SYNOPSIS
330
    parse_date_time_format()
331
    format_type		Format of string (time, date or datetime)
332
    format_str		String to parse
333
    format_length	Length of string
334
    date_time_format	Format to fill in
335
336
  NOTES
337
    Fills in date_time_format->positions for all date time parts.
338
339
    positions marks the position for a datetime element in the format string.
340
    The position array elements are in the following order:
341
    YYYY-DD-MM HH-MM-DD.FFFFFF AM
342
    0    1  2  3  4  5  6      7
343
344
    If positions[0]= 5, it means that year will be the forth element to
345
    read from the parsed date string.
346
347
  RETURN
348
    0	ok
349
    1	error
350
*/
351
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
352
bool parse_date_time_format(enum enum_drizzle_timestamp_type format_type, 
482 by Brian Aker
Remove uint.
353
			    const char *format, uint32_t format_length,
1 by brian
clean slate
354
			    DATE_TIME_FORMAT *date_time_format)
355
{
482 by Brian Aker
Remove uint.
356
  uint32_t offset= 0, separators= 0;
1 by brian
clean slate
357
  const char *ptr= format, *format_str;
358
  const char *end= ptr+format_length;
481 by Brian Aker
Remove all of uchar.
359
  unsigned char *dt_pos= date_time_format->positions;
1 by brian
clean slate
360
  /* need_p is set if we are using AM/PM format */
361
  bool need_p= 0, allow_separator= 0;
465 by Monty Taylor
Made a few macros into template functions.
362
  uint32_t part_map= 0, separator_map= 0;
1 by brian
clean slate
363
  const char *parts[16];
364
365
  date_time_format->time_separator= 0;
366
  date_time_format->flag= 0;			// For future
367
368
  /*
369
    Fill position with 'dummy' arguments to found out if a format tag is
370
    used twice (This limit's the format to 255 characters, but this is ok)
371
  */
372
  dt_pos[0]= dt_pos[1]= dt_pos[2]= dt_pos[3]=
373
    dt_pos[4]= dt_pos[5]= dt_pos[6]= dt_pos[7]= 255;
374
375
  for (; ptr != end; ptr++)
376
  {
377
    if (*ptr == '%' && ptr+1 != end)
378
    {
482 by Brian Aker
Remove uint.
379
      uint32_t position;
1 by brian
clean slate
380
      switch (*++ptr) {
381
      case 'y':					// Year
382
      case 'Y':
383
	position= 0;
384
	break;
385
      case 'c':					// Month
386
      case 'm':
387
	position= 1;
388
	break;
389
      case 'd':
390
      case 'e':
391
	position= 2;
392
	break;
393
      case 'h':
394
      case 'I':
395
      case 'l':
396
	need_p= 1;				// Need AM/PM
397
	/* Fall through */
398
      case 'k':
399
      case 'H':
400
	position= 3;
401
	break;
402
      case 'i':
403
	position= 4;
404
	break;
405
      case 's':
406
      case 'S':
407
	position= 5;
408
	break;
409
      case 'f':
410
	position= 6;
411
	if (dt_pos[5] != offset-1 || ptr[-2] != '.')
412
	  return 1;				// Wrong usage of %f
413
	break;
414
      case 'p':					// AM/PM
415
	if (offset == 0)			// Can't be first
416
	  return 0;
417
	position= 7;
418
	break;
419
      default:
420
	return 1;				// Unknown controll char
421
      }
422
      if (dt_pos[position] != 255)		// Don't allow same tag twice
423
	return 1;
424
      parts[position]= ptr-1;
425
426
      /*
427
	If switching from time to date, ensure that all time parts
428
	are used
429
      */
430
      if (part_map && position <= 2 && !(part_map & (1 | 2 | 4)))
431
	offset=5;
465 by Monty Taylor
Made a few macros into template functions.
432
      part_map|= UINT32_C(1) << position;
1 by brian
clean slate
433
      dt_pos[position]= offset++;
434
      allow_separator= 1;
435
    }
436
    else
437
    {
438
      /*
439
	Don't allow any characters in format as this could easily confuse
440
	the date reader
441
      */
442
      if (!allow_separator)
443
	return 1;				// No separator here
444
      allow_separator= 0;			// Don't allow two separators
445
      separators++;
446
      /* Store in separator_map which parts are punct characters */
383.1.12 by Brian Aker
Much closer toward UTF8 being around all the time...
447
      if (my_ispunct(&my_charset_utf8_general_ci, *ptr))
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
448
	separator_map|= 1 << (offset-1);
383.1.12 by Brian Aker
Much closer toward UTF8 being around all the time...
449
      else if (!my_isspace(&my_charset_utf8_general_ci, *ptr))
1 by brian
clean slate
450
	return 1;
451
    }
452
  }
453
454
  /* If no %f, specify it after seconds.  Move %p up, if necessary */
455
  if ((part_map & 32) && !(part_map & 64))
456
  {
457
    dt_pos[6]= dt_pos[5] +1;
458
    parts[6]= parts[5];				// For later test in (need_p)
459
    if (dt_pos[6] == dt_pos[7])			// Move %p one step up if used
460
      dt_pos[7]++;
461
  }
462
463
  /*
464
    Check that we have not used a non legal format specifier and that all
465
    format specifiers have been used
466
467
    The last test is to ensure that %p is used if and only if
468
    it's needed.
469
  */
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
470
  if ((format_type == DRIZZLE_TIMESTAMP_DATETIME &&
465 by Monty Taylor
Made a few macros into template functions.
471
       !test_all_bits(part_map, (uint32_t) (1 | 2 | 4 | 8 | 16 | 32))) ||
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
472
      (format_type == DRIZZLE_TIMESTAMP_DATE && part_map != (1 | 2 | 4)) ||
473
      (format_type == DRIZZLE_TIMESTAMP_TIME &&
465 by Monty Taylor
Made a few macros into template functions.
474
       !test_all_bits(part_map, (uint32_t) (8 | 16 | 32))) ||
1 by brian
clean slate
475
      !allow_separator ||			// %option should be last
476
      (need_p && dt_pos[6] +1 != dt_pos[7]) ||
477
      (need_p ^ (dt_pos[7] != 255)))
478
    return 1;
479
480
  if (dt_pos[6] != 255)				// If fractional seconds
481
  {
482
    /* remove fractional seconds from later tests */
482 by Brian Aker
Remove uint.
483
    uint32_t pos= dt_pos[6] -1;
1 by brian
clean slate
484
    /* Remove separator before %f from sep map */
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
485
    separator_map= ((separator_map & ((1 << pos)-1)) |
486
		    ((separator_map & ~((1 << pos)-1)) >> 1));
1 by brian
clean slate
487
    if (part_map & 64)			      
488
    {
489
      separators--;				// There is always a separator
490
      need_p= 1;				// force use of separators
491
    }
492
  }
493
494
  /*
495
    Remove possible separator before %p from sep_map
496
    (This can either be at position 3, 4, 6 or 7) h.m.d.%f %p
497
  */
498
  if (dt_pos[7] != 255)
499
  {
500
    if (need_p && parts[7] != parts[6]+2)
501
      separators--;
502
  }     
503
  /*
504
    Calculate if %p is in first or last part of the datetime field
505
506
    At this point we have either %H-%i-%s %p 'year parts' or
507
    'year parts' &H-%i-%s %p" as %f was removed above
508
  */
509
  offset= dt_pos[6] <= 3 ? 3 : 6;
510
  /* Remove separator before %p from sep map */
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
511
  separator_map= ((separator_map & ((1 << offset)-1)) |
512
		  ((separator_map & ~((1 << offset)-1)) >> 1));
1 by brian
clean slate
513
514
  format_str= 0;
515
  switch (format_type) {
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
516
  case DRIZZLE_TIMESTAMP_DATE:
1 by brian
clean slate
517
    format_str= known_date_time_formats[INTERNAL_FORMAT].date_format;
518
    /* fall through */
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
519
  case DRIZZLE_TIMESTAMP_TIME:
1 by brian
clean slate
520
    if (!format_str)
521
      format_str=known_date_time_formats[INTERNAL_FORMAT].time_format;
522
523
    /*
524
      If there is no separators, allow the internal format as we can read
525
      this.  If separators are used, they must be between each part
526
    */
527
    if (format_length == 6 && !need_p &&
528
	!my_strnncoll(&my_charset_bin,
481 by Brian Aker
Remove all of uchar.
529
		      (const unsigned char *) format, 6, 
530
		      (const unsigned char *) format_str, 6))
1 by brian
clean slate
531
      return 0;
532
    if (separator_map == (1 | 2))
533
    {
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
534
      if (format_type == DRIZZLE_TIMESTAMP_TIME)
1 by brian
clean slate
535
      {
536
	if (*(format+2) != *(format+5))
537
	  break;				// Error
538
	/* Store the character used for time formats */
539
	date_time_format->time_separator= *(format+2);
540
      }
541
      return 0;
542
    }
543
    break;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
544
  case DRIZZLE_TIMESTAMP_DATETIME:
1 by brian
clean slate
545
    /*
546
      If there is no separators, allow the internal format as we can read
547
      this.  If separators are used, they must be between each part.
548
      Between DATE and TIME we also allow space as separator
549
    */
550
    if ((format_length == 12 && !need_p &&
551
	 !my_strnncoll(&my_charset_bin, 
481 by Brian Aker
Remove all of uchar.
552
		       (const unsigned char *) format, 12,
553
		       (const unsigned char*) known_date_time_formats[INTERNAL_FORMAT].datetime_format,
1 by brian
clean slate
554
		       12)) ||
555
	(separators == 5 && separator_map == (1 | 2 | 8 | 16)))
556
      return 0;
557
    break;
558
  default:
51.1.71 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
559
    assert(1);
1 by brian
clean slate
560
    break;
561
  }
562
  return 1;					// Error
563
}
564
565
566
/*
567
  Create a DATE_TIME_FORMAT object from a format string specification
568
569
  SYNOPSIS
570
    date_time_format_make()
571
    format_type		Format to parse (time, date or datetime)
572
    format_str		String to parse
573
    format_length	Length of string
574
575
  NOTES
477 by Monty Taylor
Removed my_free(). It turns out that it had been def'd to ignore the flags passed to it in the second arg anyway. Gotta love that.
576
    The returned object should be freed with free()
1 by brian
clean slate
577
578
  RETURN
579
    NULL ponter:	Error
580
    new object
581
*/
582
583
DATE_TIME_FORMAT
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
584
*date_time_format_make(enum enum_drizzle_timestamp_type format_type,
482 by Brian Aker
Remove uint.
585
		       const char *format_str, uint32_t format_length)
1 by brian
clean slate
586
{
587
  DATE_TIME_FORMAT tmp;
588
589
  if (format_length && format_length < 255 &&
590
      !parse_date_time_format(format_type, format_str,
591
			      format_length, &tmp))
592
  {
593
    tmp.format.str=    (char*) format_str;
594
    tmp.format.length= format_length;
520.1.21 by Brian Aker
THD -> Session rename
595
    return date_time_format_copy((Session *)0, &tmp);
1 by brian
clean slate
596
  }
597
  return 0;
598
}
599
600
601
/*
602
  Create a copy of a DATE_TIME_FORMAT object
603
604
  SYNOPSIS
605
    date_and_time_format_copy()
520.1.22 by Brian Aker
Second pass of thd cleanup
606
    session			Set if variable should be allocated in thread mem
1 by brian
clean slate
607
    format		format to copy
608
609
  NOTES
477 by Monty Taylor
Removed my_free(). It turns out that it had been def'd to ignore the flags passed to it in the second arg anyway. Gotta love that.
610
    The returned object should be freed with free()
1 by brian
clean slate
611
612
  RETURN
613
    NULL ponter:	Error
614
    new object
615
*/
616
520.1.22 by Brian Aker
Second pass of thd cleanup
617
DATE_TIME_FORMAT *date_time_format_copy(Session *session, DATE_TIME_FORMAT *format)
1 by brian
clean slate
618
{
619
  DATE_TIME_FORMAT *new_format;
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
620
  uint32_t length= sizeof(*format) + format->format.length + 1;
1 by brian
clean slate
621
520.1.22 by Brian Aker
Second pass of thd cleanup
622
  if (session)
623
    new_format= (DATE_TIME_FORMAT *) session->alloc(length);
1 by brian
clean slate
624
  else
625
    new_format=  (DATE_TIME_FORMAT *) my_malloc(length, MYF(MY_WME));
626
  if (new_format)
627
  {
628
    /* Put format string after current pos */
629
    new_format->format.str= (char*) (new_format+1);
212.6.6 by Mats Kindahl
Removing redundant use of casts in drizzled/ for memcmp(), memcpy(), memset(), and memmove().
630
    memcpy(new_format->positions, format->positions,
1 by brian
clean slate
631
	   sizeof(format->positions));
632
    new_format->time_separator= format->time_separator;
633
    /* We make the string null terminated for easy printf in SHOW VARIABLES */
212.6.6 by Mats Kindahl
Removing redundant use of casts in drizzled/ for memcmp(), memcpy(), memset(), and memmove().
634
    memcpy(new_format->format.str, format->format.str,
1 by brian
clean slate
635
	   format->format.length);
636
    new_format->format.str[format->format.length]= 0;
637
    new_format->format.length= format->format.length;
638
  }
639
  return new_format;
640
}
641
642
643
KNOWN_DATE_TIME_FORMAT known_date_time_formats[6]=
644
{
645
  {"USA", "%m.%d.%Y", "%Y-%m-%d %H.%i.%s", "%h:%i:%s %p" },
646
  {"JIS", "%Y-%m-%d", "%Y-%m-%d %H:%i:%s", "%H:%i:%s" },
647
  {"ISO", "%Y-%m-%d", "%Y-%m-%d %H:%i:%s", "%H:%i:%s" },
648
  {"EUR", "%d.%m.%Y", "%Y-%m-%d %H.%i.%s", "%H.%i.%s" },
649
  {"INTERNAL", "%Y%m%d",   "%Y%m%d%H%i%s", "%H%i%s" },
650
  { 0, 0, 0, 0 }
651
};
652
653
654
/*
655
   Return format string according format name.
656
   If name is unknown, result is NULL
657
*/
658
659
const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
660
				     enum enum_drizzle_timestamp_type type)
1 by brian
clean slate
661
{
662
  switch (type) {
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
663
  case DRIZZLE_TIMESTAMP_DATE:
1 by brian
clean slate
664
    return format->date_format;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
665
  case DRIZZLE_TIMESTAMP_DATETIME:
1 by brian
clean slate
666
    return format->datetime_format;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
667
  case DRIZZLE_TIMESTAMP_TIME:
1 by brian
clean slate
668
    return format->time_format;
669
  default:
51.1.71 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
670
    assert(0);				// Impossible
1 by brian
clean slate
671
    return 0;
672
  }
673
}
674
675
/****************************************************************************
676
  Functions to create default time/date/datetime strings
677
 
678
  NOTE:
679
    For the moment the DATE_TIME_FORMAT argument is ignored becasue
680
    MySQL doesn't support comparing of date/time/datetime strings that
681
    are not in arbutary order as dates are compared as strings in some
682
    context)
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
683
    This functions don't check that given DRIZZLE_TIME structure members are
1 by brian
clean slate
684
    in valid range. If they are not, return value won't reflect any 
685
    valid date either. Additionally, make_time doesn't take into
686
    account time->day member: it's assumed that days have been converted
687
    to hours already.
688
****************************************************************************/
689
690
void make_time(const DATE_TIME_FORMAT *format __attribute__((unused)),
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
691
               const DRIZZLE_TIME *l_time, String *str)
1 by brian
clean slate
692
{
482 by Brian Aker
Remove uint.
693
  uint32_t length= (uint) my_time_to_str(l_time, (char*) str->ptr());
1 by brian
clean slate
694
  str->length(length);
695
  str->set_charset(&my_charset_bin);
696
}
697
698
699
void make_date(const DATE_TIME_FORMAT *format __attribute__((unused)),
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
700
               const DRIZZLE_TIME *l_time, String *str)
1 by brian
clean slate
701
{
482 by Brian Aker
Remove uint.
702
  uint32_t length= (uint) my_date_to_str(l_time, (char*) str->ptr());
1 by brian
clean slate
703
  str->length(length);
704
  str->set_charset(&my_charset_bin);
705
}
706
707
708
void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)),
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
709
                   const DRIZZLE_TIME *l_time, String *str)
1 by brian
clean slate
710
{
482 by Brian Aker
Remove uint.
711
  uint32_t length= (uint) my_datetime_to_str(l_time, (char*) str->ptr());
1 by brian
clean slate
712
  str->length(length);
713
  str->set_charset(&my_charset_bin);
714
}
715
716
520.1.22 by Brian Aker
Second pass of thd cleanup
717
void make_truncated_value_warning(Session *session, DRIZZLE_ERROR::enum_warning_level level,
1 by brian
clean slate
718
                                  const char *str_val,
482 by Brian Aker
Remove uint.
719
				  uint32_t str_length,
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
720
                                  enum enum_drizzle_timestamp_type time_type,
1 by brian
clean slate
721
                                  const char *field_name)
722
{
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
723
  char warn_buff[DRIZZLE_ERRMSG_SIZE];
1 by brian
clean slate
724
  const char *type_str;
383.1.12 by Brian Aker
Much closer toward UTF8 being around all the time...
725
  CHARSET_INFO *cs= &my_charset_utf8_general_ci;
1 by brian
clean slate
726
  char buff[128];
205 by Brian Aker
uint32 -> uin32_t
727
  String str(buff,(uint32_t) sizeof(buff), system_charset_info);
1 by brian
clean slate
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) {
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
732
    case DRIZZLE_TIMESTAMP_DATE: 
1 by brian
clean slate
733
      type_str= "date";
734
      break;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
735
    case DRIZZLE_TIMESTAMP_TIME:
1 by brian
clean slate
736
      type_str= "time";
737
      break;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
738
    case DRIZZLE_TIMESTAMP_DATETIME:  // FALLTHROUGH
1 by brian
clean slate
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,
520.1.22 by Brian Aker
Second pass of thd cleanup
747
                       (uint32_t) session->row_count);
1 by brian
clean slate
748
  else
749
  {
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
750
    if (time_type > DRIZZLE_TIMESTAMP_ERROR)
1 by brian
clean slate
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
  }
520.1.22 by Brian Aker
Second pass of thd cleanup
758
  push_warning(session, level,
1 by brian
clean slate
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
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
765
bool date_add_interval(DRIZZLE_TIME *ltime, interval_type int_type, INTERVAL interval)
1 by brian
clean slate
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
  {
152 by Brian Aker
longlong replacement
789
    int64_t sec, days, daynr, microseconds, extra_sec;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
790
    ltime->time_type= DRIZZLE_TIMESTAMP_DATETIME; // Return full date
1 by brian
clean slate
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 +
152 by Brian Aker
longlong replacement
797
	 sign* (int64_t) (interval.day*3600*24L +
398.1.8 by Monty Taylor
Enabled -Wlong-long.
798
                           interval.hour*3600L+interval.minute*60L+
1 by brian
clean slate
799
                           interval.second))+ extra_sec;
800
    if (microseconds < 0)
801
    {
398.1.8 by Monty Taylor
Enabled -Wlong-long.
802
      microseconds+= 1000000L;
1 by brian
clean slate
803
      sec--;
804
    }
398.1.8 by Monty Taylor
Enabled -Wlong-long.
805
    days= sec/(3600*24L);
806
    sec-= days*3600*24L;
1 by brian
clean slate
807
    if (sec < 0)
808
    {
809
      days--;
398.1.8 by Monty Taylor
Enabled -Wlong-long.
810
      sec+= 3600*24L;
1 by brian
clean slate
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 */
151 by Brian Aker
Ulonglong to uint64_t
818
    if ((uint64_t) daynr > MAX_DAY_NUMBER)
1 by brian
clean slate
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 */
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
829
    if (period > MAX_DAY_NUMBER)
1 by brian
clean slate
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;
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
835
    if (ltime->year >= 10000L)
1 by brian
clean slate
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);
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
846
    if (period >= 120000L)
1 by brian
clean slate
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:
520.1.22 by Brian Aker
Second pass of thd cleanup
865
  push_warning_printf(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
1 by brian
clean slate
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
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
891
    signs into account (i.e. for DRIZZLE_TIME values).
1 by brian
clean slate
892
893
  RETURN VALUES
894
    Returns sign of difference.
895
    1 means negative result
896
    0 means positive result
897
898
*/
899
900
bool
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
901
calc_time_diff(DRIZZLE_TIME *l_time1, DRIZZLE_TIME *l_time2, int l_sign, int64_t *seconds_out,
1 by brian
clean slate
902
               long *microseconds_out)
903
{
904
  long days;
905
  bool neg;
152 by Brian Aker
longlong replacement
906
  int64_t microseconds;
1 by brian
clean slate
907
908
  /*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
909
    We suppose that if first argument is DRIZZLE_TIMESTAMP_TIME
1 by brian
clean slate
910
    the second argument should be TIMESTAMP_TIME also.
911
    We should check it before calc_time_diff call.
912
  */
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
913
  if (l_time1->time_type == DRIZZLE_TIMESTAMP_TIME)  // Time value
1 by brian
clean slate
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);
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
920
    if (l_time2->time_type == DRIZZLE_TIMESTAMP_TIME)
1 by brian
clean slate
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
398.1.8 by Monty Taylor
Enabled -Wlong-long.
928
  microseconds= ((int64_t)days*86400L +
152 by Brian Aker
longlong replacement
929
                 (int64_t)(l_time1->hour*3600L +
1 by brian
clean slate
930
                            l_time1->minute*60L +
931
                            l_time1->second) -
152 by Brian Aker
longlong replacement
932
                 l_sign*(int64_t)(l_time2->hour*3600L +
1 by brian
clean slate
933
                                   l_time2->minute*60L +
398.1.8 by Monty Taylor
Enabled -Wlong-long.
934
                                   l_time2->second)) * 1000000L +
152 by Brian Aker
longlong replacement
935
                (int64_t)l_time1->second_part -
936
                l_sign*(int64_t)l_time2->second_part;
1 by brian
clean slate
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
/*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
951
  Compares 2 DRIZZLE_TIME structures
1 by brian
clean slate
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
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
969
my_time_compare(DRIZZLE_TIME *a, DRIZZLE_TIME *b)
1 by brian
clean slate
970
{
157 by Brian Aker
Second pass cleanup on removal of my_uint types
971
  uint64_t a_t= TIME_to_uint64_t_datetime(a);
972
  uint64_t b_t= TIME_to_uint64_t_datetime(b);
1 by brian
clean slate
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