~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>
1 by brian
clean slate
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
482 by Brian Aker
Remove uint.
98
uint32_t calc_week(DRIZZLE_TIME *l_time, uint32_t week_behaviour, uint32_t *year)
1 by brian
clean slate
99
{
482 by Brian Aker
Remove uint.
100
  uint32_t days;
1 by brian
clean slate
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
482 by Brian Aker
Remove uint.
107
  uint32_t weekday=calc_weekday(first_daynr, !monday_first);
1 by brian
clean slate
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
482 by Brian Aker
Remove uint.
141
void get_date_from_daynr(long daynr,uint32_t *ret_year,uint32_t *ret_month,
142
			 uint32_t *ret_day)
1 by brian
clean slate
143
{
482 by Brian Aker
Remove uint.
144
  uint32_t year,temp,leap_day,day_of_year,days_in_year;
481 by Brian Aker
Remove all of uchar.
145
  unsigned char *month_pos;
1 by brian
clean slate
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
/*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
212
  Convert a timestamp string to a DRIZZLE_TIME value and produce a warning 
1 by brian
clean slate
213
  if string was truncated during conversion.
214
215
  NOTE
216
    See description of str_to_datetime() for more information.
217
*/
218
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
219
enum enum_drizzle_timestamp_type
482 by Brian Aker
Remove uint.
220
str_to_datetime_with_warn(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
221
                          uint32_t flags)
1 by brian
clean slate
222
{
223
  int was_cut;
224
  THD *thd= current_thd;
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
225
  enum enum_drizzle_timestamp_type ts_type;
1 by brian
clean slate
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);
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
232
  if (was_cut || ts_type <= DRIZZLE_TIMESTAMP_ERROR)
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
233
    make_truncated_value_warning(current_thd, DRIZZLE_ERROR::WARN_LEVEL_WARN,
461 by Monty Taylor
Removed NullS. bu-bye.
234
                                 str, length, ts_type,  NULL);
1 by brian
clean slate
235
  return ts_type;
236
}
237
238
239
/*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
240
  Convert a datetime from broken-down DRIZZLE_TIME representation to corresponding 
1 by brian
clean slate
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
*/
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
256
my_time_t TIME_to_timestamp(THD *thd, const DRIZZLE_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
/*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
275
  Convert a time string to a DRIZZLE_TIME struct and produce a warning
1 by brian
clean slate
276
  if string was cut during conversion.
277
278
  NOTE
279
    See str_to_time() for more info.
280
*/
281
bool
482 by Brian Aker
Remove uint.
282
str_to_time_with_warn(const char *str, uint32_t length, DRIZZLE_TIME *l_time)
1 by brian
clean slate
283
{
284
  int warning;
285
  bool ret_val= str_to_time(str, length, l_time, &warning);
286
  if (ret_val || warning)
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
287
    make_truncated_value_warning(current_thd, DRIZZLE_ERROR::WARN_LEVEL_WARN,
461 by Monty Taylor
Removed NullS. bu-bye.
288
                                 str, length, DRIZZLE_TIMESTAMP_TIME, NULL);
1 by brian
clean slate
289
  return ret_val;
290
}
291
292
293
/*
294
  Convert a system time structure to TIME
295
*/
296
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
297
void localtime_to_TIME(DRIZZLE_TIME *to, struct tm *from)
1 by brian
clean slate
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
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
309
void calc_time_from_sec(DRIZZLE_TIME *to, long seconds, long microseconds)
1 by brian
clean slate
310
{
311
  long t_seconds;
312
  // 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.
313
  to->time_type= DRIZZLE_TIMESTAMP_TIME;
1 by brian
clean slate
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
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
351
bool parse_date_time_format(enum enum_drizzle_timestamp_type format_type, 
482 by Brian Aker
Remove uint.
352
			    const char *format, uint32_t format_length,
1 by brian
clean slate
353
			    DATE_TIME_FORMAT *date_time_format)
354
{
482 by Brian Aker
Remove uint.
355
  uint32_t offset= 0, separators= 0;
1 by brian
clean slate
356
  const char *ptr= format, *format_str;
357
  const char *end= ptr+format_length;
481 by Brian Aker
Remove all of uchar.
358
  unsigned char *dt_pos= date_time_format->positions;
1 by brian
clean slate
359
  /* need_p is set if we are using AM/PM format */
360
  bool need_p= 0, allow_separator= 0;
465 by Monty Taylor
Made a few macros into template functions.
361
  uint32_t part_map= 0, separator_map= 0;
1 by brian
clean slate
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
    {
482 by Brian Aker
Remove uint.
378
      uint32_t position;
1 by brian
clean slate
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;
465 by Monty Taylor
Made a few macros into template functions.
431
      part_map|= UINT32_C(1) << position;
1 by brian
clean slate
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 */
383.1.12 by Brian Aker
Much closer toward UTF8 being around all the time...
446
      if (my_ispunct(&my_charset_utf8_general_ci, *ptr))
1 by brian
clean slate
447
	separator_map|= (ulong) 1 << (offset-1);
383.1.12 by Brian Aker
Much closer toward UTF8 being around all the time...
448
      else if (!my_isspace(&my_charset_utf8_general_ci, *ptr))
1 by brian
clean slate
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
  */
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
469
  if ((format_type == DRIZZLE_TIMESTAMP_DATETIME &&
465 by Monty Taylor
Made a few macros into template functions.
470
       !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.
471
      (format_type == DRIZZLE_TIMESTAMP_DATE && part_map != (1 | 2 | 4)) ||
472
      (format_type == DRIZZLE_TIMESTAMP_TIME &&
465 by Monty Taylor
Made a few macros into template functions.
473
       !test_all_bits(part_map, (uint32_t) (8 | 16 | 32))) ||
1 by brian
clean slate
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 by Brian Aker
Remove uint.
482
    uint32_t pos= dt_pos[6] -1;
1 by brian
clean slate
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) {
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
515
  case DRIZZLE_TIMESTAMP_DATE:
1 by brian
clean slate
516
    format_str= known_date_time_formats[INTERNAL_FORMAT].date_format;
517
    /* fall through */
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
518
  case DRIZZLE_TIMESTAMP_TIME:
1 by brian
clean slate
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,
481 by Brian Aker
Remove all of uchar.
528
		      (const unsigned char *) format, 6, 
529
		      (const unsigned char *) format_str, 6))
1 by brian
clean slate
530
      return 0;
531
    if (separator_map == (1 | 2))
532
    {
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
533
      if (format_type == DRIZZLE_TIMESTAMP_TIME)
1 by brian
clean slate
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;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
543
  case DRIZZLE_TIMESTAMP_DATETIME:
1 by brian
clean slate
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, 
481 by Brian Aker
Remove all of uchar.
551
		       (const unsigned char *) format, 12,
552
		       (const unsigned char*) known_date_time_formats[INTERNAL_FORMAT].datetime_format,
1 by brian
clean slate
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
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.
575
    The returned object should be freed with free()
1 by brian
clean slate
576
577
  RETURN
578
    NULL ponter:	Error
579
    new object
580
*/
581
582
DATE_TIME_FORMAT
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
583
*date_time_format_make(enum enum_drizzle_timestamp_type format_type,
482 by Brian Aker
Remove uint.
584
		       const char *format_str, uint32_t format_length)
1 by brian
clean slate
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
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.
609
    The returned object should be freed with free()
1 by brian
clean slate
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);
212.6.6 by Mats Kindahl
Removing redundant use of casts in drizzled/ for memcmp(), memcpy(), memset(), and memmove().
629
    memcpy(new_format->positions, format->positions,
1 by brian
clean slate
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 */
212.6.6 by Mats Kindahl
Removing redundant use of casts in drizzled/ for memcmp(), memcpy(), memset(), and memmove().
633
    memcpy(new_format->format.str, format->format.str,
1 by brian
clean slate
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,
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
659
				     enum enum_drizzle_timestamp_type type)
1 by brian
clean slate
660
{
661
  switch (type) {
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
662
  case DRIZZLE_TIMESTAMP_DATE:
1 by brian
clean slate
663
    return format->date_format;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
664
  case DRIZZLE_TIMESTAMP_DATETIME:
1 by brian
clean slate
665
    return format->datetime_format;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
666
  case DRIZZLE_TIMESTAMP_TIME:
1 by brian
clean slate
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)
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
682
    This functions don't check that given DRIZZLE_TIME structure members are
1 by brian
clean slate
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)),
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
690
               const DRIZZLE_TIME *l_time, String *str)
1 by brian
clean slate
691
{
482 by Brian Aker
Remove uint.
692
  uint32_t length= (uint) my_time_to_str(l_time, (char*) str->ptr());
1 by brian
clean slate
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)),
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
699
               const DRIZZLE_TIME *l_time, String *str)
1 by brian
clean slate
700
{
482 by Brian Aker
Remove uint.
701
  uint32_t length= (uint) my_date_to_str(l_time, (char*) str->ptr());
1 by brian
clean slate
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)),
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
708
                   const DRIZZLE_TIME *l_time, String *str)
