178
174
for (month_pos= days_in_month ;
179
day_of_year > (uint32_t) *month_pos ;
175
day_of_year > (uint) *month_pos ;
180
176
day_of_year-= *(month_pos++), (*ret_month)++)
183
179
*ret_day=day_of_year+leap_day;
184
/* Functions to handle periods */
186
ulong convert_period_to_month(ulong period)
191
if ((a=period/100) < YY_PART_YEAR)
200
ulong convert_month_to_period(ulong month)
205
if ((year=month/12) < 100)
207
year+=(year < YY_PART_YEAR) ? 2000 : 1900;
209
return year*100+month%12+1;
189
Convert a timestamp string to a DRIZZLE_TIME value and produce a warning
214
Convert a timestamp string to a MYSQL_TIME value and produce a warning
190
215
if string was truncated during conversion.
193
218
See description of str_to_datetime() for more information.
196
enum enum_drizzle_timestamp_type
197
str_to_datetime_with_warn(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
222
str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time,
201
Session *session= current_session;
202
enum enum_drizzle_timestamp_type ts_type;
226
THD *thd= current_thd;
227
timestamp_type ts_type;
204
229
ts_type= str_to_datetime(str, length, l_time,
205
(flags | (session->variables.sql_mode &
230
(flags | (thd->variables.sql_mode &
206
231
(MODE_INVALID_DATES |
207
232
MODE_NO_ZERO_DATE))),
209
if (was_cut || ts_type <= DRIZZLE_TIMESTAMP_ERROR)
210
make_truncated_value_warning(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
211
str, length, ts_type, NULL);
234
if (was_cut || ts_type <= MYSQL_TIMESTAMP_ERROR)
235
make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
236
str, length, ts_type, NullS);
216
Convert a time string to a DRIZZLE_TIME struct and produce a warning
242
Convert a datetime from broken-down MYSQL_TIME representation to corresponding
248
t - datetime in broken-down representation,
249
in_dst_time_gap - pointer to bool which is set to true if t represents
250
value which doesn't exists (falls into the spring
251
time-gap) or to false otherwise.
254
Number seconds in UTC since start of Unix Epoch corresponding to t.
255
0 - t contains datetime value which is out of TIMESTAMP range.
258
my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, my_bool *in_dst_time_gap)
263
thd->time_zone_used= 1;
265
timestamp= thd->variables.time_zone->TIME_to_gmt_sec(t, in_dst_time_gap);
271
/* If we are here we have range error. */
277
Convert a time string to a MYSQL_TIME struct and produce a warning
217
278
if string was cut during conversion.
220
281
See str_to_time() for more info.
223
str_to_time_with_warn(const char *str, uint32_t length, DRIZZLE_TIME *l_time)
284
str_to_time_with_warn(const char *str, uint length, MYSQL_TIME *l_time)
226
287
bool ret_val= str_to_time(str, length, l_time, &warning);
227
288
if (ret_val || warning)
228
make_truncated_value_warning(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
229
str, length, DRIZZLE_TIMESTAMP_TIME, NULL);
289
make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
290
str, length, MYSQL_TIMESTAMP_TIME, NullS);
262
323
to->second_part= microseconds;
265
void make_time(const DRIZZLE_TIME *l_time, String *str)
267
str->alloc(MAX_DATE_STRING_REP_LENGTH);
268
uint32_t length= (uint32_t) my_time_to_str(l_time, str->c_ptr());
270
str->set_charset(&my_charset_bin);
274
void make_date(const DRIZZLE_TIME *l_time, String *str)
276
str->alloc(MAX_DATE_STRING_REP_LENGTH);
277
uint32_t length= (uint32_t) my_date_to_str(l_time, str->c_ptr());
279
str->set_charset(&my_charset_bin);
283
void make_datetime(const DRIZZLE_TIME *l_time, String *str)
285
str->alloc(MAX_DATE_STRING_REP_LENGTH);
286
uint32_t length= (uint32_t) my_datetime_to_str(l_time, str->c_ptr());
288
str->set_charset(&my_charset_bin);
292
void make_truncated_value_warning(Session *session, DRIZZLE_ERROR::enum_warning_level level,
328
Parse a format string specification
331
parse_date_time_format()
332
format_type Format of string (time, date or datetime)
333
format_str String to parse
334
format_length Length of string
335
date_time_format Format to fill in
338
Fills in date_time_format->positions for all date time parts.
340
positions marks the position for a datetime element in the format string.
341
The position array elements are in the following order:
342
YYYY-DD-MM HH-MM-DD.FFFFFF AM
345
If positions[0]= 5, it means that year will be the forth element to
346
read from the parsed date string.
353
bool parse_date_time_format(timestamp_type format_type,
354
const char *format, uint format_length,
355
DATE_TIME_FORMAT *date_time_format)
357
uint offset= 0, separators= 0;
358
const char *ptr= format, *format_str;
359
const char *end= ptr+format_length;
360
uchar *dt_pos= date_time_format->positions;
361
/* need_p is set if we are using AM/PM format */
362
bool need_p= 0, allow_separator= 0;
363
ulong part_map= 0, separator_map= 0;
364
const char *parts[16];
366
date_time_format->time_separator= 0;
367
date_time_format->flag= 0; // For future
370
Fill position with 'dummy' arguments to found out if a format tag is
371
used twice (This limit's the format to 255 characters, but this is ok)
373
dt_pos[0]= dt_pos[1]= dt_pos[2]= dt_pos[3]=
374
dt_pos[4]= dt_pos[5]= dt_pos[6]= dt_pos[7]= 255;
376
for (; ptr != end; ptr++)
378
if (*ptr == '%' && ptr+1 != end)
397
need_p= 1; // Need AM/PM
412
if (dt_pos[5] != offset-1 || ptr[-2] != '.')
413
return 1; // Wrong usage of %f
416
if (offset == 0) // Can't be first
421
return 1; // Unknown controll char
423
if (dt_pos[position] != 255) // Don't allow same tag twice
425
parts[position]= ptr-1;
428
If switching from time to date, ensure that all time parts
431
if (part_map && position <= 2 && !(part_map & (1 | 2 | 4)))
433
part_map|= (ulong) 1 << position;
434
dt_pos[position]= offset++;
440
Don't allow any characters in format as this could easily confuse
443
if (!allow_separator)
444
return 1; // No separator here
445
allow_separator= 0; // Don't allow two separators
447
/* Store in separator_map which parts are punct characters */
448
if (my_ispunct(&my_charset_latin1, *ptr))
449
separator_map|= (ulong) 1 << (offset-1);
450
else if (!my_isspace(&my_charset_latin1, *ptr))
455
/* If no %f, specify it after seconds. Move %p up, if necessary */
456
if ((part_map & 32) && !(part_map & 64))
458
dt_pos[6]= dt_pos[5] +1;
459
parts[6]= parts[5]; // For later test in (need_p)
460
if (dt_pos[6] == dt_pos[7]) // Move %p one step up if used
465
Check that we have not used a non legal format specifier and that all
466
format specifiers have been used
468
The last test is to ensure that %p is used if and only if
471
if ((format_type == MYSQL_TIMESTAMP_DATETIME &&
472
!test_all_bits(part_map, (1 | 2 | 4 | 8 | 16 | 32))) ||
473
(format_type == MYSQL_TIMESTAMP_DATE && part_map != (1 | 2 | 4)) ||
474
(format_type == MYSQL_TIMESTAMP_TIME &&
475
!test_all_bits(part_map, 8 | 16 | 32)) ||
476
!allow_separator || // %option should be last
477
(need_p && dt_pos[6] +1 != dt_pos[7]) ||
478
(need_p ^ (dt_pos[7] != 255)))
481
if (dt_pos[6] != 255) // If fractional seconds
483
/* remove fractional seconds from later tests */
484
uint pos= dt_pos[6] -1;
485
/* Remove separator before %f from sep map */
486
separator_map= ((separator_map & ((ulong) (1 << pos)-1)) |
487
((separator_map & ~((ulong) (1 << pos)-1)) >> 1));
490
separators--; // There is always a separator
491
need_p= 1; // force use of separators
496
Remove possible separator before %p from sep_map
497
(This can either be at position 3, 4, 6 or 7) h.m.d.%f %p
499
if (dt_pos[7] != 255)
501
if (need_p && parts[7] != parts[6]+2)
505
Calculate if %p is in first or last part of the datetime field
507
At this point we have either %H-%i-%s %p 'year parts' or
508
'year parts' &H-%i-%s %p" as %f was removed above
510
offset= dt_pos[6] <= 3 ? 3 : 6;
511
/* Remove separator before %p from sep map */
512
separator_map= ((separator_map & ((ulong) (1 << offset)-1)) |
513
((separator_map & ~((ulong) (1 << offset)-1)) >> 1));
516
switch (format_type) {
517
case MYSQL_TIMESTAMP_DATE:
518
format_str= known_date_time_formats[INTERNAL_FORMAT].date_format;
520
case MYSQL_TIMESTAMP_TIME:
522
format_str=known_date_time_formats[INTERNAL_FORMAT].time_format;
525
If there is no separators, allow the internal format as we can read
526
this. If separators are used, they must be between each part
528
if (format_length == 6 && !need_p &&
529
!my_strnncoll(&my_charset_bin,
530
(const uchar *) format, 6,
531
(const uchar *) format_str, 6))
533
if (separator_map == (1 | 2))
535
if (format_type == MYSQL_TIMESTAMP_TIME)
537
if (*(format+2) != *(format+5))
539
/* Store the character used for time formats */
540
date_time_format->time_separator= *(format+2);
545
case MYSQL_TIMESTAMP_DATETIME:
547
If there is no separators, allow the internal format as we can read
548
this. If separators are used, they must be between each part.
549
Between DATE and TIME we also allow space as separator
551
if ((format_length == 12 && !need_p &&
552
!my_strnncoll(&my_charset_bin,
553
(const uchar *) format, 12,
554
(const uchar*) known_date_time_formats[INTERNAL_FORMAT].datetime_format,
556
(separators == 5 && separator_map == (1 | 2 | 8 | 16)))
568
Create a DATE_TIME_FORMAT object from a format string specification
571
date_time_format_make()
572
format_type Format to parse (time, date or datetime)
573
format_str String to parse
574
format_length Length of string
577
The returned object should be freed with my_free()
585
*date_time_format_make(timestamp_type format_type,
586
const char *format_str, uint format_length)
588
DATE_TIME_FORMAT tmp;
590
if (format_length && format_length < 255 &&
591
!parse_date_time_format(format_type, format_str,
592
format_length, &tmp))
594
tmp.format.str= (char*) format_str;
595
tmp.format.length= format_length;
596
return date_time_format_copy((THD *)0, &tmp);
603
Create a copy of a DATE_TIME_FORMAT object
606
date_and_time_format_copy()
607
thd Set if variable should be allocated in thread mem
608
format format to copy
611
The returned object should be freed with my_free()
618
DATE_TIME_FORMAT *date_time_format_copy(THD *thd, DATE_TIME_FORMAT *format)
620
DATE_TIME_FORMAT *new_format;
621
ulong length= sizeof(*format) + format->format.length + 1;
624
new_format= (DATE_TIME_FORMAT *) thd->alloc(length);
626
new_format= (DATE_TIME_FORMAT *) my_malloc(length, MYF(MY_WME));
629
/* Put format string after current pos */
630
new_format->format.str= (char*) (new_format+1);
631
memcpy((char*) new_format->positions, (char*) format->positions,
632
sizeof(format->positions));
633
new_format->time_separator= format->time_separator;
634
/* We make the string null terminated for easy printf in SHOW VARIABLES */
635
memcpy((char*) new_format->format.str, format->format.str,
636
format->format.length);
637
new_format->format.str[format->format.length]= 0;
638
new_format->format.length= format->format.length;
644
KNOWN_DATE_TIME_FORMAT known_date_time_formats[6]=
646
{"USA", "%m.%d.%Y", "%Y-%m-%d %H.%i.%s", "%h:%i:%s %p" },
647
{"JIS", "%Y-%m-%d", "%Y-%m-%d %H:%i:%s", "%H:%i:%s" },
648
{"ISO", "%Y-%m-%d", "%Y-%m-%d %H:%i:%s", "%H:%i:%s" },
649
{"EUR", "%d.%m.%Y", "%Y-%m-%d %H.%i.%s", "%H.%i.%s" },
650
{"INTERNAL", "%Y%m%d", "%Y%m%d%H%i%s", "%H%i%s" },
656
Return format string according format name.
657
If name is unknown, result is NULL
660
const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
664
case MYSQL_TIMESTAMP_DATE:
665
return format->date_format;
666
case MYSQL_TIMESTAMP_DATETIME:
667
return format->datetime_format;
668
case MYSQL_TIMESTAMP_TIME:
669
return format->time_format;
671
DBUG_ASSERT(0); // Impossible
676
/****************************************************************************
677
Functions to create default time/date/datetime strings
680
For the moment the DATE_TIME_FORMAT argument is ignored becasue
681
MySQL doesn't support comparing of date/time/datetime strings that
682
are not in arbutary order as dates are compared as strings in some
684
This functions don't check that given MYSQL_TIME structure members are
685
in valid range. If they are not, return value won't reflect any
686
valid date either. Additionally, make_time doesn't take into
687
account time->day member: it's assumed that days have been converted
689
****************************************************************************/
691
void make_time(const DATE_TIME_FORMAT *format __attribute__((unused)),
692
const MYSQL_TIME *l_time, String *str)
694
uint length= (uint) my_time_to_str(l_time, (char*) str->ptr());
696
str->set_charset(&my_charset_bin);
700
void make_date(const DATE_TIME_FORMAT *format __attribute__((unused)),
701
const MYSQL_TIME *l_time, String *str)
703
uint length= (uint) my_date_to_str(l_time, (char*) str->ptr());
705
str->set_charset(&my_charset_bin);
709
void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)),
710
const MYSQL_TIME *l_time, String *str)
712
uint length= (uint) my_datetime_to_str(l_time, (char*) str->ptr());
714
str->set_charset(&my_charset_bin);
718
void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
293
719
const char *str_val,
295
enum enum_drizzle_timestamp_type time_type,
720
uint str_length, timestamp_type time_type,
296
721
const char *field_name)
298
char warn_buff[DRIZZLE_ERRMSG_SIZE];
723
char warn_buff[MYSQL_ERRMSG_SIZE];
299
724
const char *type_str;
300
CHARSET_INFO *cs= &my_charset_utf8_general_ci;
725
CHARSET_INFO *cs= &my_charset_latin1;
302
String str(buff,(uint32_t) sizeof(buff), system_charset_info);
727
String str(buff,(uint32) sizeof(buff), system_charset_info);
303
728
str.copy(str_val, str_length, system_charset_info);
304
729
str[str_length]= 0; // Ensure we have end 0 for snprintf
306
731
switch (time_type) {
307
case DRIZZLE_TIMESTAMP_DATE:
732
case MYSQL_TIMESTAMP_DATE:
308
733
type_str= "date";
310
case DRIZZLE_TIMESTAMP_TIME:
735
case MYSQL_TIMESTAMP_TIME:
311
736
type_str= "time";
313
case DRIZZLE_TIMESTAMP_DATETIME: // FALLTHROUGH
738
case MYSQL_TIMESTAMP_DATETIME: // FALLTHROUGH
315
740
type_str= "datetime";