1 /* Copyright (c) 2006, 2010, Oracle and/or its affiliates.
2    Copyright (c) 2011, 2020, MariaDB
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; version 2 of the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1335  USA */
16 
17 #ifndef SQL_TIME_INCLUDED
18 #define SQL_TIME_INCLUDED
19 
20 #include "my_time.h"
21 #include "mysql_time.h"                         /* timestamp_type */
22 #include "sql_error.h"                          /* Sql_condition */
23 #include "structs.h"                            /* INTERVAL */
24 
25 typedef enum enum_mysql_timestamp_type timestamp_type;
26 typedef struct st_date_time_format DATE_TIME_FORMAT;
27 typedef struct st_known_date_time_format KNOWN_DATE_TIME_FORMAT;
28 
29 /* Flags for calc_week() function.  */
30 #define WEEK_MONDAY_FIRST    1
31 #define WEEK_YEAR            2
32 #define WEEK_FIRST_WEEKDAY   4
33 
34 ulong convert_period_to_month(ulong period);
35 ulong convert_month_to_period(ulong month);
36 void set_current_date(THD *thd, MYSQL_TIME *to);
37 bool time_to_datetime(MYSQL_TIME *ltime);
38 void time_to_daytime_interval(MYSQL_TIME *l_time);
39 bool get_date_from_daynr(long daynr,uint *year, uint *month, uint *day);
40 my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code);
41 bool str_to_datetime_with_warn(CHARSET_INFO *cs, const char *str, size_t length, MYSQL_TIME *l_time,
42                                ulonglong flags);
43 bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime,
44                                   ulonglong fuzzydate,
45                                   const TABLE_SHARE *s, const char *name);
46 bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime,
47                                    ulonglong fuzzydate,
48                                    const TABLE_SHARE *s, const char *name);
49 bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime,
50                                ulonglong fuzzydate,
51                                const TABLE_SHARE *s, const char *name);
52 
53 bool time_to_datetime(THD *thd, const MYSQL_TIME *tm, MYSQL_TIME *dt);
54 bool time_to_datetime_with_warn(THD *thd,
55                                 const MYSQL_TIME *tm, MYSQL_TIME *dt,
56                                 ulonglong fuzzydate);
57 /*
58   Simply truncate the YYYY-MM-DD part to 0000-00-00
59   and change time_type to MYSQL_TIMESTAMP_TIME
60 */
datetime_to_time(MYSQL_TIME * ltime)61 inline void datetime_to_time(MYSQL_TIME *ltime)
62 {
63   DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE ||
64               ltime->time_type == MYSQL_TIMESTAMP_DATETIME);
65   DBUG_ASSERT(ltime->neg == 0);
66   ltime->year= ltime->month= ltime->day= 0;
67   ltime->time_type= MYSQL_TIMESTAMP_TIME;
68 }
69 
70 
71 /**
72   Convert DATE/DATETIME to TIME(dec)
73   using CURRENT_DATE in a non-old mode,
74   or using simple truncation in old mode (OLD_MODE_ZERO_DATE_TIME_CAST).
75 
76   @param      thd - the thread to get the variables.old_behaviour value from
77   @param      dt  - the DATE of DATETIME value to convert
78   @param[out] tm  - store result here
79   @param      dec - the desired scale. The fractional part of the result
80                     is checked according to this parameter before returning
81                     the conversion result. "dec" is important in the corner
82                     cases near the max/min limits.
83                     If the result is '838:59:59.999999' and the desired scale
84                     is less than 6, an error is returned.
85                     Note, dec is not important in the
86                     OLD_MODE_ZERO_DATE_TIME_CAST old mode.
87 
88   - in case of OLD_MODE_ZERO_DATE_TIME_CAST
89     the TIME part is simply truncated and "false" is returned.
90   - otherwise, the result is calculated effectively similar to:
91     TIMEDIFF(dt, CAST(CURRENT_DATE AS DATETIME))
92     If the difference fits into the supported TIME range, "false" is returned,
93     otherwise a warning is issued and "true" is returned.
94 
95   @return false - on success
96   @return true  - on error
97 */
98 bool datetime_to_time_with_warn(THD *, const MYSQL_TIME *dt,
99                                 MYSQL_TIME *tm, uint dec);
100 
101 
datetime_to_date(MYSQL_TIME * ltime)102 inline void datetime_to_date(MYSQL_TIME *ltime)
103 {
104   DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE ||
105               ltime->time_type == MYSQL_TIMESTAMP_DATETIME);
106   DBUG_ASSERT(ltime->neg == 0);
107   ltime->second_part= ltime->hour= ltime->minute= ltime->second= 0;
108   ltime->time_type= MYSQL_TIMESTAMP_DATE;
109 }
date_to_datetime(MYSQL_TIME * ltime)110 inline void date_to_datetime(MYSQL_TIME *ltime)
111 {
112   DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE ||
113               ltime->time_type == MYSQL_TIMESTAMP_DATETIME);
114   DBUG_ASSERT(ltime->neg == 0);
115   ltime->time_type= MYSQL_TIMESTAMP_DATETIME;
116 }
117 void make_truncated_value_warning(THD *thd,
118                                   Sql_condition::enum_warning_level level,
119                                   const ErrConv *str_val,
120                                   timestamp_type time_type,
121                                   const char *db_name, const char *table_name,
122                                   const char *field_name);
123 
make_truncated_value_warning(THD * thd,Sql_condition::enum_warning_level level,const char * str_val,size_t str_length,timestamp_type time_type,const char * db_name,const char * table_name,const char * field_name)124 static inline void make_truncated_value_warning(THD *thd,
125                 Sql_condition::enum_warning_level level, const char *str_val,
126 		size_t str_length, timestamp_type time_type,
127                 const char *db_name, const char *table_name,
128                 const char *field_name)
129 {
130   const ErrConvString str(str_val, str_length, &my_charset_bin);
131   make_truncated_value_warning(thd, level, &str, time_type, db_name, table_name,
132                                field_name);
133 }
134 
135 extern DATE_TIME_FORMAT *date_time_format_make(timestamp_type format_type,
136 					       const char *format_str,
137 					       uint format_length);
138 extern DATE_TIME_FORMAT *date_time_format_copy(THD *thd,
139 					       DATE_TIME_FORMAT *format);
140 const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
141 				     timestamp_type type);
142 bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec);
143 
144 /* MYSQL_TIME operations */
145 bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
146                        const INTERVAL &interval);
147 bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
148                     int l_sign, longlong *seconds_out, long *microseconds_out);
149 int append_interval(String *str, interval_type int_type,
150                     const INTERVAL &interval);
151 /**
152   Calculate time difference between two MYSQL_TIME values and
153   store the result as an out MYSQL_TIME value in MYSQL_TIMESTAMP_TIME format.
154 
155   The result can be outside of the supported TIME range.
156   For example, calc_time_diff('2002-01-01 00:00:00', '2001-01-01 00:00:00')
157   returns '8760:00:00'. So the caller might want to do check_time_range() or
158   adjust_time_range_with_warn() on the result of a calc_time_diff() call.
159 
160   @param l_time1       - the minuend (TIME/DATE/DATETIME value)
161   @param l_time2       - the subtrahend TIME/DATE/DATETIME value
162   @param l_sign        -  +1 if absolute values are to be subtracted,
163                           or -1 if absolute values are to be added.
164   @param[out] l_time3  - the result
165   @param fuzzydate     - flags
166 
167   @return true         - if TIME_NO_ZERO_DATE was passed in flags and
168                          the result appeared to be '00:00:00.000000'.
169                          This is important when calc_time_diff() is called
170                          when calculating DATE_ADD(TIMEDIFF(...),...)
171   @return false        - otherwise
172 */
173 bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2,
174                     int lsign, MYSQL_TIME *l_time3, ulonglong fuzzydate);
175 int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b);
176 void localtime_to_TIME(MYSQL_TIME *to, struct tm *from);
177 
178 void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds);
179 uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year);
180 
181 int calc_weekday(long daynr,bool sunday_first_day_of_week);
182 bool parse_date_time_format(timestamp_type format_type,
183                             const char *format, uint format_length,
184                             DATE_TIME_FORMAT *date_time_format);
185 /* Character set-aware version of str_to_time() */
186 bool str_to_time(CHARSET_INFO *cs, const char *str,size_t length,
187                  MYSQL_TIME *l_time, ulonglong fuzzydate,
188                  MYSQL_TIME_STATUS *status);
189 /* Character set-aware version of str_to_datetime() */
190 bool str_to_datetime(CHARSET_INFO *cs,
191                      const char *str, size_t length,
192                      MYSQL_TIME *l_time, ulonglong flags,
193                      MYSQL_TIME_STATUS *status);
194 
195 /* convenience wrapper */
parse_date_time_format(timestamp_type format_type,DATE_TIME_FORMAT * date_time_format)196 inline bool parse_date_time_format(timestamp_type format_type,
197                                    DATE_TIME_FORMAT *date_time_format)
198 {
199   return parse_date_time_format(format_type,
200                                 date_time_format->format.str,
201                                 (uint) date_time_format->format.length,
202                                 date_time_format);
203 }
204 
205 
206 extern DATE_TIME_FORMAT global_date_format;
207 extern DATE_TIME_FORMAT global_datetime_format;
208 extern DATE_TIME_FORMAT global_time_format;
209 extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[];
210 extern LEX_CSTRING interval_type_to_name[];
211 
212 static inline bool
non_zero_hhmmssuu(const MYSQL_TIME * ltime)213 non_zero_hhmmssuu(const MYSQL_TIME *ltime)
214 {
215   return ltime->hour || ltime->minute || ltime->second || ltime->second_part;
216 }
217 static inline bool
non_zero_YYMMDD(const MYSQL_TIME * ltime)218 non_zero_YYMMDD(const MYSQL_TIME *ltime)
219 {
220   return ltime->year || ltime->month || ltime->day;
221 }
222 static inline bool
non_zero_date(const MYSQL_TIME * ltime)223 non_zero_date(const MYSQL_TIME *ltime)
224 {
225   return non_zero_YYMMDD(ltime) ||
226          (ltime->time_type == MYSQL_TIMESTAMP_DATETIME &&
227           non_zero_hhmmssuu(ltime));
228 }
229 static inline bool
check_date(const MYSQL_TIME * ltime,ulonglong flags,int * was_cut)230 check_date(const MYSQL_TIME *ltime, ulonglong flags, int *was_cut)
231 {
232  return check_date(ltime, non_zero_date(ltime), flags, was_cut);
233 }
234 bool check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date,
235                           timestamp_type ts_type);
236 bool make_date_with_warn(MYSQL_TIME *ltime,
237                          ulonglong fuzzy_date, timestamp_type ts_type);
238 bool adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec);
239 
240 longlong pack_time(const MYSQL_TIME *my_time);
241 void unpack_time(longlong packed, MYSQL_TIME *my_time,
242                  enum_mysql_timestamp_type ts_type);
243 
244 #endif /* SQL_TIME_INCLUDED */
245