1 by brian
clean slate
709
{
482 by Brian Aker
Remove uint.
710
  uint32_t length= (uint) my_datetime_to_str(l_time, (char*) str->ptr());
1 by brian
clean slate
711
  str->length(length);
712
  str->set_charset(&my_charset_bin);
713
}
714
715
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
716
void make_truncated_value_warning(THD *thd, DRIZZLE_ERROR::enum_warning_level level,
1 by brian
clean slate
717
                                  const char *str_val,
482 by Brian Aker
Remove uint.
718
				  uint32_t str_length,
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
719
                                  enum enum_drizzle_timestamp_type time_type,
1 by brian
clean slate
720
                                  const char *field_name)
721
{
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
722
  char warn_buff[DRIZZLE_ERRMSG_SIZE];
1 by brian
clean slate
723
  const char *type_str;
383.1.12 by Brian Aker
Much closer toward UTF8 being around all the time...
724
  CHARSET_INFO *cs= &my_charset_utf8_general_ci;
1 by brian
clean slate
725
  char buff[128];
205 by Brian Aker
uint32 -> uin32_t
726
  String str(buff,(uint32_t) sizeof(buff), system_charset_info);
1 by brian
clean slate
727
  str.copy(str_val, str_length, system_charset_info);
728
  str[str_length]= 0;               // Ensure we have end 0 for snprintf
729
730
  switch (time_type) {
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
731
    case DRIZZLE_TIMESTAMP_DATE: 
1 by brian
clean slate
732
      type_str= "date";
733
      break;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
734
    case DRIZZLE_TIMESTAMP_TIME:
1 by brian
clean slate
735
      type_str= "time";
736
      break;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
737
    case DRIZZLE_TIMESTAMP_DATETIME:  // FALLTHROUGH
1 by brian
clean slate
738
    default:
739
      type_str= "datetime";
740
      break;
741
  }
742
  if (field_name)
743
    cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
744
                       ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
745
                       type_str, str.c_ptr(), field_name,
746
                       (ulong) thd->row_count);
