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-2009 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 <drizzled/server_includes.h>
20
#include <drizzled/drizzled_error_messages.h>
23
/* Some functions to calculate dates */
28
Name description of interval names used in statements.
30
'interval_type_to_name' is ordered and sorted on interval size and
32
Order of elements in 'interval_type_to_name' should correspond to
33
the order of elements in 'interval_type' enum
35
See also interval_type, interval_names
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")}
61
/* Calc weekday from daynr */
62
/* Returns 0 for monday, 1 for tuesday .... */
24
#include "drizzled/error.h"
25
#include "drizzled/util/test.h"
26
#include "drizzled/tztime.h"
27
#include "drizzled/session.h"
28
#include "drizzled/time_functions.h"
33
/* Some functions to calculate dates */
64
36
int calc_weekday(long daynr,bool sunday_first_day_of_week)
66
38
return ((int) ((daynr + 5L + (sunday_first_day_of_week ? 1L : 0L)) % 7));
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
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.
81
If set Week is in range 1-53.
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
87
WEEK_FIRST_WEEKDAY (2) If not set Weeks are numbered according
89
If set The week that contains the first
90
'first-day-of-week' is week 1.
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
98
uint calc_week(DRIZZLE_TIME *l_time, uint week_behaviour, uint *year)
42
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);
45
uint32_t daynr= calc_daynr(l_time->year,l_time->month,l_time->day);
46
uint32_t first_daynr= calc_daynr(l_time->year,1,1);
103
47
bool monday_first= test(week_behaviour & WEEK_MONDAY_FIRST);
104
48
bool week_year= test(week_behaviour & WEEK_YEAR);
105
49
bool first_weekday= test(week_behaviour & WEEK_FIRST_WEEKDAY);
107
uint weekday=calc_weekday(first_daynr, !monday_first);
51
uint32_t weekday= calc_weekday(first_daynr, !monday_first);
108
52
*year=l_time->year;
110
54
if (l_time->month == 1 && l_time->day <= 7-weekday)
182
/* Functions to handle periods */
184
ulong convert_period_to_month(ulong period)
189
if ((a=period/100) < YY_PART_YEAR)
198
ulong convert_month_to_period(ulong month)
203
if ((year=month/12) < 100)
205
year+=(year < YY_PART_YEAR) ? 2000 : 1900;
207
return year*100+month%12+1;
212
Convert a timestamp string to a DRIZZLE_TIME value and produce a warning
213
if string was truncated during conversion.
216
See description of str_to_datetime() for more information.
220
str_to_datetime_with_warn(const char *str, uint length, DRIZZLE_TIME *l_time,
127
enum enum_drizzle_timestamp_type
128
str_to_datetime_with_warn(const char *str,
130
DRIZZLE_TIME *l_time,
224
THD *thd= current_thd;
225
timestamp_type ts_type;
134
Session *session= current_session;
135
enum enum_drizzle_timestamp_type ts_type;
227
137
ts_type= str_to_datetime(str, length, l_time,
228
(flags | (thd->variables.sql_mode &
138
(flags | (session->variables.sql_mode &
229
139
(MODE_INVALID_DATES |
230
140
MODE_NO_ZERO_DATE))),
232
142
if (was_cut || ts_type <= DRIZZLE_TIMESTAMP_ERROR)
233
make_truncated_value_warning(current_thd, DRIZZLE_ERROR::WARN_LEVEL_WARN,
234
str, length, ts_type, NullS);
143
make_truncated_value_warning(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
144
str, length, ts_type, NULL);
240
Convert a datetime from broken-down DRIZZLE_TIME representation to corresponding
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.
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.
256
my_time_t TIME_to_timestamp(THD *thd, const DRIZZLE_TIME *t, bool *in_dst_time_gap)
261
thd->time_zone_used= 1;
263
timestamp= thd->variables.time_zone->TIME_to_gmt_sec(t, in_dst_time_gap);
269
/* If we are here we have range error. */
275
Convert a time string to a DRIZZLE_TIME struct and produce a warning
276
if string was cut during conversion.
279
See str_to_time() for more info.
282
str_to_time_with_warn(const char *str, uint length, DRIZZLE_TIME *l_time)
150
str_to_time_with_warn(const char *str, uint32_t length, DRIZZLE_TIME *l_time)
285
153
bool ret_val= str_to_time(str, length, l_time, &warning);
286
154
if (ret_val || warning)
287
make_truncated_value_warning(current_thd, DRIZZLE_ERROR::WARN_LEVEL_WARN,
288
str, length, DRIZZLE_TIMESTAMP_TIME, NullS);
155
make_truncated_value_warning(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
156
str, length, DRIZZLE_TIMESTAMP_TIME, NULL);
294
Convert a system time structure to TIME
297
161
void localtime_to_TIME(DRIZZLE_TIME *to, struct tm *from)
306
170
to->second= (int) from->tm_sec;
309
void calc_time_from_sec(DRIZZLE_TIME *to, long seconds, long microseconds)
312
// to->neg is not cleared, it may already be set to a useful value
313
to->time_type= DRIZZLE_TIMESTAMP_TIME;
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;
326
Parse a format string specification
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
336
Fills in date_time_format->positions for all date time parts.
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
343
If positions[0]= 5, it means that year will be the forth element to
344
read from the parsed date string.
351
bool parse_date_time_format(timestamp_type format_type,
352
const char *format, uint format_length,
353
DATE_TIME_FORMAT *date_time_format)
355
uint offset= 0, separators= 0;
356
const char *ptr= format, *format_str;
357
const char *end= ptr+format_length;
358
uchar *dt_pos= date_time_format->positions;
359
/* need_p is set if we are using AM/PM format */
360
bool need_p= 0, allow_separator= 0;
361
ulong part_map= 0, separator_map= 0;
362
const char *parts[16];
364
date_time_format->time_separator= 0;
365
date_time_format->flag= 0; // For future
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)
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;
374
for (; ptr != end; ptr++)
376
if (*ptr == '%' && ptr+1 != end)
395
need_p= 1; // Need AM/PM
410
if (dt_pos[5] != offset-1 || ptr[-2] != '.')
411
return 1; // Wrong usage of %f
414
if (offset == 0) // Can't be first
419
return 1; // Unknown controll char
421
if (dt_pos[position] != 255) // Don't allow same tag twice
423
parts[position]= ptr-1;
426
If switching from time to date, ensure that all time parts
429
if (part_map && position <= 2 && !(part_map & (1 | 2 | 4)))
431
part_map|= (ulong) 1 << position;
432
dt_pos[position]= offset++;
438
Don't allow any characters in format as this could easily confuse
441
if (!allow_separator)
442
return 1; // No separator here
443
allow_separator= 0; // Don't allow two separators
445
/* Store in separator_map which parts are punct characters */
446
if (my_ispunct(&my_charset_utf8_general_ci, *ptr))
447
separator_map|= (ulong) 1 << (offset-1);
448
else if (!my_isspace(&my_charset_utf8_general_ci, *ptr))
453
/* If no %f, specify it after seconds. Move %p up, if necessary */
454
if ((part_map & 32) && !(part_map & 64))
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
463
Check that we have not used a non legal format specifier and that all
464
format specifiers have been used
466
The last test is to ensure that %p is used if and only if
469
if ((format_type == DRIZZLE_TIMESTAMP_DATETIME &&
470
!test_all_bits(part_map, (1 | 2 | 4 | 8 | 16 | 32))) ||
471
(format_type == DRIZZLE_TIMESTAMP_DATE && part_map != (1 | 2 | 4)) ||
472
(format_type == DRIZZLE_TIMESTAMP_TIME &&
473
!test_all_bits(part_map, 8 | 16 | 32)) ||
474
!allow_separator || // %option should be last
475
(need_p && dt_pos[6] +1 != dt_pos[7]) ||
476
(need_p ^ (dt_pos[7] != 255)))
479
if (dt_pos[6] != 255) // If fractional seconds
481
/* remove fractional seconds from later tests */
482
uint pos= dt_pos[6] -1;
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));
488
separators--; // There is always a separator
489
need_p= 1; // force use of separators
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
497
if (dt_pos[7] != 255)
499
if (need_p && parts[7] != parts[6]+2)
503
Calculate if %p is in first or last part of the datetime field
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
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));
514
switch (format_type) {
515
case DRIZZLE_TIMESTAMP_DATE:
516
format_str= known_date_time_formats[INTERNAL_FORMAT].date_format;
518
case DRIZZLE_TIMESTAMP_TIME:
520
format_str=known_date_time_formats[INTERNAL_FORMAT].time_format;
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
526
if (format_length == 6 && !need_p &&
527
!my_strnncoll(&my_charset_bin,
528
(const uchar *) format, 6,
529
(const uchar *) format_str, 6))
531
if (separator_map == (1 | 2))
533
if (format_type == DRIZZLE_TIMESTAMP_TIME)
535
if (*(format+2) != *(format+5))
537
/* Store the character used for time formats */
538
date_time_format->time_separator= *(format+2);
543
case DRIZZLE_TIMESTAMP_DATETIME:
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
549
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,
554
(separators == 5 && separator_map == (1 | 2 | 8 | 16)))
566
Create a DATE_TIME_FORMAT object from a format string specification
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
575
The returned object should be freed with my_free()
583
*date_time_format_make(timestamp_type format_type,
584
const char *format_str, uint format_length)
586
DATE_TIME_FORMAT tmp;
588
if (format_length && format_length < 255 &&
589
!parse_date_time_format(format_type, format_str,
590
format_length, &tmp))
592
tmp.format.str= (char*) format_str;
593
tmp.format.length= format_length;
594
return date_time_format_copy((THD *)0, &tmp);
601
Create a copy of a DATE_TIME_FORMAT object
604
date_and_time_format_copy()
605
thd Set if variable should be allocated in thread mem
606
format format to copy
609
The returned object should be freed with my_free()
616
DATE_TIME_FORMAT *date_time_format_copy(THD *thd, DATE_TIME_FORMAT *format)
618
DATE_TIME_FORMAT *new_format;
619
ulong length= sizeof(*format) + format->format.length + 1;
622
new_format= (DATE_TIME_FORMAT *) thd->alloc(length);
624
new_format= (DATE_TIME_FORMAT *) my_malloc(length, MYF(MY_WME));
627
/* Put format string after current pos */
628
new_format->format.str= (char*) (new_format+1);
629
memcpy(new_format->positions, format->positions,
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 */
633
memcpy(new_format->format.str, format->format.str,
634
format->format.length);
635
new_format->format.str[format->format.length]= 0;
636
new_format->format.length= format->format.length;
642
KNOWN_DATE_TIME_FORMAT known_date_time_formats[6]=
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" },
654
Return format string according format name.
655
If name is unknown, result is NULL
658
const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
662
case DRIZZLE_TIMESTAMP_DATE:
663
return format->date_format;
664
case DRIZZLE_TIMESTAMP_DATETIME:
665
return format->datetime_format;
666
case DRIZZLE_TIMESTAMP_TIME:
667
return format->time_format;
669
assert(0); // Impossible
674
/****************************************************************************
675
Functions to create default time/date/datetime strings
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
682
This functions don't check that given DRIZZLE_TIME structure members are
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
687
****************************************************************************/
689
void make_time(const DATE_TIME_FORMAT *format __attribute__((unused)),
690
const DRIZZLE_TIME *l_time, String *str)
692
uint length= (uint) my_time_to_str(l_time, (char*) str->ptr());
694
str->set_charset(&my_charset_bin);
698
void make_date(const DATE_TIME_FORMAT *format __attribute__((unused)),
699
const DRIZZLE_TIME *l_time, String *str)
701
uint length= (uint) my_date_to_str(l_time, (char*) str->ptr());
703
str->set_charset(&my_charset_bin);
707
void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)),
708
const DRIZZLE_TIME *l_time, String *str)
710
uint length= (uint) my_datetime_to_str(l_time, (char*) str->ptr());
712
str->set_charset(&my_charset_bin);
716
void make_truncated_value_warning(THD *thd, DRIZZLE_ERROR::enum_warning_level level,
173
void make_date(const DRIZZLE_TIME *l_time, String *str)
175
str->alloc(MAX_DATE_STRING_REP_LENGTH);
176
uint32_t length= (uint32_t) my_date_to_str(l_time, str->c_ptr());
178
str->set_charset(&my_charset_bin);
182
void make_datetime(const DRIZZLE_TIME *l_time, String *str)
184
str->alloc(MAX_DATE_STRING_REP_LENGTH);
185
uint32_t length= (uint32_t) my_datetime_to_str(l_time, str->c_ptr());
187
str->set_charset(&my_charset_bin);
191
void make_truncated_value_warning(Session *session,
192
DRIZZLE_ERROR::enum_warning_level level,
717
193
const char *str_val,
718
uint str_length, timestamp_type time_type,
195
enum enum_drizzle_timestamp_type time_type,
719
196
const char *field_name)
721
198
char warn_buff[DRIZZLE_ERRMSG_SIZE];
753
230
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
754
231
ER(ER_WRONG_VALUE), type_str, str.c_ptr());
756
push_warning(thd, level,
233
push_warning(session, level,
757
234
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(DRIZZLE_TIME *ltime, interval_type int_type, INTERVAL interval)
769
sign= (interval.neg ? -1 : 1);
772
case INTERVAL_SECOND:
773
case INTERVAL_SECOND_MICROSECOND:
774
case INTERVAL_MICROSECOND:
775
case INTERVAL_MINUTE:
777
case INTERVAL_MINUTE_MICROSECOND:
778
case INTERVAL_MINUTE_SECOND:
779
case INTERVAL_HOUR_MICROSECOND:
780
case INTERVAL_HOUR_SECOND:
781
case INTERVAL_HOUR_MINUTE:
782
case INTERVAL_DAY_MICROSECOND:
783
case INTERVAL_DAY_SECOND:
784
case INTERVAL_DAY_MINUTE:
785
case INTERVAL_DAY_HOUR:
787
int64_t sec, days, daynr, microseconds, extra_sec;
788
ltime->time_type= DRIZZLE_TIMESTAMP_DATETIME; // Return full date
789
microseconds= ltime->second_part + sign*interval.second_part;
790
extra_sec= microseconds/1000000L;
791
microseconds= microseconds%1000000L;
793
sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
795
sign* (int64_t) (interval.day*3600*24L +
796
interval.hour*3600LL+interval.minute*60LL+
797
interval.second))+ extra_sec;
798
if (microseconds < 0)
800
microseconds+= 1000000LL;
803
days= sec/(3600*24LL);
804
sec-= days*3600*24LL;
810
ltime->second_part= (uint) microseconds;
811
ltime->second= (uint) (sec % 60);
812
ltime->minute= (uint) (sec/60 % 60);
813
ltime->hour= (uint) (sec/3600);
814
daynr= calc_daynr(ltime->year,ltime->month,1) + days;
815
/* Day number from year 0 to 9999-12-31 */
816
if ((uint64_t) daynr > MAX_DAY_NUMBER)
818
get_date_from_daynr((long) daynr, <ime->year, <ime->month,
824
period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
825
sign * (long) interval.day);
826
/* Daynumber from year 0 to 9999-12-31 */
827
if ((ulong) period > MAX_DAY_NUMBER)
829
get_date_from_daynr((long) period,<ime->year,<ime->month,<ime->day);
832
ltime->year+= sign * (long) interval.year;
833
if ((ulong) ltime->year >= 10000L)
835
if (ltime->month == 2 && ltime->day == 29 &&
836
calc_days_in_year(ltime->year) != 366)
837
ltime->day=28; // Was leap-year
839
case INTERVAL_YEAR_MONTH:
840
case INTERVAL_QUARTER:
842
period= (ltime->year*12 + sign * (long) interval.year*12 +
843
ltime->month-1 + sign * (long) interval.month);
844
if ((ulong) period >= 120000L)
846
ltime->year= (uint) (period / 12);
847
ltime->month= (uint) (period % 12L)+1;
848
/* Adjust day if the new month doesn't have enough days */
849
if (ltime->day > days_in_month[ltime->month-1])
851
ltime->day = days_in_month[ltime->month-1];
852
if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
853
ltime->day++; // Leap-year
863
push_warning_printf(current_thd, DRIZZLE_ERROR::WARN_LEVEL_WARN,
864
ER_DATETIME_FUNCTION_OVERFLOW,
865
ER(ER_DATETIME_FUNCTION_OVERFLOW),
873
Calculate difference between two datetime values as seconds + microseconds.
877
l_time1 - TIME/DATE/DATETIME value
878
l_time2 - TIME/DATE/DATETIME value
879
l_sign - 1 absolute values are substracted,
880
-1 absolute values are added.
881
seconds_out - Out parameter where difference between
882
l_time1 and l_time2 in seconds is stored.
883
microseconds_out- Out parameter where microsecond part of difference
884
between l_time1 and l_time2 is stored.
887
This function calculates difference between l_time1 and l_time2 absolute
888
values. So one should set l_sign and correct result if he want to take
889
signs into account (i.e. for DRIZZLE_TIME values).
892
Returns sign of difference.
893
1 means negative result
894
0 means positive result
899
239
calc_time_diff(DRIZZLE_TIME *l_time1, DRIZZLE_TIME *l_time2, int l_sign, int64_t *seconds_out,