1
/* Copyright (C) 2000-2006 MySQL AB
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.
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.
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 */
1
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
4
* Copyright (C) 2008 Sun Microsystems
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; version 2 of the License.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
21
/* Functions to handle date and time */
19
#include "mysql_priv.h"
23
/* Some functions to calculate dates */
23
#include <drizzled/server_includes.h>
24
#include <drizzled/error.h>
25
#include <drizzled/util/test.h>
26
#include <drizzled/tztime.h>
27
#include <drizzled/item/timefunc.h>
28
#include <drizzled/session.h>
30
/* Some functions to calculate dates */
89
96
If set The week that contains the first
90
97
'first-day-of-week' is week 1.
92
99
ISO 8601:1988 means that if the week containing January 1 has
93
100
four or more days in the new year, then it is week 1;
94
101
Otherwise it is the last week of the previous year, and the
95
102
next week is week 1.
98
uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year)
105
uint32_t calc_week(DRIZZLE_TIME *l_time, uint32_t week_behaviour, uint32_t *year)
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);
108
uint32_t daynr= calc_daynr(l_time->year,l_time->month,l_time->day);
109
uint32_t first_daynr= calc_daynr(l_time->year,1,1);
103
110
bool monday_first= test(week_behaviour & WEEK_MONDAY_FIRST);
104
111
bool week_year= test(week_behaviour & WEEK_YEAR);
105
112
bool first_weekday= test(week_behaviour & WEEK_FIRST_WEEKDAY);
107
uint weekday=calc_weekday(first_daynr, !monday_first);
114
uint32_t weekday=calc_weekday(first_daynr, !monday_first);
108
115
*year=l_time->year;
110
117
if (l_time->month == 1 && l_time->day <= 7-weekday)
138
145
/* Change a daynr to year, month and day */
139
146
/* Daynr 0 is returned as date 00.00.00 */
141
void get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month,
148
void get_date_from_daynr(long daynr,uint32_t *ret_year,uint32_t *ret_month,
144
uint year,temp,leap_day,day_of_year,days_in_year;
151
uint32_t year,temp,leap_day,day_of_year,days_in_year;
152
unsigned char *month_pos;
147
154
if (daynr <= 365L || daynr >= 3652500)
148
155
{ /* Fix if wrong daynr */
212
Convert a timestamp string to a MYSQL_TIME value and produce a warning
219
Convert a timestamp string to a DRIZZLE_TIME value and produce a warning
213
220
if string was truncated during conversion.
216
223
See description of str_to_datetime() for more information.
220
str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time,
226
enum enum_drizzle_timestamp_type
227
str_to_datetime_with_warn(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
224
THD *thd= current_thd;
225
timestamp_type ts_type;
231
Session *session= current_session;
232
enum enum_drizzle_timestamp_type ts_type;
227
234
ts_type= str_to_datetime(str, length, l_time,
228
(flags | (thd->variables.sql_mode &
235
(flags | (session->variables.sql_mode &
229
236
(MODE_INVALID_DATES |
230
237
MODE_NO_ZERO_DATE))),
232
if (was_cut || ts_type <= MYSQL_TIMESTAMP_ERROR)
233
make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
234
str, length, ts_type, NullS);
239
if (was_cut || ts_type <= DRIZZLE_TIMESTAMP_ERROR)
240
make_truncated_value_warning(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
241
str, length, ts_type, NULL);
240
Convert a datetime from broken-down MYSQL_TIME representation to corresponding
247
Convert a datetime from broken-down DRIZZLE_TIME representation to corresponding
244
251
TIME_to_timestamp()
246
t - datetime in broken-down representation,
252
session - current thread
253
t - datetime in broken-down representation,
247
254
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
255
value which doesn't exists (falls into the spring
249
256
time-gap) or to false otherwise.
252
259
Number seconds in UTC since start of Unix Epoch corresponding to t.
253
260
0 - t contains datetime value which is out of TIMESTAMP range.
256
my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, bool *in_dst_time_gap)
263
my_time_t TIME_to_timestamp(Session *session, const DRIZZLE_TIME *t,
264
bool *in_dst_time_gap)
258
266
my_time_t timestamp;
260
268
*in_dst_time_gap= 0;
261
thd->time_zone_used= 1;
263
timestamp= thd->variables.time_zone->TIME_to_gmt_sec(t, in_dst_time_gap);
270
timestamp= session->variables.time_zone->TIME_to_gmt_sec(t, in_dst_time_gap);
266
273
return timestamp;
275
Convert a time string to a MYSQL_TIME struct and produce a warning
282
Convert a time string to a DRIZZLE_TIME struct and produce a warning
276
283
if string was cut during conversion.
279
286
See str_to_time() for more info.
282
str_to_time_with_warn(const char *str, uint length, MYSQL_TIME *l_time)
289
str_to_time_with_warn(const char *str, uint32_t length, DRIZZLE_TIME *l_time)
285
292
bool ret_val= str_to_time(str, length, l_time, &warning);
286
293
if (ret_val || warning)
287
make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
288
str, length, MYSQL_TIMESTAMP_TIME, NullS);
294
make_truncated_value_warning(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
295
str, length, DRIZZLE_TIMESTAMP_TIME, NULL);
351
bool parse_date_time_format(timestamp_type format_type,
352
const char *format, uint format_length,
358
bool parse_date_time_format(enum enum_drizzle_timestamp_type format_type,
359
const char *format, uint32_t format_length,
353
360
DATE_TIME_FORMAT *date_time_format)
355
uint offset= 0, separators= 0;
362
uint32_t offset= 0, separators= 0;
356
363
const char *ptr= format, *format_str;
357
364
const char *end= ptr+format_length;
358
uchar *dt_pos= date_time_format->positions;
365
unsigned char *dt_pos= date_time_format->positions;
359
366
/* need_p is set if we are using AM/PM format */
360
367
bool need_p= 0, allow_separator= 0;
361
ulong part_map= 0, separator_map= 0;
368
uint32_t part_map= 0, separator_map= 0;
362
369
const char *parts[16];
364
371
date_time_format->time_separator= 0;
443
450
allow_separator= 0; // Don't allow two separators
445
452
/* Store in separator_map which parts are punct characters */
446
if (my_ispunct(&my_charset_latin1, *ptr))
447
separator_map|= (ulong) 1 << (offset-1);
448
else if (!my_isspace(&my_charset_latin1, *ptr))
453
if (my_ispunct(&my_charset_utf8_general_ci, *ptr))
454
separator_map|= 1 << (offset-1);
455
else if (!my_isspace(&my_charset_utf8_general_ci, *ptr))
466
473
The last test is to ensure that %p is used if and only if
469
if ((format_type == MYSQL_TIMESTAMP_DATETIME &&
470
!test_all_bits(part_map, (1 | 2 | 4 | 8 | 16 | 32))) ||
471
(format_type == MYSQL_TIMESTAMP_DATE && part_map != (1 | 2 | 4)) ||
472
(format_type == MYSQL_TIMESTAMP_TIME &&
473
!test_all_bits(part_map, 8 | 16 | 32)) ||
476
if ((format_type == DRIZZLE_TIMESTAMP_DATETIME &&
477
!test_all_bits(part_map, (uint32_t) (1 | 2 | 4 | 8 | 16 | 32))) ||
478
(format_type == DRIZZLE_TIMESTAMP_DATE && part_map != (1 | 2 | 4)) ||
479
(format_type == DRIZZLE_TIMESTAMP_TIME &&
480
!test_all_bits(part_map, (uint32_t) (8 | 16 | 32))) ||
474
481
!allow_separator || // %option should be last
475
482
(need_p && dt_pos[6] +1 != dt_pos[7]) ||
476
483
(need_p ^ (dt_pos[7] != 255)))
479
486
if (dt_pos[6] != 255) // If fractional seconds
481
488
/* remove fractional seconds from later tests */
482
uint pos= dt_pos[6] -1;
489
uint32_t pos= dt_pos[6] -1;
483
490
/* Remove separator before %f from sep map */
484
separator_map= ((separator_map & ((ulong) (1 << pos)-1)) |
485
((separator_map & ~((ulong) (1 << pos)-1)) >> 1));
491
separator_map= ((separator_map & ((1 << pos)-1)) |
492
((separator_map & ~((1 << pos)-1)) >> 1));
488
495
separators--; // There is always a separator
489
496
need_p= 1; // force use of separators
508
515
offset= dt_pos[6] <= 3 ? 3 : 6;
509
516
/* Remove separator before %p from sep map */
510
separator_map= ((separator_map & ((ulong) (1 << offset)-1)) |
511
((separator_map & ~((ulong) (1 << offset)-1)) >> 1));
517
separator_map= ((separator_map & ((1 << offset)-1)) |
518
((separator_map & ~((1 << offset)-1)) >> 1));
514
521
switch (format_type) {
515
case MYSQL_TIMESTAMP_DATE:
522
case DRIZZLE_TIMESTAMP_DATE:
516
523
format_str= known_date_time_formats[INTERNAL_FORMAT].date_format;
517
524
/* fall through */
518
case MYSQL_TIMESTAMP_TIME:
525
case DRIZZLE_TIMESTAMP_TIME:
520
527
format_str=known_date_time_formats[INTERNAL_FORMAT].time_format;
526
533
if (format_length == 6 && !need_p &&
527
534
!my_strnncoll(&my_charset_bin,
528
(const uchar *) format, 6,
529
(const uchar *) format_str, 6))
535
(const unsigned char *) format, 6,
536
(const unsigned char *) format_str, 6))
531
538
if (separator_map == (1 | 2))
533
if (format_type == MYSQL_TIMESTAMP_TIME)
540
if (format_type == DRIZZLE_TIMESTAMP_TIME)
535
542
if (*(format+2) != *(format+5))
543
case MYSQL_TIMESTAMP_DATETIME:
550
case DRIZZLE_TIMESTAMP_DATETIME:
545
552
If there is no separators, allow the internal format as we can read
546
553
this. If separators are used, they must be between each part.
547
554
Between DATE and TIME we also allow space as separator
549
556
if ((format_length == 12 && !need_p &&
550
!my_strnncoll(&my_charset_bin,
551
(const uchar *) format, 12,
552
(const uchar*) known_date_time_formats[INTERNAL_FORMAT].datetime_format,
557
!my_strnncoll(&my_charset_bin,
558
(const unsigned char *) format, 12,
559
(const unsigned char*) known_date_time_formats[INTERNAL_FORMAT].datetime_format,
554
561
(separators == 5 && separator_map == (1 | 2 | 8 | 16)))
604
611
date_and_time_format_copy()
605
thd Set if variable should be allocated in thread mem
612
session Set if variable should be allocated in thread mem
606
613
format format to copy
609
The returned object should be freed with my_free()
616
The returned object should be freed with free()
612
619
NULL ponter: Error
616
DATE_TIME_FORMAT *date_time_format_copy(THD *thd, DATE_TIME_FORMAT *format)
623
DATE_TIME_FORMAT *date_time_format_copy(Session *session, DATE_TIME_FORMAT *format)
618
625
DATE_TIME_FORMAT *new_format;
619
ulong length= sizeof(*format) + format->format.length + 1;
626
uint32_t length= sizeof(*format) + format->format.length + 1;
622
new_format= (DATE_TIME_FORMAT *) thd->alloc(length);
629
new_format= (DATE_TIME_FORMAT *) session->alloc(length);
624
new_format= (DATE_TIME_FORMAT *) my_malloc(length, MYF(MY_WME));
631
new_format= (DATE_TIME_FORMAT *) malloc(length);
627
634
/* Put format string after current pos */
628
635
new_format->format.str= (char*) (new_format+1);
629
memcpy((char*) new_format->positions, (char*) format->positions,
636
memcpy(new_format->positions, format->positions,
630
637
sizeof(format->positions));
631
638
new_format->time_separator= format->time_separator;
632
639
/* We make the string null terminated for easy printf in SHOW VARIABLES */
633
memcpy((char*) new_format->format.str, format->format.str,
640
memcpy(new_format->format.str, format->format.str,
634
641
format->format.length);
635
642
new_format->format.str[format->format.length]= 0;
636
643
new_format->format.length= format->format.length;
658
665
const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
666
enum enum_drizzle_timestamp_type type)
662
case MYSQL_TIMESTAMP_DATE:
669
case DRIZZLE_TIMESTAMP_DATE:
663
670
return format->date_format;
664
case MYSQL_TIMESTAMP_DATETIME:
671
case DRIZZLE_TIMESTAMP_DATETIME:
665
672
return format->datetime_format;
666
case MYSQL_TIMESTAMP_TIME:
673
case DRIZZLE_TIMESTAMP_TIME:
667
674
return format->time_format;
669
676
assert(0); // Impossible
674
681
/****************************************************************************
675
682
Functions to create default time/date/datetime strings
678
685
For the moment the DATE_TIME_FORMAT argument is ignored becasue
679
686
MySQL doesn't support comparing of date/time/datetime strings that
680
687
are not in arbutary order as dates are compared as strings in some
682
This functions don't check that given MYSQL_TIME structure members are
683
in valid range. If they are not, return value won't reflect any
689
This functions don't check that given DRIZZLE_TIME structure members are
690
in valid range. If they are not, return value won't reflect any
684
691
valid date either. Additionally, make_time doesn't take into
685
692
account time->day member: it's assumed that days have been converted
686
693
to hours already.
687
694
****************************************************************************/
689
696
void make_time(const DATE_TIME_FORMAT *format __attribute__((unused)),
690
const MYSQL_TIME *l_time, String *str)
697
const DRIZZLE_TIME *l_time, String *str)
692
uint length= (uint) my_time_to_str(l_time, (char*) str->ptr());
699
uint32_t length= (uint) my_time_to_str(l_time, (char*) str->ptr());
693
700
str->length(length);
694
701
str->set_charset(&my_charset_bin);
698
705
void make_date(const DATE_TIME_FORMAT *format __attribute__((unused)),
699
const MYSQL_TIME *l_time, String *str)
706
const DRIZZLE_TIME *l_time, String *str)
701
uint length= (uint) my_date_to_str(l_time, (char*) str->ptr());
708
uint32_t length= (uint) my_date_to_str(l_time, (char*) str->ptr());
702
709
str->length(length);
703
710
str->set_charset(&my_charset_bin);
707
714
void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)),
708
const MYSQL_TIME *l_time, String *str)
715
const DRIZZLE_TIME *l_time, String *str)
710
uint length= (uint) my_datetime_to_str(l_time, (char*) str->ptr());
717
uint32_t length= (uint) my_datetime_to_str(l_time, (char*) str->ptr());
711
718
str->length(length);
712
719
str->set_charset(&my_charset_bin);
716
void make_truncated_value_warning(THD *thd, MYSQL_ERROR::enum_warning_level level,
723
void make_truncated_value_warning(Session *session, DRIZZLE_ERROR::enum_warning_level level,
717
724
const char *str_val,
718
uint str_length, timestamp_type time_type,
726
enum enum_drizzle_timestamp_type time_type,
719
727
const char *field_name)
721
char warn_buff[MYSQL_ERRMSG_SIZE];
729
char warn_buff[DRIZZLE_ERRMSG_SIZE];
722
730
const char *type_str;
723
CHARSET_INFO *cs= &my_charset_latin1;
731
CHARSET_INFO *cs= &my_charset_utf8_general_ci;
725
733
String str(buff,(uint32_t) sizeof(buff), system_charset_info);
726
734
str.copy(str_val, str_length, system_charset_info);
727
735
str[str_length]= 0; // Ensure we have end 0 for snprintf
729
737
switch (time_type) {
730
case MYSQL_TIMESTAMP_DATE:
738
case DRIZZLE_TIMESTAMP_DATE:
731
739
type_str= "date";
733
case MYSQL_TIMESTAMP_TIME:
741
case DRIZZLE_TIMESTAMP_TIME:
734
742
type_str= "time";
736
case MYSQL_TIMESTAMP_DATETIME: // FALLTHROUGH
744
case DRIZZLE_TIMESTAMP_DATETIME: // FALLTHROUGH
738
746
type_str= "datetime";
742
750
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
743
751
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
744
752
type_str, str.c_ptr(), field_name,
745
(ulong) thd->row_count);
753
(uint32_t) session->row_count);
748
if (time_type > MYSQL_TIMESTAMP_ERROR)
756
if (time_type > DRIZZLE_TIMESTAMP_ERROR)
749
757
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
750
758
ER(ER_TRUNCATED_WRONG_VALUE),
751
759
type_str, str.c_ptr());
753
761
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
754
762
ER(ER_WRONG_VALUE), type_str, str.c_ptr());
756
push_warning(thd, level,
764
push_warning(session, level,
757
765
ER_TRUNCATED_WRONG_VALUE, warn_buff);
760
/* Daynumber from year 0 to 9999-12-31 */
761
#define MAX_DAY_NUMBER 3652424L
763
bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL interval)
768
bool date_add_interval(DRIZZLE_TIME *ltime, interval_type int_type, INTERVAL interval)
765
770
long period, sign;
824
829
period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
825
830
sign * (long) interval.day);
826
831
/* Daynumber from year 0 to 9999-12-31 */
827
if ((ulong) period > MAX_DAY_NUMBER)
832
if (period > MAX_DAY_NUMBER)
828
833
goto invalid_date;
829
834
get_date_from_daynr((long) period,<ime->year,<ime->month,<ime->day);
831
836
case INTERVAL_YEAR:
832
837
ltime->year+= sign * (long) interval.year;
833
if ((ulong) ltime->year >= 10000L)
838
if (ltime->year >= 10000L)
834
839
goto invalid_date;
835
840
if (ltime->month == 2 && ltime->day == 29 &&
836
841
calc_days_in_year(ltime->year) != 366)
904
909
int64_t microseconds;
907
We suppose that if first argument is MYSQL_TIMESTAMP_TIME
912
We suppose that if first argument is DRIZZLE_TIMESTAMP_TIME
908
913
the second argument should be TIMESTAMP_TIME also.
909
914
We should check it before calc_time_diff call.
911
if (l_time1->time_type == MYSQL_TIMESTAMP_TIME) // Time value
916
if (l_time1->time_type == DRIZZLE_TIMESTAMP_TIME) // Time value
912
917
days= (long)l_time1->day - l_sign * (long)l_time2->day;
915
920
days= calc_daynr((uint) l_time1->year,
916
921
(uint) l_time1->month,
917
922
(uint) l_time1->day);
918
if (l_time2->time_type == MYSQL_TIMESTAMP_TIME)
923
if (l_time2->time_type == DRIZZLE_TIMESTAMP_TIME)
919
924
days-= l_sign * (long)l_time2->day;
921
926
days-= l_sign*calc_daynr((uint) l_time2->year,