747
  else
748
  {
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
749
    if (time_type > DRIZZLE_TIMESTAMP_ERROR)
1 by brian
clean slate
750
      cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
751
                         ER(ER_TRUNCATED_WRONG_VALUE),
752
                         type_str, str.c_ptr());
753
    else
754
      cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
755
                         ER(ER_WRONG_VALUE), type_str, str.c_ptr());
756
  }
757
  push_warning(thd, level,
758
               ER_TRUNCATED_WRONG_VALUE, warn_buff);
759
}
760
761
/* Daynumber from year 0 to 9999-12-31 */
762
#define MAX_DAY_NUMBER 3652424L
763
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
764
bool date_add_interval(DRIZZLE_TIME *ltime, interval_type int_type, INTERVAL interval)
1 by brian
clean slate
765
{
766
  long period, sign;
767
768
  ltime->neg= 0;
769
770
  sign= (interval.neg ? -1 : 1);
771
772
  switch (int_type) {
773
  case INTERVAL_SECOND:
774
  case INTERVAL_SECOND_MICROSECOND:
775
  case INTERVAL_MICROSECOND:
776
  case INTERVAL_MINUTE:
777
  case INTERVAL_HOUR:
778
  case INTERVAL_MINUTE_MICROSECOND:
779
  case INTERVAL_MINUTE_SECOND:
780
  case INTERVAL_HOUR_MICROSECOND:
781
  case INTERVAL_HOUR_SECOND:
782
  case INTERVAL_HOUR_MINUTE:
783
  case INTERVAL_DAY_MICROSECOND:
784
  case INTERVAL_DAY_SECOND:
785
  case INTERVAL_DAY_MINUTE:
786
  case INTERVAL_DAY_HOUR:
787
  {
152 by Brian Aker
longlong replacement
788
    int64_t sec, days, daynr, microseconds, extra_sec;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
789
    ltime->time_type= DRIZZLE_TIMESTAMP_DATETIME; // Return full date
1 by brian
clean slate
790
    microseconds= ltime->second_part + sign*interval.second_part;
791
    extra_sec= microseconds/1000000L;
792
    microseconds= microseconds%1000000L;
793
794
    sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
795
	 ltime->second +
152 by Brian Aker
longlong replacement
796
	 sign* (int64_t) (interval.day*3600*24L +
398.1.8 by Monty Taylor
Enabled -Wlong-long.
797
                           interval.hour*3600L+interval.minute*60L+
1 by brian
clean slate
798
                           interval.second))+ extra_sec;
799
    if (microseconds < 0)
800
    {
398.1.8 by Monty Taylor
Enabled -Wlong-long.
801
      microseconds+= 1000000L;
1 by brian
clean slate
802
      sec--;
803
    }
398.1.8 by Monty Taylor
Enabled -Wlong-long.
804
    days= sec/(3600*24L);
805
    sec-= days*3600*24L;
1 by brian
clean slate
806
    if (sec < 0)
807
    {
808
      days--;
398.1.8 by Monty Taylor
Enabled -Wlong-long.
809
      sec+= 3600*24L;
1 by brian
clean slate
810
    }
811
    ltime->second_part= (uint) microseconds;
812
    ltime->second= (uint) (sec % 60);
813
    ltime->minute= (uint) (sec/60 % 60);
814
    ltime->hour=   (uint) (sec/3600);
815
    daynr= calc_daynr(ltime->year,ltime->month,1) + days;
816
    /* Day number from year 0 to 9999-12-31 */
151 by Brian Aker
Ulonglong to uint64_t
817
    if ((uint64_t) daynr > MAX_DAY_NUMBER)
1 by brian
clean slate
818
      goto invalid_date;
819
    get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
820
                        &ltime->day);
821
    break;
822
  }
