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>
29
/* Some functions to calculate dates */
96
101
next week is week 1.
99
uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year)
104
uint32_t calc_week(DRIZZLE_TIME *l_time, uint32_t week_behaviour, uint32_t *year)
102
ulong daynr=calc_daynr(l_time->year,l_time->month,l_time->day);
103
ulong first_daynr=calc_daynr(l_time->year,1,1);
107
uint32_t daynr= calc_daynr(l_time->year,l_time->month,l_time->day);
108
uint32_t first_daynr= calc_daynr(l_time->year,1,1);
104
109
bool monday_first= test(week_behaviour & WEEK_MONDAY_FIRST);
105
110
bool week_year= test(week_behaviour & WEEK_YEAR);
106
111
bool first_weekday= test(week_behaviour & WEEK_FIRST_WEEKDAY);
108
uint weekday=calc_weekday(first_daynr, !monday_first);
113
uint32_t weekday=calc_weekday(first_daynr, !monday_first);
109
114
*year=l_time->year;
111
116
if (l_time->month == 1 && l_time->day <= 7-weekday)
139
144
/* Change a daynr to year, month and day */
140
145
/* Daynr 0 is returned as date 00.00.00 */
142
void get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month,
147
void get_date_from_daynr(long daynr,uint32_t *ret_year,uint32_t *ret_month,
145
uint year,temp,leap_day,day_of_year,days_in_year;
147
DBUG_ENTER("get_date_from_daynr");
150
uint32_t year,temp,leap_day,day_of_year,days_in_year;
151
unsigned char *month_pos;
149
153
if (daynr <= 365L || daynr >= 3652500)
150
154
{ /* Fix if wrong daynr */
214
Convert a timestamp string to a MYSQL_TIME value and produce a warning
218
Convert a timestamp string to a DRIZZLE_TIME value and produce a warning
215
219
if string was truncated during conversion.
218
222
See description of str_to_datetime() for more information.
222
str_to_datetime_with_warn(const char *str, uint length, MYSQL_TIME *l_time,
225
enum enum_drizzle_timestamp_type
226
str_to_datetime_with_warn(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
226
THD *thd= current_thd;
227
timestamp_type ts_type;
230
Session *session= current_session;
231
enum enum_drizzle_timestamp_type ts_type;
229
233
ts_type= str_to_datetime(str, length, l_time,
230
(flags | (thd->variables.sql_mode &
234
(flags | (session->variables.sql_mode &
231
235
(MODE_INVALID_DATES |
232
236
MODE_NO_ZERO_DATE))),
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);
238
if (was_cut || ts_type <= DRIZZLE_TIMESTAMP_ERROR)
239
make_truncated_value_warning(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
240
str, length, ts_type, NULL);
242
Convert a datetime from broken-down MYSQL_TIME representation to corresponding
246
Convert a datetime from broken-down DRIZZLE_TIME representation to corresponding
246
250
TIME_to_timestamp()
251
session - current thread
248
252
t - datetime in broken-down representation,
249
253
in_dst_time_gap - pointer to bool which is set to true if t represents
250
254
value which doesn't exists (falls into the spring
255
259
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)
262
my_time_t TIME_to_timestamp(Session *session, const DRIZZLE_TIME *t,
263
bool *in_dst_time_gap)
260
265
my_time_t timestamp;
262
267
*in_dst_time_gap= 0;
263
thd->time_zone_used= 1;
268
session->time_zone_used= 1;
265
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);
268
273
return timestamp;
277
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
278
283
if string was cut during conversion.
281
286
See str_to_time() for more info.
284
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)
287
292
bool ret_val= str_to_time(str, length, l_time, &warning);
288
293
if (ret_val || warning)
289
make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
290
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);
308
313
to->second= (int) from->tm_sec;
311
void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds)
316
void calc_time_from_sec(DRIZZLE_TIME *to, long seconds, long microseconds)
314
319
// to->neg is not cleared, it may already be set to a useful value
315
to->time_type= MYSQL_TIMESTAMP_TIME;
320
to->time_type= DRIZZLE_TIMESTAMP_TIME;
353
bool parse_date_time_format(timestamp_type format_type,
354
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,
355
360
DATE_TIME_FORMAT *date_time_format)
357
uint offset= 0, separators= 0;
362
uint32_t offset= 0, separators= 0;
358
363
const char *ptr= format, *format_str;
359
364
const char *end= ptr+format_length;
360
uchar *dt_pos= date_time_format->positions;
365
unsigned char *dt_pos= date_time_format->positions;
361
366
/* need_p is set if we are using AM/PM format */
362
367
bool need_p= 0, allow_separator= 0;
363
ulong part_map= 0, separator_map= 0;
368
uint32_t part_map= 0, separator_map= 0;
364
369
const char *parts[16];
366
371
date_time_format->time_separator= 0;
445
450
allow_separator= 0; // Don't allow two separators
447
452
/* 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))
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))
468
473
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
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))) ||
476
481
!allow_separator || // %option should be last
477
482
(need_p && dt_pos[6] +1 != dt_pos[7]) ||
478
483
(need_p ^ (dt_pos[7] != 255)))
481
486
if (dt_pos[6] != 255) // If fractional seconds
483
488
/* remove fractional seconds from later tests */
484
uint pos= dt_pos[6] -1;
489
uint32_t pos= dt_pos[6] -1;
485
490
/* Remove separator before %f from sep map */
486
separator_map= ((separator_map & ((ulong) (1 << pos)-1)) |
487
((separator_map & ~((ulong) (1 << pos)-1)) >> 1));
491
separator_map= ((separator_map & ((1 << pos)-1)) |
492
((separator_map & ~((1 << pos)-1)) >> 1));
488
493
if (part_map & 64)
490
495
separators--; // There is always a separator
510
515
offset= dt_pos[6] <= 3 ? 3 : 6;
511
516
/* Remove separator before %p from sep map */
512
separator_map= ((separator_map & ((ulong) (1 << offset)-1)) |
513
((separator_map & ~((ulong) (1 << offset)-1)) >> 1));
517
separator_map= ((separator_map & ((1 << offset)-1)) |
518
((separator_map & ~((1 << offset)-1)) >> 1));
516
521
switch (format_type) {
517
case MYSQL_TIMESTAMP_DATE:
522
case DRIZZLE_TIMESTAMP_DATE:
518
523
format_str= known_date_time_formats[INTERNAL_FORMAT].date_format;
519
524
/* fall through */
520
case MYSQL_TIMESTAMP_TIME:
525
case DRIZZLE_TIMESTAMP_TIME:
522
527
format_str=known_date_time_formats[INTERNAL_FORMAT].time_format;
528
533
if (format_length == 6 && !need_p &&
529
534
!my_strnncoll(&my_charset_bin,
530
(const uchar *) format, 6,
531
(const uchar *) format_str, 6))
535
(const unsigned char *) format, 6,
536
(const unsigned char *) format_str, 6))
533
538
if (separator_map == (1 | 2))
535
if (format_type == MYSQL_TIMESTAMP_TIME)
540
if (format_type == DRIZZLE_TIMESTAMP_TIME)
537
542
if (*(format+2) != *(format+5))
551
556
if ((format_length == 12 && !need_p &&
552
557
!my_strnncoll(&my_charset_bin,
553
(const uchar *) format, 12,
554
(const uchar*) known_date_time_formats[INTERNAL_FORMAT].datetime_format,
558
(const unsigned char *) format, 12,
559
(const unsigned char*) known_date_time_formats[INTERNAL_FORMAT].datetime_format,
556
561
(separators == 5 && separator_map == (1 | 2 | 8 | 16)))
563
568
return 1; // Error
606
611
date_and_time_format_copy()
607
thd Set if variable should be allocated in thread mem
612
session Set if variable should be allocated in thread mem
608
613
format format to copy
611
The returned object should be freed with my_free()
616
The returned object should be freed with free()
614
619
NULL ponter: Error
618
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)
620
625
DATE_TIME_FORMAT *new_format;
621
ulong length= sizeof(*format) + format->format.length + 1;
626
uint32_t length= sizeof(*format) + format->format.length + 1;
624
new_format= (DATE_TIME_FORMAT *) thd->alloc(length);
629
new_format= (DATE_TIME_FORMAT *) session->alloc(length);
626
631
new_format= (DATE_TIME_FORMAT *) my_malloc(length, MYF(MY_WME));
629
634
/* Put format string after current pos */
630
635
new_format->format.str= (char*) (new_format+1);
631
memcpy((char*) new_format->positions, (char*) format->positions,
636
memcpy(new_format->positions, format->positions,
632
637
sizeof(format->positions));
633
638
new_format->time_separator= format->time_separator;
634
639
/* We make the string null terminated for easy printf in SHOW VARIABLES */
635
memcpy((char*) new_format->format.str, format->format.str,
640
memcpy(new_format->format.str, format->format.str,
636
641
format->format.length);
637
642
new_format->format.str[format->format.length]= 0;
638
643
new_format->format.length= format->format.length;
660
665
const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
666
enum enum_drizzle_timestamp_type type)
664
case MYSQL_TIMESTAMP_DATE:
669
case DRIZZLE_TIMESTAMP_DATE:
665
670
return format->date_format;
666
case MYSQL_TIMESTAMP_DATETIME:
671
case DRIZZLE_TIMESTAMP_DATETIME:
667
672
return format->datetime_format;
668
case MYSQL_TIMESTAMP_TIME:
673
case DRIZZLE_TIMESTAMP_TIME:
669
674
return format->time_format;
671
DBUG_ASSERT(0); // Impossible
676
assert(0); // Impossible
681
686
MySQL doesn't support comparing of date/time/datetime strings that
682
687
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
689
This functions don't check that given DRIZZLE_TIME structure members are
685
690
in valid range. If they are not, return value won't reflect any
686
691
valid date either. Additionally, make_time doesn't take into
687
692
account time->day member: it's assumed that days have been converted
689
694
****************************************************************************/
691
696
void make_time(const DATE_TIME_FORMAT *format __attribute__((unused)),
692
const MYSQL_TIME *l_time, String *str)
697
const DRIZZLE_TIME *l_time, String *str)
694
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());
695
700
str->length(length);
696
701
str->set_charset(&my_charset_bin);
700
705
void make_date(const DATE_TIME_FORMAT *format __attribute__((unused)),
701
const MYSQL_TIME *l_time, String *str)
706
const DRIZZLE_TIME *l_time, String *str)
703
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());
704
709
str->length(length);
705
710
str->set_charset(&my_charset_bin);
709
714
void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)),
710
const MYSQL_TIME *l_time, String *str)
715
const DRIZZLE_TIME *l_time, String *str)
712
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());
713
718
str->length(length);
714
719
str->set_charset(&my_charset_bin);
718
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,
719
724
const char *str_val,
720
uint str_length, timestamp_type time_type,
726
enum enum_drizzle_timestamp_type time_type,
721
727
const char *field_name)
723
char warn_buff[MYSQL_ERRMSG_SIZE];
729
char warn_buff[DRIZZLE_ERRMSG_SIZE];
724
730
const char *type_str;
725
CHARSET_INFO *cs= &my_charset_latin1;
731
CHARSET_INFO *cs= &my_charset_utf8_general_ci;
727
String str(buff,(uint32) sizeof(buff), system_charset_info);
733
String str(buff,(uint32_t) sizeof(buff), system_charset_info);
728
734
str.copy(str_val, str_length, system_charset_info);
729
735
str[str_length]= 0; // Ensure we have end 0 for snprintf
731
737
switch (time_type) {
732
case MYSQL_TIMESTAMP_DATE:
738
case DRIZZLE_TIMESTAMP_DATE:
733
739
type_str= "date";
735
case MYSQL_TIMESTAMP_TIME:
741
case DRIZZLE_TIMESTAMP_TIME:
736
742
type_str= "time";
738
case MYSQL_TIMESTAMP_DATETIME: // FALLTHROUGH
744
case DRIZZLE_TIMESTAMP_DATETIME: // FALLTHROUGH
740
746
type_str= "datetime";
744
750
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
745
751
ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
746
752
type_str, str.c_ptr(), field_name,
747
(ulong) thd->row_count);
753
(uint32_t) session->row_count);
750
if (time_type > MYSQL_TIMESTAMP_ERROR)
756
if (time_type > DRIZZLE_TIMESTAMP_ERROR)
751
757
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
752
758
ER(ER_TRUNCATED_WRONG_VALUE),
753
759
type_str, str.c_ptr());
755
761
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
756
762
ER(ER_WRONG_VALUE), type_str, str.c_ptr());
758
push_warning(thd, level,
764
push_warning(session, level,
759
765
ER_TRUNCATED_WRONG_VALUE, warn_buff);
762
768
/* Daynumber from year 0 to 9999-12-31 */
763
769
#define MAX_DAY_NUMBER 3652424L
765
bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, INTERVAL interval)
771
bool date_add_interval(DRIZZLE_TIME *ltime, interval_type int_type, INTERVAL interval)
767
773
long period, sign;
786
792
case INTERVAL_DAY_MINUTE:
787
793
case INTERVAL_DAY_HOUR:
789
longlong sec, days, daynr, microseconds, extra_sec;
790
ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date
795
int64_t sec, days, daynr, microseconds, extra_sec;
796
ltime->time_type= DRIZZLE_TIMESTAMP_DATETIME; // Return full date
791
797
microseconds= ltime->second_part + sign*interval.second_part;
792
798
extra_sec= microseconds/1000000L;
793
799
microseconds= microseconds%1000000L;
795
801
sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
797
sign* (longlong) (interval.day*3600*24L +
798
interval.hour*LL(3600)+interval.minute*LL(60)+
803
sign* (int64_t) (interval.day*3600*24L +
804
interval.hour*3600L+interval.minute*60L+
799
805
interval.second))+ extra_sec;
800
806
if (microseconds < 0)
802
microseconds+= LL(1000000);
808
microseconds+= 1000000L;
805
days= sec/(3600*LL(24));
806
sec-= days*3600*LL(24);
811
days= sec/(3600*24L);
812
818
ltime->second_part= (uint) microseconds;
813
819
ltime->second= (uint) (sec % 60);
815
821
ltime->hour= (uint) (sec/3600);
816
822
daynr= calc_daynr(ltime->year,ltime->month,1) + days;
817
823
/* Day number from year 0 to 9999-12-31 */
818
if ((ulonglong) daynr > MAX_DAY_NUMBER)
824
if ((uint64_t) daynr > MAX_DAY_NUMBER)
819
825
goto invalid_date;
820
826
get_date_from_daynr((long) daynr, <ime->year, <ime->month,
826
832
period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
827
833
sign * (long) interval.day);
828
834
/* Daynumber from year 0 to 9999-12-31 */
829
if ((ulong) period > MAX_DAY_NUMBER)
835
if (period > MAX_DAY_NUMBER)
830
836
goto invalid_date;
831
837
get_date_from_daynr((long) period,<ime->year,<ime->month,<ime->day);
833
839
case INTERVAL_YEAR:
834
840
ltime->year+= sign * (long) interval.year;
835
if ((ulong) ltime->year >= 10000L)
841
if (ltime->year >= 10000L)
836
842
goto invalid_date;
837
843
if (ltime->month == 2 && ltime->day == 29 &&
838
844
calc_days_in_year(ltime->year) != 366)
901
calc_time_diff(MYSQL_TIME *l_time1, MYSQL_TIME *l_time2, int l_sign, longlong *seconds_out,
907
calc_time_diff(DRIZZLE_TIME *l_time1, DRIZZLE_TIME *l_time2, int l_sign, int64_t *seconds_out,
902
908
long *microseconds_out)
906
longlong microseconds;
912
int64_t microseconds;
909
We suppose that if first argument is MYSQL_TIMESTAMP_TIME
915
We suppose that if first argument is DRIZZLE_TIMESTAMP_TIME
910
916
the second argument should be TIMESTAMP_TIME also.
911
917
We should check it before calc_time_diff call.
913
if (l_time1->time_type == MYSQL_TIMESTAMP_TIME) // Time value
919
if (l_time1->time_type == DRIZZLE_TIMESTAMP_TIME) // Time value
914
920
days= (long)l_time1->day - l_sign * (long)l_time2->day;
917
923
days= calc_daynr((uint) l_time1->year,
918
924
(uint) l_time1->month,
919
925
(uint) l_time1->day);
920
if (l_time2->time_type == MYSQL_TIMESTAMP_TIME)
926
if (l_time2->time_type == DRIZZLE_TIMESTAMP_TIME)
921
927
days-= l_sign * (long)l_time2->day;
923
929
days-= l_sign*calc_daynr((uint) l_time2->year,
925
931
(uint) l_time2->day);
928
microseconds= ((longlong)days*LL(86400) +
929
(longlong)(l_time1->hour*3600L +
934
microseconds= ((int64_t)days*86400L +
935
(int64_t)(l_time1->hour*3600L +
930
936
l_time1->minute*60L +
931
937
l_time1->second) -
932
l_sign*(longlong)(l_time2->hour*3600L +
938
l_sign*(int64_t)(l_time2->hour*3600L +
933
939
l_time2->minute*60L +
934
l_time2->second)) * LL(1000000) +
935
(longlong)l_time1->second_part -
936
l_sign*(longlong)l_time2->second_part;
940
l_time2->second)) * 1000000L +
941
(int64_t)l_time1->second_part -
942
l_sign*(int64_t)l_time2->second_part;
939
945
if (microseconds < 0)
969
my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b)
975
my_time_compare(DRIZZLE_TIME *a, DRIZZLE_TIME *b)
971
my_ulonglong a_t= TIME_to_ulonglong_datetime(a);
972
my_ulonglong b_t= TIME_to_ulonglong_datetime(b);
977
uint64_t a_t= TIME_to_uint64_t_datetime(a);
978
uint64_t b_t= TIME_to_uint64_t_datetime(b);