| 1 | /* Copyright (c) 2000, 2010, Oracle and/or its affiliates. | 
| 2 |    Copyright (c) 2009, 2013 Monty Program Ab. | 
| 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-1301  USA */ | 
| 16 |  | 
| 17 |  | 
| 18 | /* Functions to handle date and time */ | 
| 19 |  | 
| 20 | #include "mariadb.h" | 
| 21 | #include "sql_priv.h" | 
| 22 | #include "sql_time.h" | 
| 23 | #include "tztime.h"                      // struct Time_zone | 
| 24 | #include "sql_class.h"                   // THD | 
| 25 | #include <m_ctype.h> | 
| 26 |  | 
| 27 |  | 
| 28 | #define MAX_DAY_NUMBER 3652424L | 
| 29 |  | 
| 30 | 	/* Some functions to calculate dates */ | 
| 31 |  | 
| 32 | /* | 
| 33 |   Name description of interval names used in statements. | 
| 34 |  | 
| 35 |   'interval_type_to_name' is ordered and sorted on interval size and | 
| 36 |   interval complexity. | 
| 37 |   Order of elements in 'interval_type_to_name' should correspond to  | 
| 38 |   the order of elements in 'interval_type' enum | 
| 39 |    | 
| 40 |   See also interval_type, interval_names, append_interval | 
| 41 | */ | 
| 42 |  | 
| 43 | LEX_CSTRING interval_type_to_name[INTERVAL_LAST] = { | 
| 44 |   { STRING_WITH_LEN("YEAR" )}, | 
| 45 |   { STRING_WITH_LEN("QUARTER" )}, | 
| 46 |   { STRING_WITH_LEN("MONTH" )}, | 
| 47 |   { STRING_WITH_LEN("WEEK" )}, | 
| 48 |   { STRING_WITH_LEN("DAY" )}, | 
| 49 |   { STRING_WITH_LEN("HOUR" )}, | 
| 50 |   { STRING_WITH_LEN("MINUTE" )}, | 
| 51 |   { STRING_WITH_LEN("SECOND" )}, | 
| 52 |   { STRING_WITH_LEN("MICROSECOND" )}, | 
| 53 |   { STRING_WITH_LEN("YEAR_MONTH" )}, | 
| 54 |   { STRING_WITH_LEN("DAY_HOUR" )}, | 
| 55 |   { STRING_WITH_LEN("DAY_MINUTE" )}, | 
| 56 |   { STRING_WITH_LEN("DAY_SECOND" )}, | 
| 57 |   { STRING_WITH_LEN("HOUR_MINUTE" )}, | 
| 58 |   { STRING_WITH_LEN("HOUR_SECOND" )}, | 
| 59 |   { STRING_WITH_LEN("MINUTE_SECOND" )}, | 
| 60 |   { STRING_WITH_LEN("DAY_MICROSECOND" )}, | 
| 61 |   { STRING_WITH_LEN("HOUR_MICROSECOND" )}, | 
| 62 |   { STRING_WITH_LEN("MINUTE_MICROSECOND" )}, | 
| 63 |   { STRING_WITH_LEN("SECOND_MICROSECOND" )} | 
| 64 | }; | 
| 65 |  | 
| 66 | int append_interval(String *str, interval_type int_type, const INTERVAL &interval) | 
| 67 | { | 
| 68 |   char buf[64]; | 
| 69 |   size_t len; | 
| 70 |   switch (int_type) { | 
| 71 |   case INTERVAL_YEAR: | 
| 72 |     len= my_snprintf(buf,sizeof(buf),"%u" , interval.year); | 
| 73 |     break; | 
| 74 |   case INTERVAL_QUARTER: | 
| 75 |   case INTERVAL_MONTH: | 
| 76 |     len= my_snprintf(buf,sizeof(buf),"%u" , interval.month); | 
| 77 |     int_type=INTERVAL_MONTH; | 
| 78 |     break; | 
| 79 |   case INTERVAL_WEEK: | 
| 80 |   case INTERVAL_DAY: | 
| 81 |     len= my_snprintf(buf,sizeof(buf),"%u" , interval.day); | 
| 82 |     int_type=INTERVAL_DAY; | 
| 83 |     break; | 
| 84 |   case INTERVAL_HOUR: | 
| 85 |     len= my_snprintf(buf,sizeof(buf),"%u" , interval.hour); | 
| 86 |     break; | 
| 87 |   case INTERVAL_MINUTE: | 
| 88 |     len= my_snprintf(buf,sizeof(buf),"%u" , interval.minute); | 
| 89 |     break; | 
| 90 |   case INTERVAL_SECOND: | 
| 91 |     len= my_snprintf(buf,sizeof(buf),"%u" , interval.second); | 
| 92 |     break; | 
| 93 |   case INTERVAL_MICROSECOND: | 
| 94 |     len= my_snprintf(buf,sizeof(buf),"%u" , interval.second_part); | 
| 95 |     break; | 
| 96 |   case INTERVAL_YEAR_MONTH: | 
| 97 |     len= my_snprintf(buf,sizeof(buf),"%u-%02u" , interval.day, interval.month); | 
| 98 |     break; | 
| 99 |   case INTERVAL_DAY_HOUR: | 
| 100 |     len= my_snprintf(buf,sizeof(buf),"%u %u" , interval.day, interval.hour); | 
| 101 |     break; | 
| 102 |   case INTERVAL_DAY_MINUTE: | 
| 103 |     len= my_snprintf(buf,sizeof(buf),"%u %u:%02u" , interval.day, interval.hour, interval.minute); | 
| 104 |     break; | 
| 105 |   case INTERVAL_DAY_SECOND: | 
| 106 |     len= my_snprintf(buf,sizeof(buf),"%u %u:%02u:%02u" , interval.day, interval.hour, interval.minute, interval.second); | 
| 107 |     break; | 
| 108 |   case INTERVAL_HOUR_MINUTE: | 
| 109 |     len= my_snprintf(buf,sizeof(buf),"%u:%02u" , interval.hour, interval.minute); | 
| 110 |     break; | 
| 111 |   case INTERVAL_HOUR_SECOND: | 
| 112 |     len= my_snprintf(buf,sizeof(buf),"%u:%02u:%02u" , interval.hour, interval.minute, interval.second); | 
| 113 |     break; | 
| 114 |   case INTERVAL_MINUTE_SECOND: | 
| 115 |     len= my_snprintf(buf,sizeof(buf),"%u:%02u" , interval.minute, interval.second); | 
| 116 |     break; | 
| 117 |   case INTERVAL_DAY_MICROSECOND: | 
| 118 |     len= my_snprintf(buf,sizeof(buf),"%u %u:%02u:%02u.%06u" , interval.day, interval.hour, interval.minute, interval.second, interval.second_part); | 
| 119 |     break; | 
| 120 |   case INTERVAL_HOUR_MICROSECOND: | 
| 121 |     len= my_snprintf(buf,sizeof(buf),"%u:%02u:%02u.%06u" , interval.hour, interval.minute, interval.second, interval.second_part); | 
| 122 |     break; | 
| 123 |   case INTERVAL_MINUTE_MICROSECOND: | 
| 124 |     len= my_snprintf(buf,sizeof(buf),"%u:%02u.%06u" , interval.minute, interval.second, interval.second_part); | 
| 125 |     break; | 
| 126 |   case INTERVAL_SECOND_MICROSECOND: | 
| 127 |     len= my_snprintf(buf,sizeof(buf),"%u.%06u" , interval.second, interval.second_part); | 
| 128 |     break; | 
| 129 |   default: | 
| 130 |     DBUG_ASSERT(0); | 
| 131 |     len= 0; | 
| 132 |   } | 
| 133 |   return str->append(buf, len) || str->append(' ') || | 
| 134 |          str->append(interval_type_to_name + int_type); | 
| 135 | } | 
| 136 |  | 
| 137 |  | 
| 138 | /* | 
| 139 |   Calc weekday from daynr | 
| 140 |   Returns 0 for monday, 1 for tuesday ... | 
| 141 | */ | 
| 142 |  | 
| 143 | int calc_weekday(long daynr,bool sunday_first_day_of_week) | 
| 144 | { | 
| 145 |   DBUG_ENTER("calc_weekday" ); | 
| 146 |   DBUG_RETURN ((int) ((daynr + 5L + (sunday_first_day_of_week ? 1L : 0L)) % 7)); | 
| 147 | } | 
| 148 |  | 
| 149 | /* | 
| 150 |   The bits in week_format has the following meaning: | 
| 151 |    WEEK_MONDAY_FIRST (0)  If not set	Sunday is first day of week | 
| 152 |       		   	  If set	Monday is first day of week | 
| 153 |    WEEK_YEAR (1)	  If not set	Week is in range 0-53 | 
| 154 |  | 
| 155 |    	Week 0 is returned for the the last week of the previous year (for | 
| 156 | 	a date at start of january) In this case one can get 53 for the | 
| 157 | 	first week of next year.  This flag ensures that the week is | 
| 158 | 	relevant for the given year. Note that this flag is only | 
| 159 | 	releveant if WEEK_JANUARY is not set. | 
| 160 |  | 
| 161 | 			  If set	 Week is in range 1-53. | 
| 162 |  | 
| 163 | 	In this case one may get week 53 for a date in January (when | 
| 164 | 	the week is that last week of previous year) and week 1 for a | 
| 165 | 	date in December. | 
| 166 |  | 
| 167 |   WEEK_FIRST_WEEKDAY (2)  If not set	Weeks are numbered according | 
| 168 | 			   		to ISO 8601:1988 | 
| 169 | 			  If set	The week that contains the first | 
| 170 | 					'first-day-of-week' is week 1. | 
| 171 | 	 | 
| 172 | 	ISO 8601:1988 means that if the week containing January 1 has | 
| 173 | 	four or more days in the new year, then it is week 1; | 
| 174 | 	Otherwise it is the last week of the previous year, and the | 
| 175 | 	next week is week 1. | 
| 176 | */ | 
| 177 |  | 
| 178 | uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year) | 
| 179 | { | 
| 180 |   uint days; | 
| 181 |   ulong daynr=calc_daynr(l_time->year,l_time->month,l_time->day); | 
| 182 |   ulong first_daynr=calc_daynr(l_time->year,1,1); | 
| 183 |   bool monday_first= MY_TEST(week_behaviour & WEEK_MONDAY_FIRST); | 
| 184 |   bool week_year= MY_TEST(week_behaviour & WEEK_YEAR); | 
| 185 |   bool first_weekday= MY_TEST(week_behaviour & WEEK_FIRST_WEEKDAY); | 
| 186 |  | 
| 187 |   uint weekday=calc_weekday(first_daynr, !monday_first); | 
| 188 |   *year=l_time->year; | 
| 189 |  | 
| 190 |   if (l_time->month == 1 && l_time->day <= 7-weekday) | 
| 191 |   { | 
| 192 |     if (!week_year &&  | 
| 193 | 	((first_weekday && weekday != 0) || | 
| 194 | 	 (!first_weekday && weekday >= 4))) | 
| 195 |       return 0; | 
| 196 |     week_year= 1; | 
| 197 |     (*year)--; | 
| 198 |     first_daynr-= (days=calc_days_in_year(*year)); | 
| 199 |     weekday= (weekday + 53*7- days) % 7; | 
| 200 |   } | 
| 201 |  | 
| 202 |   if ((first_weekday && weekday != 0) || | 
| 203 |       (!first_weekday && weekday >= 4)) | 
| 204 |     days= daynr - (first_daynr+ (7-weekday)); | 
| 205 |   else | 
| 206 |     days= daynr - (first_daynr - weekday); | 
| 207 |  | 
| 208 |   if (week_year && days >= 52*7) | 
| 209 |   { | 
| 210 |     weekday= (weekday + calc_days_in_year(*year)) % 7; | 
| 211 |     if ((!first_weekday && weekday < 4) || | 
| 212 | 	(first_weekday && weekday == 0)) | 
| 213 |     { | 
| 214 |       (*year)++; | 
| 215 |       return 1; | 
| 216 |     } | 
| 217 |   } | 
| 218 |   return days/7+1; | 
| 219 | } | 
| 220 |  | 
| 221 | 	/* Change a daynr to year, month and day */ | 
| 222 | 	/* Daynr 0 is returned as date 00.00.00 */ | 
| 223 |  | 
| 224 | bool get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month, | 
| 225 | 			 uint *ret_day) | 
| 226 | { | 
| 227 |   uint year,temp,leap_day,day_of_year,days_in_year; | 
| 228 |   uchar *month_pos; | 
| 229 |   DBUG_ENTER("get_date_from_daynr" ); | 
| 230 |  | 
| 231 |   if (daynr < 366 || daynr > MAX_DAY_NUMBER) | 
| 232 |     DBUG_RETURN(1); | 
| 233 |  | 
| 234 |   year= (uint) (daynr*100 / 36525L); | 
| 235 |   temp=(((year-1)/100+1)*3)/4; | 
| 236 |   day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp; | 
| 237 |   while (day_of_year > (days_in_year= calc_days_in_year(year))) | 
| 238 |   { | 
| 239 |     day_of_year-=days_in_year; | 
| 240 |     (year)++; | 
| 241 |   } | 
| 242 |   leap_day=0; | 
| 243 |   if (days_in_year == 366) | 
| 244 |   { | 
| 245 |     if (day_of_year > 31+28) | 
| 246 |     { | 
| 247 |       day_of_year--; | 
| 248 |       if (day_of_year == 31+28) | 
| 249 |         leap_day=1;		/* Handle leapyears leapday */ | 
| 250 |     } | 
| 251 |   } | 
| 252 |   *ret_month=1; | 
| 253 |   for (month_pos= days_in_month ; | 
| 254 |        day_of_year > (uint) *month_pos ; | 
| 255 |        day_of_year-= *(month_pos++), (*ret_month)++) | 
| 256 |     ; | 
| 257 |   *ret_year=year; | 
| 258 |   *ret_day=day_of_year+leap_day; | 
| 259 |   DBUG_RETURN(0); | 
| 260 | } | 
| 261 |  | 
| 262 | 	/* Functions to handle periods */ | 
| 263 |  | 
| 264 | ulong convert_period_to_month(ulong period) | 
| 265 | { | 
| 266 |   ulong a,b; | 
| 267 |   if (period == 0) | 
| 268 |     return 0L; | 
| 269 |   if ((a=period/100) < YY_PART_YEAR) | 
| 270 |     a+=2000; | 
| 271 |   else if (a < 100) | 
| 272 |     a+=1900; | 
| 273 |   b=period%100; | 
| 274 |   return a*12+b-1; | 
| 275 | } | 
| 276 |  | 
| 277 |  | 
| 278 | ulong convert_month_to_period(ulong month) | 
| 279 | { | 
| 280 |   ulong year; | 
| 281 |   if (month == 0L) | 
| 282 |     return 0L; | 
| 283 |   if ((year=month/12) < 100) | 
| 284 |   { | 
| 285 |     year+=(year < YY_PART_YEAR) ? 2000 : 1900; | 
| 286 |   } | 
| 287 |   return year*100+month%12+1; | 
| 288 | } | 
| 289 |  | 
| 290 |  | 
| 291 | bool | 
| 292 | check_date_with_warn(const MYSQL_TIME *ltime, ulonglong fuzzy_date, | 
| 293 |                      timestamp_type ts_type) | 
| 294 | { | 
| 295 |   int unused; | 
| 296 |   if (check_date(ltime, fuzzy_date, &unused)) | 
| 297 |   { | 
| 298 |     ErrConvTime str(ltime); | 
| 299 |     make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, | 
| 300 |                                  &str, ts_type, 0); | 
| 301 |     return true; | 
| 302 |   } | 
| 303 |   return false; | 
| 304 | } | 
| 305 |  | 
| 306 |  | 
| 307 | bool | 
| 308 | adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec) | 
| 309 | { | 
| 310 |   MYSQL_TIME copy= *ltime; | 
| 311 |   ErrConvTime str(©); | 
| 312 |   int warnings= 0; | 
| 313 |   if (check_time_range(ltime, dec, &warnings)) | 
| 314 |     return true; | 
| 315 |   if (warnings) | 
| 316 |     make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, | 
| 317 |                                  &str, MYSQL_TIMESTAMP_TIME, NullS); | 
| 318 |   return false; | 
| 319 | } | 
| 320 |  | 
| 321 | /* | 
| 322 |   Convert a string to 8-bit representation, | 
| 323 |   for use in str_to_time/str_to_date/str_to_date. | 
| 324 |    | 
| 325 |   In the future to_ascii() can be extended to convert | 
| 326 |   non-ASCII digits to ASCII digits | 
| 327 |   (for example, ARABIC-INDIC, DEVANAGARI, BENGALI, and so on) | 
| 328 |   so DATE/TIME/DATETIME values understand digits in the | 
| 329 |   respected scripts. | 
| 330 | */ | 
| 331 | static uint | 
| 332 | to_ascii(CHARSET_INFO *cs, | 
| 333 |          const char *src, size_t src_length, | 
| 334 |          char *dst, size_t dst_length) | 
| 335 |                       | 
| 336 | { | 
| 337 |   int cnvres; | 
| 338 |   my_wc_t wc; | 
| 339 |   const char *srcend= src + src_length; | 
| 340 |   char *dst0= dst, *dstend= dst + dst_length - 1; | 
| 341 |   while (dst < dstend && | 
| 342 |          (cnvres= (cs->cset->mb_wc)(cs, &wc, | 
| 343 |                                     (const uchar*) src, | 
| 344 |                                     (const uchar*) srcend)) > 0 && | 
| 345 |          wc < 128) | 
| 346 |   { | 
| 347 |     src+= cnvres; | 
| 348 |     *dst++= static_cast<char>(wc); | 
| 349 |   } | 
| 350 |   *dst= '\0'; | 
| 351 |   return (uint)(dst - dst0); | 
| 352 | } | 
| 353 |  | 
| 354 |  | 
| 355 | /* Character set-aware version of str_to_time() */ | 
| 356 | bool | 
| 357 | str_to_time(CHARSET_INFO *cs, const char *str, size_t length, | 
| 358 |             MYSQL_TIME *l_time, ulonglong fuzzydate, MYSQL_TIME_STATUS *status) | 
| 359 | { | 
| 360 |   char cnv[32]; | 
| 361 |   if ((cs->state & MY_CS_NONASCII) != 0) | 
| 362 |   { | 
| 363 |     length= to_ascii(cs, str, length, cnv, sizeof(cnv)); | 
| 364 |     str= cnv; | 
| 365 |   } | 
| 366 |   return str_to_time(str, length, l_time, fuzzydate, status); | 
| 367 | } | 
| 368 |  | 
| 369 |  | 
| 370 | /* Character set-aware version of str_to_datetime() */ | 
| 371 | bool str_to_datetime(CHARSET_INFO *cs, const char *str, size_t length, | 
| 372 |                      MYSQL_TIME *l_time, ulonglong flags, | 
| 373 |                      MYSQL_TIME_STATUS *status) | 
| 374 | { | 
| 375 |   char cnv[32]; | 
| 376 |   if ((cs->state & MY_CS_NONASCII) != 0) | 
| 377 |   { | 
| 378 |     length= to_ascii(cs, str, length, cnv, sizeof(cnv)); | 
| 379 |     str= cnv; | 
| 380 |   } | 
| 381 |   return str_to_datetime(str, length, l_time, flags, status); | 
| 382 | } | 
| 383 |  | 
| 384 |  | 
| 385 | /* | 
| 386 |   Convert a timestamp string to a MYSQL_TIME value and produce a warning  | 
| 387 |   if string was truncated during conversion. | 
| 388 |  | 
| 389 |   NOTE | 
| 390 |     See description of str_to_datetime() for more information. | 
| 391 | */ | 
| 392 |  | 
| 393 | bool | 
| 394 | str_to_datetime_with_warn(CHARSET_INFO *cs, | 
| 395 |                           const char *str, size_t length, MYSQL_TIME *l_time, | 
| 396 |                           ulonglong flags) | 
| 397 | { | 
| 398 |   MYSQL_TIME_STATUS status; | 
| 399 |   THD *thd= current_thd; | 
| 400 |   bool ret_val= str_to_datetime(cs, str, length, l_time, flags, &status); | 
| 401 |   if (ret_val || status.warnings) | 
| 402 |     make_truncated_value_warning(thd, | 
| 403 |                                  ret_val ? Sql_condition::WARN_LEVEL_WARN : | 
| 404 |                                  Sql_condition::time_warn_level(status.warnings), | 
| 405 |                                  str, length, flags & TIME_TIME_ONLY ? | 
| 406 |                                  MYSQL_TIMESTAMP_TIME : l_time->time_type, NullS); | 
| 407 |   DBUG_EXECUTE_IF("str_to_datetime_warn" , | 
| 408 |                   push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, | 
| 409 |                                ER_YES, str);); | 
| 410 |   return ret_val; | 
| 411 | } | 
| 412 |  | 
| 413 |  | 
| 414 | /** | 
| 415 |   converts a pair of numbers (integer part, microseconds) to MYSQL_TIME | 
| 416 |  | 
| 417 |   @param neg           sign of the time value | 
| 418 |   @param nr            integer part of the number to convert | 
| 419 |   @param sec_part      microsecond part of the number | 
| 420 |   @param ltime         converted value will be written here | 
| 421 |   @param fuzzydate     conversion flags (TIME_INVALID_DATE, etc) | 
| 422 |   @param str           original number, as an ErrConv. For the warning | 
| 423 |   @param field_name    field name or NULL if not a field. For the warning | 
| 424 |    | 
| 425 |   @returns 0 for success, 1 for a failure | 
| 426 | */ | 
| 427 | static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part, | 
| 428 |                                      MYSQL_TIME *ltime, ulonglong fuzzydate, | 
| 429 |                                      const ErrConv *str, | 
| 430 |                                      const char *field_name) | 
| 431 | { | 
| 432 |   int was_cut; | 
| 433 |   longlong res; | 
| 434 |   enum_mysql_timestamp_type ts_type; | 
| 435 |   bool have_warnings; | 
| 436 |  | 
| 437 |   if (fuzzydate & TIME_TIME_ONLY) | 
| 438 |   { | 
| 439 |     fuzzydate= TIME_TIME_ONLY; // clear other flags | 
| 440 |     ts_type= MYSQL_TIMESTAMP_TIME; | 
| 441 |     res= number_to_time(neg, nr, sec_part, ltime, &was_cut); | 
| 442 |     have_warnings= MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut); | 
| 443 |   } | 
| 444 |   else | 
| 445 |   { | 
| 446 |     ts_type= MYSQL_TIMESTAMP_DATETIME; | 
| 447 |     if (neg) | 
| 448 |     { | 
| 449 |       res= -1; | 
| 450 |     } | 
| 451 |     else | 
| 452 |     { | 
| 453 |       res= number_to_datetime(nr, sec_part, ltime, fuzzydate, &was_cut); | 
| 454 |       have_warnings= was_cut && (fuzzydate & TIME_NO_ZERO_IN_DATE); | 
| 455 |     } | 
| 456 |   } | 
| 457 |  | 
| 458 |   if (res < 0 || have_warnings) | 
| 459 |   { | 
| 460 |     make_truncated_value_warning(current_thd, | 
| 461 |                                  Sql_condition::WARN_LEVEL_WARN, str, | 
| 462 |                                  res < 0 ? MYSQL_TIMESTAMP_ERROR : ts_type, | 
| 463 |                                  field_name); | 
| 464 |   } | 
| 465 |   return res < 0; | 
| 466 | } | 
| 467 |  | 
| 468 |  | 
| 469 | bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime, | 
| 470 |                                   ulonglong fuzzydate, const char *field_name) | 
| 471 | { | 
| 472 |   const ErrConvDouble str(value); | 
| 473 |   bool neg= value < 0; | 
| 474 |  | 
| 475 |   if (neg) | 
| 476 |     value= -value; | 
| 477 |  | 
| 478 |   if (value > LONGLONG_MAX) | 
| 479 |     value= static_cast<double>(LONGLONG_MAX); | 
| 480 |  | 
| 481 |   longlong nr= static_cast<ulonglong>(floor(value)); | 
| 482 |   uint sec_part= static_cast<ulong>((value - floor(value))*TIME_SECOND_PART_FACTOR); | 
| 483 |   return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate, &str, | 
| 484 |                                   field_name); | 
| 485 | } | 
| 486 |  | 
| 487 |  | 
| 488 | bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime, | 
| 489 |                                    ulonglong fuzzydate, const char *field_name) | 
| 490 | { | 
| 491 |   const ErrConvDecimal str(value); | 
| 492 |   ulonglong nr; | 
| 493 |   ulong sec_part; | 
| 494 |   bool neg= my_decimal2seconds(value, &nr, &sec_part); | 
| 495 |   return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate, &str, | 
| 496 |                                   field_name); | 
| 497 | } | 
| 498 |  | 
| 499 |  | 
| 500 | bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime, | 
| 501 |                                ulonglong fuzzydate, const char *field_name) | 
| 502 | { | 
| 503 |   const ErrConvInteger str(neg ? - (longlong) value : (longlong) value, !neg); | 
| 504 |   return number_to_time_with_warn(neg, value, 0, ltime, | 
| 505 |                                   fuzzydate, &str, field_name); | 
| 506 | } | 
| 507 |  | 
| 508 |  | 
| 509 | /* | 
| 510 |   Convert a datetime from broken-down MYSQL_TIME representation to | 
| 511 |   corresponding TIMESTAMP value. | 
| 512 |  | 
| 513 |   SYNOPSIS | 
| 514 |     TIME_to_timestamp() | 
| 515 |       thd             - current thread | 
| 516 |       t               - datetime in broken-down representation,  | 
| 517 |       error_code      - 0, if the conversion was successful; | 
| 518 |                         ER_WARN_DATA_OUT_OF_RANGE, if t contains datetime value | 
| 519 |                            which is out of TIMESTAMP range; | 
| 520 |                         ER_WARN_INVALID_TIMESTAMP, if t represents value which | 
| 521 |                            doesn't exists (falls into the spring time-gap). | 
| 522 |     | 
| 523 |   RETURN | 
| 524 |      Number seconds in UTC since start of Unix Epoch corresponding to t. | 
| 525 |      0 - in case of ER_WARN_DATA_OUT_OF_RANGE | 
| 526 | */ | 
| 527 |  | 
| 528 | my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error_code) | 
| 529 | { | 
| 530 |   thd->time_zone_used= 1; | 
| 531 |   return thd->variables.time_zone->TIME_to_gmt_sec(t, error_code); | 
| 532 | } | 
| 533 |  | 
| 534 |  | 
| 535 | /* | 
| 536 |   Convert a system time structure to TIME | 
| 537 | */ | 
| 538 |  | 
| 539 | void localtime_to_TIME(MYSQL_TIME *to, struct tm *from) | 
| 540 | { | 
| 541 |   to->neg=0; | 
| 542 |   to->second_part=0; | 
| 543 |   to->year=	(int) ((from->tm_year+1900) % 10000); | 
| 544 |   to->month=	(int) from->tm_mon+1; | 
| 545 |   to->day=	(int) from->tm_mday; | 
| 546 |   to->hour=	(int) from->tm_hour; | 
| 547 |   to->minute=	(int) from->tm_min; | 
| 548 |   to->second=   (int) from->tm_sec; | 
| 549 | } | 
| 550 |  | 
| 551 |  | 
| 552 | void calc_time_from_sec(MYSQL_TIME *to, long seconds, long microseconds) | 
| 553 | { | 
| 554 |   long t_seconds; | 
| 555 |   // to->neg is not cleared, it may already be set to a useful value | 
| 556 |   to->time_type= MYSQL_TIMESTAMP_TIME; | 
| 557 |   to->year= 0; | 
| 558 |   to->month= 0; | 
| 559 |   to->day= 0; | 
| 560 |   to->hour= seconds/3600L; | 
| 561 |   t_seconds= seconds%3600L; | 
| 562 |   to->minute= t_seconds/60L; | 
| 563 |   to->second= t_seconds%60L; | 
| 564 |   to->second_part= microseconds; | 
| 565 | } | 
| 566 |  | 
| 567 |  | 
| 568 | /* | 
| 569 |   Parse a format string specification | 
| 570 |  | 
| 571 |   SYNOPSIS | 
| 572 |     parse_date_time_format() | 
| 573 |     format_type		Format of string (time, date or datetime) | 
| 574 |     format_str		String to parse | 
| 575 |     format_length	Length of string | 
| 576 |     date_time_format	Format to fill in | 
| 577 |  | 
| 578 |   NOTES | 
| 579 |     Fills in date_time_format->positions for all date time parts. | 
| 580 |  | 
| 581 |     positions marks the position for a datetime element in the format string. | 
| 582 |     The position array elements are in the following order: | 
| 583 |     YYYY-DD-MM HH-MM-DD.FFFFFF AM | 
| 584 |     0    1  2  3  4  5  6      7 | 
| 585 |  | 
| 586 |     If positions[0]= 5, it means that year will be the forth element to | 
| 587 |     read from the parsed date string. | 
| 588 |  | 
| 589 |   RETURN | 
| 590 |     0	ok | 
| 591 |     1	error | 
| 592 | */ | 
| 593 |  | 
| 594 | bool parse_date_time_format(timestamp_type format_type,  | 
| 595 | 			    const char *format, uint format_length, | 
| 596 | 			    DATE_TIME_FORMAT *date_time_format) | 
| 597 | { | 
| 598 |   uint offset= 0, separators= 0; | 
| 599 |   const char *ptr= format, *format_str; | 
| 600 |   const char *end= ptr+format_length; | 
| 601 |   uchar *dt_pos= date_time_format->positions; | 
| 602 |   /* need_p is set if we are using AM/PM format */ | 
| 603 |   bool need_p= 0, allow_separator= 0; | 
| 604 |   ulong part_map= 0, separator_map= 0; | 
| 605 |   const char *parts[16]; | 
| 606 |  | 
| 607 |   date_time_format->time_separator= 0; | 
| 608 |   date_time_format->flag= 0;			// For future | 
| 609 |  | 
| 610 |   /* | 
| 611 |     Fill position with 'dummy' arguments to found out if a format tag is | 
| 612 |     used twice (This limit's the format to 255 characters, but this is ok) | 
| 613 |   */ | 
| 614 |   dt_pos[0]= dt_pos[1]= dt_pos[2]= dt_pos[3]= | 
| 615 |     dt_pos[4]= dt_pos[5]= dt_pos[6]= dt_pos[7]= 255; | 
| 616 |  | 
| 617 |   for (; ptr != end; ptr++) | 
| 618 |   { | 
| 619 |     if (*ptr == '%' && ptr+1 != end) | 
| 620 |     { | 
| 621 |       uint UNINIT_VAR(position); | 
| 622 |       switch (*++ptr) { | 
| 623 |       case 'y':					// Year | 
| 624 |       case 'Y': | 
| 625 | 	position= 0; | 
| 626 | 	break; | 
| 627 |       case 'c':					// Month | 
| 628 |       case 'm': | 
| 629 | 	position= 1; | 
| 630 | 	break; | 
| 631 |       case 'd': | 
| 632 |       case 'e': | 
| 633 | 	position= 2; | 
| 634 | 	break; | 
| 635 |       case 'h': | 
| 636 |       case 'I': | 
| 637 |       case 'l': | 
| 638 | 	need_p= 1;				// Need AM/PM | 
| 639 | 	/* Fall through */ | 
| 640 |       case 'k': | 
| 641 |       case 'H': | 
| 642 | 	position= 3; | 
| 643 | 	break; | 
| 644 |       case 'i': | 
| 645 | 	position= 4; | 
| 646 | 	break; | 
| 647 |       case 's': | 
| 648 |       case 'S': | 
| 649 | 	position= 5; | 
| 650 | 	break; | 
| 651 |       case 'f': | 
| 652 | 	position= 6; | 
| 653 | 	if (dt_pos[5] != offset-1 || ptr[-2] != '.') | 
| 654 | 	  return 1;				// Wrong usage of %f | 
| 655 | 	break; | 
| 656 |       case 'p':					// AM/PM | 
| 657 | 	if (offset == 0)			// Can't be first | 
| 658 | 	  return 0; | 
| 659 | 	position= 7; | 
| 660 | 	break; | 
| 661 |       default: | 
| 662 | 	return 1;				// Unknown controll char | 
| 663 |       } | 
| 664 |       if (dt_pos[position] != 255)		// Don't allow same tag twice | 
| 665 | 	return 1; | 
| 666 |       parts[position]= ptr-1; | 
| 667 |  | 
| 668 |       /* | 
| 669 | 	If switching from time to date, ensure that all time parts | 
| 670 | 	are used | 
| 671 |       */ | 
| 672 |       if (part_map && position <= 2 && !(part_map & (1 | 2 | 4))) | 
| 673 | 	offset=5; | 
| 674 |       part_map|= (ulong) 1 << position; | 
| 675 |       dt_pos[position]= offset++; | 
| 676 |       allow_separator= 1; | 
| 677 |     } | 
| 678 |     else | 
| 679 |     { | 
| 680 |       /* | 
| 681 | 	Don't allow any characters in format as this could easily confuse | 
| 682 | 	the date reader | 
| 683 |       */ | 
| 684 |       if (!allow_separator) | 
| 685 | 	return 1;				// No separator here | 
| 686 |       allow_separator= 0;			// Don't allow two separators | 
| 687 |       separators++; | 
| 688 |       /* Store in separator_map which parts are punct characters */ | 
| 689 |       if (my_ispunct(&my_charset_latin1, *ptr)) | 
| 690 | 	separator_map|= (ulong) 1 << (offset-1); | 
| 691 |       else if (!my_isspace(&my_charset_latin1, *ptr)) | 
| 692 | 	return 1; | 
| 693 |     } | 
| 694 |   } | 
| 695 |  | 
| 696 |   /* If no %f, specify it after seconds.  Move %p up, if necessary */ | 
| 697 |   if ((part_map & 32) && !(part_map & 64)) | 
| 698 |   { | 
| 699 |     dt_pos[6]= dt_pos[5] +1; | 
| 700 |     parts[6]= parts[5];				// For later test in (need_p) | 
| 701 |     if (dt_pos[6] == dt_pos[7])			// Move %p one step up if used | 
| 702 |       dt_pos[7]++; | 
| 703 |   } | 
| 704 |  | 
| 705 |   /* | 
| 706 |     Check that we have not used a non legal format specifier and that all | 
| 707 |     format specifiers have been used | 
| 708 |  | 
| 709 |     The last test is to ensure that %p is used if and only if | 
| 710 |     it's needed. | 
| 711 |   */ | 
| 712 |   if ((format_type == MYSQL_TIMESTAMP_DATETIME && | 
| 713 |        !test_all_bits(part_map, (1 | 2 | 4 | 8 | 16 | 32))) || | 
| 714 |       (format_type == MYSQL_TIMESTAMP_DATE && part_map != (1 | 2 | 4)) || | 
| 715 |       (format_type == MYSQL_TIMESTAMP_TIME && | 
| 716 |        !test_all_bits(part_map, 8 | 16 | 32)) || | 
| 717 |       !allow_separator ||			// %option should be last | 
| 718 |       (need_p && dt_pos[6] +1 != dt_pos[7]) || | 
| 719 |       (need_p ^ (dt_pos[7] != 255))) | 
| 720 |     return 1; | 
| 721 |  | 
| 722 |   if (dt_pos[6] != 255)				// If fractional seconds | 
| 723 |   { | 
| 724 |     /* remove fractional seconds from later tests */ | 
| 725 |     uint pos= dt_pos[6] -1; | 
| 726 |     /* Remove separator before %f from sep map */ | 
| 727 |     separator_map= ((separator_map & ((ulong) (1 << pos)-1)) | | 
| 728 | 		    ((separator_map & ~((ulong) (1 << pos)-1)) >> 1)); | 
| 729 |     if (part_map & 64)			       | 
| 730 |     { | 
| 731 |       separators--;				// There is always a separator | 
| 732 |       need_p= 1;				// force use of separators | 
| 733 |     } | 
| 734 |   } | 
| 735 |  | 
| 736 |   /* | 
| 737 |     Remove possible separator before %p from sep_map | 
| 738 |     (This can either be at position 3, 4, 6 or 7) h.m.d.%f %p | 
| 739 |   */ | 
| 740 |   if (dt_pos[7] != 255) | 
| 741 |   { | 
| 742 |     if (need_p && parts[7] != parts[6]+2) | 
| 743 |       separators--; | 
| 744 |   }      | 
| 745 |   /* | 
| 746 |     Calculate if %p is in first or last part of the datetime field | 
| 747 |  | 
| 748 |     At this point we have either %H-%i-%s %p 'year parts' or | 
| 749 |     'year parts' &H-%i-%s %p" as %f was removed above | 
| 750 |   */ | 
| 751 |   offset= dt_pos[6] <= 3 ? 3 : 6; | 
| 752 |   /* Remove separator before %p from sep map */ | 
| 753 |   separator_map= ((separator_map & ((ulong) (1 << offset)-1)) | | 
| 754 | 		  ((separator_map & ~((ulong) (1 << offset)-1)) >> 1)); | 
| 755 |  | 
| 756 |   format_str= 0; | 
| 757 |   switch (format_type) { | 
| 758 |   case MYSQL_TIMESTAMP_DATE: | 
| 759 |     format_str= known_date_time_formats[INTERNAL_FORMAT].date_format; | 
| 760 |     /* fall through */ | 
| 761 |   case MYSQL_TIMESTAMP_TIME: | 
| 762 |     if (!format_str) | 
| 763 |       format_str=known_date_time_formats[INTERNAL_FORMAT].time_format; | 
| 764 |  | 
| 765 |     /* | 
| 766 |       If there is no separators, allow the internal format as we can read | 
| 767 |       this.  If separators are used, they must be between each part | 
| 768 |     */ | 
| 769 |     if (format_length == 6 && !need_p && | 
| 770 | 	!my_strnncoll(&my_charset_bin, | 
| 771 | 		      (const uchar *) format, 6,  | 
| 772 | 		      (const uchar *) format_str, 6)) | 
| 773 |       return 0; | 
| 774 |     if (separator_map == (1 | 2)) | 
| 775 |     { | 
| 776 |       if (format_type == MYSQL_TIMESTAMP_TIME) | 
| 777 |       { | 
| 778 | 	if (*(format+2) != *(format+5)) | 
| 779 | 	  break;				// Error | 
| 780 | 	/* Store the character used for time formats */ | 
| 781 | 	date_time_format->time_separator= *(format+2); | 
| 782 |       } | 
| 783 |       return 0; | 
| 784 |     } | 
| 785 |     break; | 
| 786 |   case MYSQL_TIMESTAMP_DATETIME: | 
| 787 |     /* | 
| 788 |       If there is no separators, allow the internal format as we can read | 
| 789 |       this.  If separators are used, they must be between each part. | 
| 790 |       Between DATE and TIME we also allow space as separator | 
| 791 |     */ | 
| 792 |     if ((format_length == 12 && !need_p && | 
| 793 | 	 !my_strnncoll(&my_charset_bin,  | 
| 794 | 		       (const uchar *) format, 12, | 
| 795 | 		       (const uchar*) known_date_time_formats[INTERNAL_FORMAT].datetime_format, | 
| 796 | 		       12)) || | 
| 797 | 	(separators == 5 && separator_map == (1 | 2 | 8 | 16))) | 
| 798 |       return 0; | 
| 799 |     break; | 
| 800 |   default: | 
| 801 |     DBUG_ASSERT(0); | 
| 802 |     break; | 
| 803 |   } | 
| 804 |   return 1;					// Error | 
| 805 | } | 
| 806 |  | 
| 807 |  | 
| 808 | /* | 
| 809 |   Create a DATE_TIME_FORMAT object from a format string specification | 
| 810 |  | 
| 811 |   SYNOPSIS | 
| 812 |     date_time_format_make() | 
| 813 |     format_type		Format to parse (time, date or datetime) | 
| 814 |     format_str		String to parse | 
| 815 |     format_length	Length of string | 
| 816 |  | 
| 817 |   NOTES | 
| 818 |     The returned object should be freed with my_free() | 
| 819 |  | 
| 820 |   RETURN | 
| 821 |     NULL ponter:	Error | 
| 822 |     new object | 
| 823 | */ | 
| 824 |  | 
| 825 | DATE_TIME_FORMAT | 
| 826 | *date_time_format_make(timestamp_type format_type, | 
| 827 | 		       const char *format_str, uint format_length) | 
| 828 | { | 
| 829 |   DATE_TIME_FORMAT tmp; | 
| 830 |  | 
| 831 |   if (format_length && format_length < 255 && | 
| 832 |       !parse_date_time_format(format_type, format_str, | 
| 833 | 			      format_length, &tmp)) | 
| 834 |   { | 
| 835 |     tmp.format.str=    format_str; | 
| 836 |     tmp.format.length= format_length; | 
| 837 |     return date_time_format_copy((THD *)0, &tmp); | 
| 838 |   } | 
| 839 |   return 0; | 
| 840 | } | 
| 841 |  | 
| 842 |  | 
| 843 | /* | 
| 844 |   Create a copy of a DATE_TIME_FORMAT object | 
| 845 |  | 
| 846 |   SYNOPSIS | 
| 847 |     date_and_time_format_copy() | 
| 848 |     thd			Set if variable should be allocated in thread mem | 
| 849 |     format		format to copy | 
| 850 |  | 
| 851 |   NOTES | 
| 852 |     The returned object should be freed with my_free() | 
| 853 |  | 
| 854 |   RETURN | 
| 855 |     NULL ponter:	Error | 
| 856 |     new object | 
| 857 | */ | 
| 858 |  | 
| 859 | DATE_TIME_FORMAT *date_time_format_copy(THD *thd, DATE_TIME_FORMAT *format) | 
| 860 | { | 
| 861 |   DATE_TIME_FORMAT *new_format; | 
| 862 |   size_t length= sizeof(*format) + format->format.length + 1; | 
| 863 |   char *format_pos; | 
| 864 |  | 
| 865 |   if (thd) | 
| 866 |     new_format= (DATE_TIME_FORMAT *) thd->alloc(length); | 
| 867 |   else | 
| 868 |     new_format=  (DATE_TIME_FORMAT *) my_malloc(length, MYF(MY_WME)); | 
| 869 |   if (new_format) | 
| 870 |   { | 
| 871 |     /* Put format string after current pos */ | 
| 872 |     new_format->format.str= format_pos= (char*) (new_format+1); | 
| 873 |     memcpy((char*) new_format->positions, (char*) format->positions, | 
| 874 | 	   sizeof(format->positions)); | 
| 875 |     new_format->time_separator= format->time_separator; | 
| 876 |     /* We make the string null terminated for easy printf in SHOW VARIABLES */ | 
| 877 |     memcpy(format_pos, format->format.str, format->format.length); | 
| 878 |     format_pos[format->format.length]= 0; | 
| 879 |     new_format->format.length= format->format.length; | 
| 880 |   } | 
| 881 |   return new_format; | 
| 882 | } | 
| 883 |  | 
| 884 |  | 
| 885 | KNOWN_DATE_TIME_FORMAT known_date_time_formats[6]= | 
| 886 | { | 
| 887 |   {"USA" , "%m.%d.%Y" , "%Y-%m-%d %H.%i.%s" , "%h:%i:%s %p"  }, | 
| 888 |   {"JIS" , "%Y-%m-%d" , "%Y-%m-%d %H:%i:%s" , "%H:%i:%s"  }, | 
| 889 |   {"ISO" , "%Y-%m-%d" , "%Y-%m-%d %H:%i:%s" , "%H:%i:%s"  }, | 
| 890 |   {"EUR" , "%d.%m.%Y" , "%Y-%m-%d %H.%i.%s" , "%H.%i.%s"  }, | 
| 891 |   {"INTERNAL" , "%Y%m%d" ,   "%Y%m%d%H%i%s" , "%H%i%s"  }, | 
| 892 |   { 0, 0, 0, 0 } | 
| 893 | }; | 
| 894 |  | 
| 895 |  | 
| 896 | const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format, | 
| 897 | 				     timestamp_type type) | 
| 898 | { | 
| 899 |   switch (type) { | 
| 900 |   case MYSQL_TIMESTAMP_DATE: | 
| 901 |     return format->date_format; | 
| 902 |   case MYSQL_TIMESTAMP_DATETIME: | 
| 903 |     return format->datetime_format; | 
| 904 |   case MYSQL_TIMESTAMP_TIME: | 
| 905 |     return format->time_format; | 
| 906 |   default: | 
| 907 |     DBUG_ASSERT(0);				// Impossible | 
| 908 |     return 0; | 
| 909 |   } | 
| 910 | } | 
| 911 |  | 
| 912 |  | 
| 913 | /** | 
| 914 |   Convert TIME/DATE/DATETIME value to String. | 
| 915 |   @param l_time   DATE value | 
| 916 |   @param OUT str  String to convert to | 
| 917 |   @param dec      Number of fractional digits. | 
| 918 | */ | 
| 919 | bool my_TIME_to_str(const MYSQL_TIME *ltime, String *str, uint dec) | 
| 920 | { | 
| 921 |   if (str->alloc(MAX_DATE_STRING_REP_LENGTH)) | 
| 922 |     return true; | 
| 923 |   str->set_charset(&my_charset_numeric); | 
| 924 |   str->length(my_TIME_to_str(ltime, const_cast<char*>(str->ptr()), dec)); | 
| 925 |   return false; | 
| 926 | } | 
| 927 |  | 
| 928 |  | 
| 929 | void make_truncated_value_warning(THD *thd, | 
| 930 |                                   Sql_condition::enum_warning_level level, | 
| 931 |                                   const ErrConv *sval, | 
| 932 | 				  timestamp_type time_type, | 
| 933 |                                   const char *field_name) | 
| 934 | { | 
| 935 |   char warn_buff[MYSQL_ERRMSG_SIZE]; | 
| 936 |   const char *type_str; | 
| 937 |   CHARSET_INFO *cs= &my_charset_latin1; | 
| 938 |  | 
| 939 |   switch (time_type) { | 
| 940 |     case MYSQL_TIMESTAMP_DATE:  | 
| 941 |       type_str= "date" ; | 
| 942 |       break; | 
| 943 |     case MYSQL_TIMESTAMP_TIME: | 
| 944 |       type_str= "time" ; | 
| 945 |       break; | 
| 946 |     case MYSQL_TIMESTAMP_DATETIME:  // FALLTHROUGH | 
| 947 |     default: | 
| 948 |       type_str= "datetime" ; | 
| 949 |       break; | 
| 950 |   } | 
| 951 |   if (field_name) | 
| 952 |     cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff), | 
| 953 |                        ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), | 
| 954 |                        type_str, sval->ptr(), field_name, | 
| 955 |                        (ulong) thd->get_stmt_da()->current_row_for_warning()); | 
| 956 |   else | 
| 957 |   { | 
| 958 |     if (time_type > MYSQL_TIMESTAMP_ERROR) | 
| 959 |       cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff), | 
| 960 |                          ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), | 
| 961 |                          type_str, sval->ptr()); | 
| 962 |     else | 
| 963 |       cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff), | 
| 964 |                          ER_THD(thd, ER_WRONG_VALUE), type_str, sval->ptr()); | 
| 965 |   } | 
| 966 |   push_warning(thd, level, | 
| 967 |                ER_TRUNCATED_WRONG_VALUE, warn_buff); | 
| 968 | } | 
| 969 |  | 
| 970 |  | 
| 971 | /* Daynumber from year 0 to 9999-12-31 */ | 
| 972 | #define COMBINE(X)                                                      \ | 
| 973 |                (((((X)->day * 24LL + (X)->hour) * 60LL +                \ | 
| 974 |                    (X)->minute) * 60LL + (X)->second)*1000000LL +       \ | 
| 975 |                    (X)->second_part) | 
| 976 | #define GET_PART(X, N) X % N ## LL; X/= N ## LL | 
| 977 |  | 
| 978 | bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, | 
| 979 |                        const INTERVAL &interval) | 
| 980 | { | 
| 981 |   long period, sign; | 
| 982 |  | 
| 983 |   sign= (interval.neg == (bool)ltime->neg ? 1 : -1); | 
| 984 |  | 
| 985 |   switch (int_type) { | 
| 986 |   case INTERVAL_SECOND: | 
| 987 |   case INTERVAL_SECOND_MICROSECOND: | 
| 988 |   case INTERVAL_MICROSECOND: | 
| 989 |   case INTERVAL_MINUTE: | 
| 990 |   case INTERVAL_HOUR: | 
| 991 |   case INTERVAL_MINUTE_MICROSECOND: | 
| 992 |   case INTERVAL_MINUTE_SECOND: | 
| 993 |   case INTERVAL_HOUR_MICROSECOND: | 
| 994 |   case INTERVAL_HOUR_SECOND: | 
| 995 |   case INTERVAL_HOUR_MINUTE: | 
| 996 |   case INTERVAL_DAY_MICROSECOND: | 
| 997 |   case INTERVAL_DAY_SECOND: | 
| 998 |   case INTERVAL_DAY_MINUTE: | 
| 999 |   case INTERVAL_DAY_HOUR: | 
| 1000 |   case INTERVAL_DAY: | 
| 1001 |   { | 
| 1002 |     longlong usec, daynr; | 
| 1003 |     my_bool neg= 0; | 
| 1004 |     enum enum_mysql_timestamp_type time_type= ltime->time_type; | 
| 1005 |  | 
| 1006 |     if (((ulonglong) interval.day + | 
| 1007 |          (ulonglong) interval.hour / 24 + | 
| 1008 |          (ulonglong) interval.minute / 24 / 60 + | 
| 1009 |          (ulonglong) interval.second / 24 / 60 / 60) > MAX_DAY_NUMBER) | 
| 1010 |       goto invalid_date; | 
| 1011 |  | 
| 1012 |     if (time_type != MYSQL_TIMESTAMP_TIME) | 
| 1013 |       ltime->day+= calc_daynr(ltime->year, ltime->month, 1) - 1; | 
| 1014 |  | 
| 1015 |     usec= COMBINE(ltime) + sign*COMBINE(&interval); | 
| 1016 |  | 
| 1017 |     if (usec < 0) | 
| 1018 |     { | 
| 1019 |       neg= 1; | 
| 1020 |       usec= -usec; | 
| 1021 |     } | 
| 1022 |  | 
| 1023 |     ltime->second_part= GET_PART(usec, 1000000); | 
| 1024 |     ltime->second= GET_PART(usec, 60); | 
| 1025 |     ltime->minute= GET_PART(usec, 60); | 
| 1026 |     ltime->neg^= neg; | 
| 1027 |  | 
| 1028 |     if (time_type == MYSQL_TIMESTAMP_TIME) | 
| 1029 |     { | 
| 1030 |       if (usec > TIME_MAX_HOUR) | 
| 1031 |         goto invalid_date; | 
| 1032 |       ltime->hour= static_cast<uint>(usec); | 
| 1033 |       ltime->day= 0; | 
| 1034 |       return 0; | 
| 1035 |     } | 
| 1036 |     else if (ltime->neg) | 
| 1037 |       goto invalid_date; | 
| 1038 |  | 
| 1039 |     if (int_type != INTERVAL_DAY) | 
| 1040 |       ltime->time_type= MYSQL_TIMESTAMP_DATETIME; // Return full date | 
| 1041 |  | 
| 1042 |     ltime->hour= GET_PART(usec, 24); | 
| 1043 |     daynr= usec; | 
| 1044 |  | 
| 1045 |     /* Day number from year 0 to 9999-12-31 */ | 
| 1046 |     if (get_date_from_daynr((long) daynr, <ime->year, <ime->month, | 
| 1047 |                             <ime->day)) | 
| 1048 |       goto invalid_date; | 
| 1049 |     break; | 
| 1050 |   } | 
| 1051 |   case INTERVAL_WEEK: | 
| 1052 |     period= (calc_daynr(ltime->year,ltime->month,ltime->day) + | 
| 1053 |              sign * (long) interval.day); | 
| 1054 |     /* Daynumber from year 0 to 9999-12-31 */ | 
| 1055 |     if (get_date_from_daynr((long) period,<ime->year,<ime->month, | 
| 1056 |                             <ime->day)) | 
| 1057 |       goto invalid_date; | 
| 1058 |     break; | 
| 1059 |   case INTERVAL_YEAR: | 
| 1060 |     ltime->year+= sign * (long) interval.year; | 
| 1061 |     if ((ulong) ltime->year >= 10000L) | 
| 1062 |       goto invalid_date; | 
| 1063 |     if (ltime->month == 2 && ltime->day == 29 && | 
| 1064 | 	calc_days_in_year(ltime->year) != 366) | 
| 1065 |       ltime->day=28;				// Was leap-year | 
| 1066 |     break; | 
| 1067 |   case INTERVAL_YEAR_MONTH: | 
| 1068 |   case INTERVAL_QUARTER: | 
| 1069 |   case INTERVAL_MONTH: | 
| 1070 |     period= (ltime->year*12 + sign * (long) interval.year*12 + | 
| 1071 | 	     ltime->month-1 + sign * (long) interval.month); | 
| 1072 |     if ((ulong) period >= 120000L) | 
| 1073 |       goto invalid_date; | 
| 1074 |     ltime->year= (uint) (period / 12); | 
| 1075 |     ltime->month= (uint) (period % 12L)+1; | 
| 1076 |     /* Adjust day if the new month doesn't have enough days */ | 
| 1077 |     if (ltime->day > days_in_month[ltime->month-1]) | 
| 1078 |     { | 
| 1079 |       ltime->day = days_in_month[ltime->month-1]; | 
| 1080 |       if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366) | 
| 1081 | 	ltime->day++;				// Leap-year | 
| 1082 |     } | 
| 1083 |     break; | 
| 1084 |   default: | 
| 1085 |     goto null_date; | 
| 1086 |   } | 
| 1087 |  | 
| 1088 |   if (ltime->time_type != MYSQL_TIMESTAMP_TIME) | 
| 1089 |     return 0;                                   // Ok | 
| 1090 |  | 
| 1091 | invalid_date: | 
| 1092 |   { | 
| 1093 |     THD *thd= current_thd; | 
| 1094 |     push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, | 
| 1095 |                         ER_DATETIME_FUNCTION_OVERFLOW, | 
| 1096 |                         ER_THD(thd, ER_DATETIME_FUNCTION_OVERFLOW), | 
| 1097 |                         ltime->time_type == MYSQL_TIMESTAMP_TIME ? | 
| 1098 |                         "time"  : "datetime" ); | 
| 1099 |   } | 
| 1100 | null_date: | 
| 1101 |   return 1; | 
| 1102 | } | 
| 1103 |  | 
| 1104 |  | 
| 1105 | /* | 
| 1106 |   Calculate difference between two datetime values as seconds + microseconds. | 
| 1107 |  | 
| 1108 |   SYNOPSIS | 
| 1109 |     calc_time_diff() | 
| 1110 |       l_time1         - TIME/DATE/DATETIME value | 
| 1111 |       l_time2         - TIME/DATE/DATETIME value | 
| 1112 |       l_sign          - 1 absolute values are substracted, | 
| 1113 |                         -1 absolute values are added. | 
| 1114 |       seconds_out     - Out parameter where difference between | 
| 1115 |                         l_time1 and l_time2 in seconds is stored. | 
| 1116 |       microseconds_out- Out parameter where microsecond part of difference | 
| 1117 |                         between l_time1 and l_time2 is stored. | 
| 1118 |  | 
| 1119 |   NOTE | 
| 1120 |     This function calculates difference between l_time1 and l_time2 absolute | 
| 1121 |     values. So one should set l_sign and correct result if he want to take | 
| 1122 |     signs into account (i.e. for MYSQL_TIME values). | 
| 1123 |  | 
| 1124 |   RETURN VALUES | 
| 1125 |     Returns sign of difference. | 
| 1126 |     1 means negative result | 
| 1127 |     0 means positive result | 
| 1128 |  | 
| 1129 | */ | 
| 1130 |  | 
| 1131 | bool | 
| 1132 | calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, | 
| 1133 |                int l_sign, longlong *seconds_out, long *microseconds_out) | 
| 1134 | { | 
| 1135 |   long days; | 
| 1136 |   bool neg; | 
| 1137 |   longlong microseconds; | 
| 1138 |  | 
| 1139 |   /* | 
| 1140 |     We suppose that if first argument is MYSQL_TIMESTAMP_TIME | 
| 1141 |     the second argument should be TIMESTAMP_TIME also. | 
| 1142 |     We should check it before calc_time_diff call. | 
| 1143 |   */ | 
| 1144 |   if (l_time1->time_type == MYSQL_TIMESTAMP_TIME)  // Time value | 
| 1145 |     days= (long)l_time1->day - l_sign * (long)l_time2->day; | 
| 1146 |   else | 
| 1147 |   { | 
| 1148 |     days= calc_daynr((uint) l_time1->year, | 
| 1149 | 		     (uint) l_time1->month, | 
| 1150 | 		     (uint) l_time1->day); | 
| 1151 |     if (l_time2->time_type == MYSQL_TIMESTAMP_TIME) | 
| 1152 |       days-= l_sign * (long)l_time2->day; | 
| 1153 |     else | 
| 1154 |       days-= l_sign*calc_daynr((uint) l_time2->year, | 
| 1155 | 			       (uint) l_time2->month, | 
| 1156 | 			       (uint) l_time2->day); | 
| 1157 |   } | 
| 1158 |  | 
| 1159 |   microseconds= ((longlong)days * SECONDS_IN_24H + | 
| 1160 |                  (longlong)(l_time1->hour*3600L + | 
| 1161 |                             l_time1->minute*60L + | 
| 1162 |                             l_time1->second) - | 
| 1163 |                  l_sign*(longlong)(l_time2->hour*3600L + | 
| 1164 |                                    l_time2->minute*60L + | 
| 1165 |                                    l_time2->second)) * 1000000LL + | 
| 1166 |                 (longlong)l_time1->second_part - | 
| 1167 |                 l_sign*(longlong)l_time2->second_part; | 
| 1168 |  | 
| 1169 |   neg= 0; | 
| 1170 |   if (microseconds < 0) | 
| 1171 |   { | 
| 1172 |     microseconds= -microseconds; | 
| 1173 |     neg= 1; | 
| 1174 |   } | 
| 1175 |   *seconds_out= microseconds/1000000L; | 
| 1176 |   *microseconds_out= (long) (microseconds%1000000L); | 
| 1177 |   return neg; | 
| 1178 | } | 
| 1179 |  | 
| 1180 |  | 
| 1181 | bool calc_time_diff(const MYSQL_TIME *l_time1, const MYSQL_TIME *l_time2, | 
| 1182 |                     int l_sign, MYSQL_TIME *l_time3, ulonglong fuzzydate) | 
| 1183 | { | 
| 1184 |   longlong seconds; | 
| 1185 |   long microseconds; | 
| 1186 |   bzero((char *) l_time3, sizeof(*l_time3)); | 
| 1187 |   l_time3->neg= calc_time_diff(l_time1, l_time2, l_sign, | 
| 1188 | 			       &seconds, µseconds); | 
| 1189 |   /* | 
| 1190 |     For MYSQL_TIMESTAMP_TIME only: | 
| 1191 |       If first argument was negative and diff between arguments | 
| 1192 |       is non-zero we need to swap sign to get proper result. | 
| 1193 |   */ | 
| 1194 |   if (l_time1->neg && (seconds || microseconds)) | 
| 1195 |     l_time3->neg= 1 - l_time3->neg;         // Swap sign of result | 
| 1196 |  | 
| 1197 |   /* | 
| 1198 |     seconds is longlong, when casted to long it may become a small number | 
| 1199 |     even if the original seconds value was too large and invalid. | 
| 1200 |     as a workaround we limit seconds by a large invalid long number | 
| 1201 |     ("invalid" means > TIME_MAX_SECOND) | 
| 1202 |   */ | 
| 1203 |   set_if_smaller(seconds, INT_MAX32); | 
| 1204 |   calc_time_from_sec(l_time3, (long) seconds, microseconds); | 
| 1205 |   return ((fuzzydate & TIME_NO_ZERO_DATE) && (seconds == 0) && | 
| 1206 |           (microseconds == 0)); | 
| 1207 | } | 
| 1208 |  | 
| 1209 |  | 
| 1210 | /* | 
| 1211 |   Compares 2 MYSQL_TIME structures | 
| 1212 |  | 
| 1213 |   SYNOPSIS | 
| 1214 |     my_time_compare() | 
| 1215 |  | 
| 1216 |       a - first time | 
| 1217 |       b - second time | 
| 1218 |  | 
| 1219 |   RETURN VALUE | 
| 1220 |    -1   - a < b | 
| 1221 |     0   - a == b | 
| 1222 |     1   - a > b | 
| 1223 |  | 
| 1224 | */ | 
| 1225 |  | 
| 1226 | int my_time_compare(const MYSQL_TIME *a, const MYSQL_TIME *b) | 
| 1227 | { | 
| 1228 |   ulonglong a_t= pack_time(a); | 
| 1229 |   ulonglong b_t= pack_time(b); | 
| 1230 |  | 
| 1231 |   if (a_t < b_t) | 
| 1232 |     return -1; | 
| 1233 |   if (a_t > b_t) | 
| 1234 |     return 1; | 
| 1235 |  | 
| 1236 |   return 0; | 
| 1237 | } | 
| 1238 |  | 
| 1239 |  | 
| 1240 | /** | 
| 1241 |   Convert TIME to DATETIME. | 
| 1242 |   @param   ltime    The value to convert. | 
| 1243 |   @return  false on success, true of error (negative time). | 
| 1244 | */ | 
| 1245 | bool time_to_datetime(MYSQL_TIME *ltime) | 
| 1246 | { | 
| 1247 |   DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_TIME); | 
| 1248 |   DBUG_ASSERT(ltime->year == 0); | 
| 1249 |   DBUG_ASSERT(ltime->month == 0); | 
| 1250 |   DBUG_ASSERT(ltime->day == 0); | 
| 1251 |   if (ltime->neg) | 
| 1252 |     return true; | 
| 1253 |   uint day= ltime->hour / 24; | 
| 1254 |   ltime->hour%= 24; | 
| 1255 |   ltime->month= day / 31; | 
| 1256 |   ltime->day= day % 31;   | 
| 1257 |   return false; | 
| 1258 | } | 
| 1259 |  | 
| 1260 |  | 
| 1261 | /** | 
| 1262 |   Return a valid DATE or DATETIME value from an arbitrary MYSQL_TIME. | 
| 1263 |   If ltime is TIME, it's first converted to DATETIME. | 
| 1264 |   If ts_type is DATE, hhmmss is set to zero. | 
| 1265 |   The date part of the result is checked against fuzzy_date. | 
| 1266 |  | 
| 1267 |   @param   ltime       The value to convert. | 
| 1268 |   @param   fuzzy_date  Flags to check date. | 
| 1269 |   @param   ts_type     The type to convert to. | 
| 1270 |   @return  false on success, true of error (negative time).*/ | 
| 1271 | bool | 
| 1272 | make_date_with_warn(MYSQL_TIME *ltime, ulonglong fuzzy_date, | 
| 1273 |                     timestamp_type ts_type) | 
| 1274 | { | 
| 1275 |   DBUG_ASSERT(ts_type == MYSQL_TIMESTAMP_DATE || | 
| 1276 |               ts_type == MYSQL_TIMESTAMP_DATETIME); | 
| 1277 |   if (ltime->time_type == MYSQL_TIMESTAMP_TIME && time_to_datetime(ltime)) | 
| 1278 |   { | 
| 1279 |     /* e.g. negative time */ | 
| 1280 |     ErrConvTime str(ltime); | 
| 1281 |     make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN, | 
| 1282 |                                  &str, ts_type, 0); | 
| 1283 |     return true; | 
| 1284 |   } | 
| 1285 |   if ((ltime->time_type= ts_type) == MYSQL_TIMESTAMP_DATE) | 
| 1286 |     ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0; | 
| 1287 |   return check_date_with_warn(ltime, fuzzy_date, ts_type); | 
| 1288 | } | 
| 1289 |  | 
| 1290 |  | 
| 1291 | /* | 
| 1292 |   Convert a TIME value to DAY-TIME interval, e.g. for extraction: | 
| 1293 |     EXTRACT(DAY FROM x), EXTRACT(HOUR FROM x), etc. | 
| 1294 |   Moves full days from ltime->hour to ltime->day. | 
| 1295 |   Note, time_type is set to MYSQL_TIMESTAMP_NONE, to make sure that | 
| 1296 |   the structure is not used for anything else other than extraction: | 
| 1297 |   non-extraction TIME functions expect zero day value! | 
| 1298 | */ | 
| 1299 | void time_to_daytime_interval(MYSQL_TIME *ltime) | 
| 1300 | { | 
| 1301 |   DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_TIME); | 
| 1302 |   DBUG_ASSERT(ltime->year == 0); | 
| 1303 |   DBUG_ASSERT(ltime->month == 0); | 
| 1304 |   DBUG_ASSERT(ltime->day == 0); | 
| 1305 |   ltime->day= ltime->hour / 24; | 
| 1306 |   ltime->hour%= 24; | 
| 1307 |   ltime->time_type= MYSQL_TIMESTAMP_NONE; | 
| 1308 | } | 
| 1309 |  | 
| 1310 |  | 
| 1311 | /*** Conversion from TIME to DATETIME ***/ | 
| 1312 |  | 
| 1313 | /* | 
| 1314 |   Simple case: TIME is within normal 24 hours internal. | 
| 1315 |   Mix DATE part of ldate and TIME part of ltime together. | 
| 1316 | */ | 
| 1317 | static void | 
| 1318 | mix_date_and_time_simple(MYSQL_TIME *ldate, const MYSQL_TIME *ltime) | 
| 1319 | { | 
| 1320 |   DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE || | 
| 1321 |               ldate->time_type == MYSQL_TIMESTAMP_DATETIME); | 
| 1322 |   ldate->hour= ltime->hour; | 
| 1323 |   ldate->minute= ltime->minute; | 
| 1324 |   ldate->second= ltime->second; | 
| 1325 |   ldate->second_part= ltime->second_part; | 
| 1326 |   ldate->time_type= MYSQL_TIMESTAMP_DATETIME; | 
| 1327 | } | 
| 1328 |  | 
| 1329 |  | 
| 1330 | /* | 
| 1331 |   Complex case: TIME is negative or outside of the 24 hour interval. | 
| 1332 | */ | 
| 1333 | static void | 
| 1334 | mix_date_and_time_complex(MYSQL_TIME *ldate, const MYSQL_TIME *ltime) | 
| 1335 | { | 
| 1336 |   DBUG_ASSERT(ldate->time_type == MYSQL_TIMESTAMP_DATE || | 
| 1337 |               ldate->time_type == MYSQL_TIMESTAMP_DATETIME); | 
| 1338 |   longlong seconds; | 
| 1339 |   long days, useconds; | 
| 1340 |   int sign= ltime->neg ? 1 : -1; | 
| 1341 |   ldate->neg= calc_time_diff(ldate, ltime, sign, &seconds, &useconds); | 
| 1342 |  | 
| 1343 |   DBUG_ASSERT(!ldate->neg); | 
| 1344 |   DBUG_ASSERT(ldate->year > 0); | 
| 1345 |  | 
| 1346 |   days= (long) (seconds / SECONDS_IN_24H); | 
| 1347 |   calc_time_from_sec(ldate, seconds % SECONDS_IN_24H, useconds); | 
| 1348 |   get_date_from_daynr(days, &ldate->year, &ldate->month, &ldate->day); | 
| 1349 |   ldate->time_type= MYSQL_TIMESTAMP_DATETIME; | 
| 1350 | } | 
| 1351 |  | 
| 1352 |  | 
| 1353 | /** | 
| 1354 |   Mix a date value and a time value. | 
| 1355 |  | 
| 1356 |   @param  IN/OUT  ldate  Date value. | 
| 1357 |   @param          ltime  Time value. | 
| 1358 | */ | 
| 1359 | static void | 
| 1360 | mix_date_and_time(MYSQL_TIME *to, const MYSQL_TIME *from) | 
| 1361 | { | 
| 1362 |   if (!from->neg && from->hour < 24) | 
| 1363 |     mix_date_and_time_simple(to, from); | 
| 1364 |   else | 
| 1365 |     mix_date_and_time_complex(to, from); | 
| 1366 | } | 
| 1367 |  | 
| 1368 |  | 
| 1369 | /** | 
| 1370 |   Get current date in DATE format | 
| 1371 | */ | 
| 1372 | void set_current_date(THD *thd, MYSQL_TIME *to) | 
| 1373 | { | 
| 1374 |   thd->variables.time_zone->gmt_sec_to_TIME(to, thd->query_start()); | 
| 1375 |   thd->time_zone_used= 1; | 
| 1376 |   datetime_to_date(to); | 
| 1377 | } | 
| 1378 |  | 
| 1379 |  | 
| 1380 | /** | 
| 1381 |   5.5 compatible conversion from TIME to DATETIME | 
| 1382 | */ | 
| 1383 | static bool | 
| 1384 | time_to_datetime_old(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to) | 
| 1385 | { | 
| 1386 |   DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME); | 
| 1387 |  | 
| 1388 |   if (from->neg) | 
| 1389 |     return true; | 
| 1390 |  | 
| 1391 |   /* Set the date part */ | 
| 1392 |   uint day= from->hour / 24; | 
| 1393 |   to->day= day % 31; | 
| 1394 |   to->month= day / 31; | 
| 1395 |   to->year= 0; | 
| 1396 |   /* Set the time part */ | 
| 1397 |   to->hour= from->hour % 24; | 
| 1398 |   to->minute= from->minute; | 
| 1399 |   to->second= from->second; | 
| 1400 |   to->second_part= from->second_part; | 
| 1401 |   /* set sign and type */ | 
| 1402 |   to->neg= 0; | 
| 1403 |   to->time_type= MYSQL_TIMESTAMP_DATETIME; | 
| 1404 |   return false; | 
| 1405 | } | 
| 1406 |  | 
| 1407 |  | 
| 1408 | /** | 
| 1409 |   Convert time to datetime. | 
| 1410 |  | 
| 1411 |   The time value is added to the current datetime value. | 
| 1412 |   @param  IN  ltime    Time value to convert from. | 
| 1413 |   @param  OUT ltime2   Datetime value to convert to. | 
| 1414 | */ | 
| 1415 | bool | 
| 1416 | time_to_datetime(THD *thd, const MYSQL_TIME *from, MYSQL_TIME *to) | 
| 1417 | { | 
| 1418 |   if (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) | 
| 1419 |     return time_to_datetime_old(thd, from, to); | 
| 1420 |   set_current_date(thd, to); | 
| 1421 |   mix_date_and_time(to, from); | 
| 1422 |   return false; | 
| 1423 | } | 
| 1424 |  | 
| 1425 |  | 
| 1426 | bool | 
| 1427 | time_to_datetime_with_warn(THD *thd, | 
| 1428 |                            const MYSQL_TIME *from, MYSQL_TIME *to, | 
| 1429 |                            ulonglong fuzzydate) | 
| 1430 | { | 
| 1431 |   int warn= 0; | 
| 1432 |   DBUG_ASSERT(from->time_type == MYSQL_TIMESTAMP_TIME); | 
| 1433 |   /* | 
| 1434 |     After time_to_datetime() we need to do check_date(), as | 
| 1435 |     the caller may want TIME_NO_ZERO_DATE or TIME_NO_ZERO_IN_DATE. | 
| 1436 |     Note, the SQL standard time->datetime conversion mode always returns | 
| 1437 |     a valid date based on CURRENT_DATE. So we need to do check_date() | 
| 1438 |     only in the old mode. | 
| 1439 |   */ | 
| 1440 |   if (time_to_datetime(thd, from, to) || | 
| 1441 |       ((thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) && | 
| 1442 |         check_date(to, fuzzydate, &warn))) | 
| 1443 |   { | 
| 1444 |     ErrConvTime str(from); | 
| 1445 |     make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, | 
| 1446 |                                  &str, MYSQL_TIMESTAMP_DATETIME, 0);  | 
| 1447 |     return true; | 
| 1448 |   } | 
| 1449 |   return false; | 
| 1450 | } | 
| 1451 |  | 
| 1452 |  | 
| 1453 | bool datetime_to_time_with_warn(THD *thd, const MYSQL_TIME *dt, | 
| 1454 |                                 MYSQL_TIME *tm, uint dec) | 
| 1455 | { | 
| 1456 |   if (thd->variables.old_behavior & OLD_MODE_ZERO_DATE_TIME_CAST) | 
| 1457 |   { | 
| 1458 |     *tm= *dt; | 
| 1459 |     datetime_to_time(tm); | 
| 1460 |     return false; | 
| 1461 |   } | 
| 1462 |   else /* new mode */ | 
| 1463 |   { | 
| 1464 |     MYSQL_TIME current_date; | 
| 1465 |     set_current_date(thd, ¤t_date); | 
| 1466 |     calc_time_diff(dt, ¤t_date, 1, tm, 0); | 
| 1467 |   } | 
| 1468 |   int warnings= 0; | 
| 1469 |   return check_time_range(tm, dec, &warnings); | 
| 1470 | } | 
| 1471 |  | 
| 1472 |  | 
| 1473 | longlong pack_time(const MYSQL_TIME *my_time) | 
| 1474 | { | 
| 1475 |   return  ((((((my_time->year     * 13ULL + | 
| 1476 |                my_time->month)    * 32ULL + | 
| 1477 |                my_time->day)      * 24ULL + | 
| 1478 |                my_time->hour)     * 60ULL + | 
| 1479 |                my_time->minute)   * 60ULL + | 
| 1480 |                my_time->second)   * 1000000ULL + | 
| 1481 |                my_time->second_part) * (my_time->neg ? -1 : 1); | 
| 1482 | } | 
| 1483 |  | 
| 1484 | #define get_one(WHERE, FACTOR) WHERE= (ulong)(packed % FACTOR); packed/= FACTOR | 
| 1485 |  | 
| 1486 | void unpack_time(longlong packed, MYSQL_TIME *my_time, | 
| 1487 |                      enum_mysql_timestamp_type ts_type) | 
| 1488 | { | 
| 1489 |   if ((my_time->neg= packed < 0)) | 
| 1490 |     packed= -packed; | 
| 1491 |   get_one(my_time->second_part, 1000000ULL); | 
| 1492 |   get_one(my_time->second,           60U); | 
| 1493 |   get_one(my_time->minute,           60U); | 
| 1494 |   get_one(my_time->hour,             24U); | 
| 1495 |   get_one(my_time->day,              32U); | 
| 1496 |   get_one(my_time->month,            13U); | 
| 1497 |   my_time->year= (uint)packed; | 
| 1498 |   my_time->time_type= ts_type; | 
| 1499 |   switch (ts_type) { | 
| 1500 |   case MYSQL_TIMESTAMP_TIME: | 
| 1501 |     my_time->hour+= (my_time->month * 32 + my_time->day) * 24; | 
| 1502 |     my_time->month= my_time->day= 0; | 
| 1503 |     break; | 
| 1504 |   case MYSQL_TIMESTAMP_DATE: | 
| 1505 |     my_time->hour= my_time->minute= my_time->second= my_time->second_part= 0; | 
| 1506 |     break; | 
| 1507 |   case MYSQL_TIMESTAMP_NONE: | 
| 1508 |   case MYSQL_TIMESTAMP_ERROR: | 
| 1509 |     DBUG_ASSERT(0); | 
| 1510 |   case MYSQL_TIMESTAMP_DATETIME: | 
| 1511 |     break; | 
| 1512 |   } | 
| 1513 | } | 
| 1514 |  |