823
  case INTERVAL_DAY:
824
  case INTERVAL_WEEK:
825
    period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
826
             sign * (long) interval.day);
827
    /* Daynumber from year 0 to 9999-12-31 */
828
    if ((ulong) period > MAX_DAY_NUMBER)
829
      goto invalid_date;
830
    get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
831
    break;
832
  case INTERVAL_YEAR:
833
    ltime->year+= sign * (long) interval.year;
834
    if ((ulong) ltime->year >= 10000L)
835
      goto invalid_date;
836
    if (ltime->month == 2 && ltime->day == 29 &&
837
	calc_days_in_year(ltime->year) != 366)
838
      ltime->day=28;				// Was leap-year
839
    break;
840
  case INTERVAL_YEAR_MONTH:
841
  case INTERVAL_QUARTER:
842
  case INTERVAL_MONTH:
843
    period= (ltime->year*12 + sign * (long) interval.year*12 +
844
	     ltime->month-1 + sign * (long) interval.month);
845
    if ((ulong) period >= 120000L)
846
      goto invalid_date;
847
    ltime->year= (uint) (period / 12);
848
    ltime->month= (uint) (period % 12L)+1;
849
    /* Adjust day if the new month doesn't have enough days */
850
    if (ltime->day > days_in_month[ltime->month-1])
851
    {
852
      ltime->day = days_in_month[ltime->month-1];
853
      if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
854
	ltime->day++;				// Leap-year
855
    }
856
    break;
857
  default:
858
    goto null_date;
859
  }
860
861
  return 0;					// Ok
862
863
invalid_date:
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
864
  push_warning_printf(current_thd, DRIZZLE_ERROR::WARN_LEVEL_WARN,
1 by brian
clean slate
865
                      ER_DATETIME_FUNCTION_OVERFLOW,
866
                      ER(ER_DATETIME_FUNCTION_OVERFLOW),
867
                      "datetime");
868
null_date:
869
  return 1;
870
}
871
872
873
/*
874
  Calculate difference between two datetime values as seconds + microseconds.
875
876
  SYNOPSIS
877
    calc_time_diff()
878
      l_time1         - TIME/DATE/DATETIME value
879
      l_time2         - TIME/DATE/DATETIME value
880
      l_sign          - 1 absolute values are substracted,
881
                        -1 absolute values are added.
882
      seconds_out     - Out parameter where difference between
883
                        l_time1 and l_time2 in seconds is stored.
884
      microseconds_out- Out parameter where microsecond part of difference
885
                        between l_time1 and l_time2 is stored.
886
887
  NOTE
888
    This function calculates difference between l_time1 and l_time2 absolute
889
    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.
890
    signs into account (i.e. for DRIZZLE_TIME values).
1 by brian
clean slate
891
892
  RETURN VALUES
893
    Returns sign of difference.
894
    1 means negative result
895
    0 means positive result
896
897
*/
898
899
bool
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
900
calc_time_diff(DRIZZLE_TIME *l_time1, DRIZZLE_TIME *l_time2, int l_sign, int64_t *seconds_out,
1 by brian
clean slate
901
               long *microseconds_out)
902
{
903
  long days;
904
  bool neg;
152 by Brian Aker
longlong replacement
905
  int64_t microseconds;
1 by brian
clean slate
906
907
  /*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
908
    We suppose that if first argument is DRIZZLE_TIMESTAMP_TIME
1 by brian
clean slate
909
    the second argument should be TIMESTAMP_TIME also.
910
    We should check it before calc_time_diff call.
911
  */
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
912
  if (l_time1->time_type == DRIZZLE_TIMESTAMP_TIME)  // Time value
1 by brian
clean slate
913
    days= (long)l_time1->day - l_sign * (long)l_time2->day;
914
  else
915
  {
916
    days= calc_daynr((uint) l_time1->year,
917
		     (uint) l_time1->month,
918
		     (uint) l_time1->day);
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
919
    if (l_time2->time_type == DRIZZLE_TIMESTAMP_TIME)
1 by brian
clean slate
920
      days-= l_sign * (long)l_time2->day;
921
    else
922
      days-= l_sign*calc_daynr((uint) l_time2->year,
923
			       (uint) l_time2->month,
924
			       (uint) l_time2->day);
925
  }
926
398.1.8 by Monty Taylor
Enabled -Wlong-long.
927
  microseconds= ((int64_t)days*86400L +
152 by Brian Aker
longlong replacement
928
                 (int64_t)(l_time1->hour*3600L +
1 by brian
clean slate
929
                            l_time1->minute*60L +
930
                            l_time1->second) -
152 by Brian Aker
longlong replacement
931
                 l_sign*(int64_t)(l_time2->hour*3600L +
1 by brian
clean slate
932
                                   l_time2->minute*60L +
398.1.8 by Monty Taylor
Enabled -Wlong-long.
933
                                   l_time2->second)) * 1000000L +
152 by Brian Aker
longlong replacement
934
                (int64_t)l_time1->second_part -
935
                l_sign*(int64_t)l_time2->second_part;
1 by brian
clean slate
936
937
  neg= 0;
938
  if (microseconds < 0)
939
  {
940
    microseconds= -microseconds;
941
    neg= 1;
942
  }
943
  *seconds_out= microseconds/1000000L;
944
  *microseconds_out= (long) (microseconds%1000000L);
945
  return neg;
946
}
947
948
949
/*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
950
  Compares 2 DRIZZLE_TIME structures
1 by brian
clean slate
951
952
  SYNOPSIS
953
    my_time_compare()
954
955
      a - first time
956
      b - second time
957
958
  RETURN VALUE
959
   -1   - a < b
960
    0   - a == b
961
    1   - a > b
962
963
  NOTES
964
    TIME.second_part is not considered during comparison
965
*/
966
967
int
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
968
my_time_compare(DRIZZLE_TIME *a, DRIZZLE_TIME *b)
1 by brian
clean slate
969
{
157 by Brian Aker
Second pass cleanup on removal of my_uint types
970
  uint64_t a_t= TIME_to_uint64_t_datetime(a);
971
  uint64_t b_t= TIME_to_uint64_t_datetime(b);
1 by brian
clean slate
972
973
  if (a_t > b_t)
974
    return 1;
975
  else if (a_t < b_t)
976
    return -1;
977
978
  return 0;
979
}
980
981
#endif