~drizzle-trunk/drizzle/development

520.4.14 by Monty Taylor
Removed korr.h and tztime.h from common_includes. Also removed the HAVE_DTRACE block and stuck it in autoconf.
1
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
 *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3
 *
4
 *  Copyright (C) 2008 Sun Microsystems
5
 *
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.
9
 *
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.
14
 *
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
18
 */
1 by brian
clean slate
19
20
21
/* Functions to handle date and time */
22
243.1.17 by Jay Pipes
FINAL PHASE removal of mysql_priv.h (Bye, bye my friend.)
23
#include <drizzled/server_includes.h>
549 by Monty Taylor
Took gettext.h out of header files.
24
#include <drizzled/error.h>
492.1.7 by Monty Taylor
Moved test() to its own file.
25
#include <drizzled/util/test.h>
520.4.14 by Monty Taylor
Removed korr.h and tztime.h from common_includes. Also removed the HAVE_DTRACE block and stuck it in autoconf.
26
#include <drizzled/tztime.h>
584.4.7 by Monty Taylor
Removed a big bank of includes from item.h.
27
#include <drizzled/item/timefunc.h>
584.1.15 by Monty Taylor
The mega-patch from hell. Renamed sql_class to session (since that's what it is) and removed it and field and table from common_includes.
28
#include <drizzled/session.h>
520.4.14 by Monty Taylor
Removed korr.h and tztime.h from common_includes. Also removed the HAVE_DTRACE block and stuck it in autoconf.
29
30
/* Some functions to calculate dates */
1 by brian
clean slate
31
32
#ifndef TESTTIME
33
34
/*
35
  Name description of interval names used in statements.
36
37
  'interval_type_to_name' is ordered and sorted on interval size and
38
  interval complexity.
39
  Order of elements in 'interval_type_to_name' should correspond to 
40
  the order of elements in 'interval_type' enum
41
  
42
  See also interval_type, interval_names
43
*/
44
45
LEX_STRING interval_type_to_name[INTERVAL_LAST] = {
46
  { C_STRING_WITH_LEN("YEAR")},
47
  { C_STRING_WITH_LEN("QUARTER")},
48
  { C_STRING_WITH_LEN("MONTH")},
49
  { C_STRING_WITH_LEN("WEEK")},
50
  { C_STRING_WITH_LEN("DAY")},
51
  { C_STRING_WITH_LEN("HOUR")},
52
  { C_STRING_WITH_LEN("MINUTE")},
53
  { C_STRING_WITH_LEN("SECOND")},
54
  { C_STRING_WITH_LEN("MICROSECOND")},
55
  { C_STRING_WITH_LEN("YEAR_MONTH")},
56
  { C_STRING_WITH_LEN("DAY_HOUR")},
57
  { C_STRING_WITH_LEN("DAY_MINUTE")},
58
  { C_STRING_WITH_LEN("DAY_SECOND")},
59
  { C_STRING_WITH_LEN("HOUR_MINUTE")},
60
  { C_STRING_WITH_LEN("HOUR_SECOND")},
61
  { C_STRING_WITH_LEN("MINUTE_SECOND")},
62
  { C_STRING_WITH_LEN("DAY_MICROSECOND")},
63
  { C_STRING_WITH_LEN("HOUR_MICROSECOND")},
64
  { C_STRING_WITH_LEN("MINUTE_MICROSECOND")},
65
  { C_STRING_WITH_LEN("SECOND_MICROSECOND")}
66
}; 
67
68
	/* Calc weekday from daynr */
69
	/* Returns 0 for monday, 1 for tuesday .... */
70
71
int calc_weekday(long daynr,bool sunday_first_day_of_week)
72
{
51.1.71 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
73
  return ((int) ((daynr + 5L + (sunday_first_day_of_week ? 1L : 0L)) % 7));
1 by brian
clean slate
74
}
75
76
/*
77
  The bits in week_format has the following meaning:
78
   WEEK_MONDAY_FIRST (0)  If not set	Sunday is first day of week
79
      		   	  If set	Monday is first day of week
80
   WEEK_YEAR (1)	  If not set	Week is in range 0-53
81
82
   	Week 0 is returned for the the last week of the previous year (for
83
	a date at start of january) In this case one can get 53 for the
84
	first week of next year.  This flag ensures that the week is
85
	relevant for the given year. Note that this flag is only
86
	releveant if WEEK_JANUARY is not set.
87
88
			  If set	 Week is in range 1-53.
89
90
	In this case one may get week 53 for a date in January (when
91
	the week is that last week of previous year) and week 1 for a
92
	date in December.
93
94
  WEEK_FIRST_WEEKDAY (2)  If not set	Weeks are numbered according
95
			   		to ISO 8601:1988
96
			  If set	The week that contains the first
97
					'first-day-of-week' is week 1.
98
	
99
	ISO 8601:1988 means that if the week containing January 1 has
100
	four or more days in the new year, then it is week 1;
101
	Otherwise it is the last week of the previous year, and the
102
	next week is week 1.
103
*/
104
482 by Brian Aker
Remove uint.
105
uint32_t calc_week(DRIZZLE_TIME *l_time, uint32_t week_behaviour, uint32_t *year)
1 by brian
clean slate
106
{
482 by Brian Aker
Remove uint.
107
  uint32_t days;
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
108
  uint32_t daynr= calc_daynr(l_time->year,l_time->month,l_time->day);
109
  uint32_t first_daynr= calc_daynr(l_time->year,1,1);
1 by brian
clean slate
110
  bool monday_first= test(week_behaviour & WEEK_MONDAY_FIRST);
111
  bool week_year= test(week_behaviour & WEEK_YEAR);
112
  bool first_weekday= test(week_behaviour & WEEK_FIRST_WEEKDAY);
113
482 by Brian Aker
Remove uint.
114
  uint32_t weekday=calc_weekday(first_daynr, !monday_first);
1 by brian
clean slate
115
  *year=l_time->year;
116
117
  if (l_time->month == 1 && l_time->day <= 7-weekday)
118
  {
119
    if ((!week_year) && ((first_weekday && weekday != 0) || (!first_weekday && weekday >= 4)))
120
      return 0;
121
    week_year= 1;
122
    (*year)--;
123
    first_daynr-= (days=calc_days_in_year(*year));
124
    weekday= (weekday + 53*7- days) % 7;
125
  }
126
127
  if ((first_weekday && weekday != 0) ||
128
      (!first_weekday && weekday >= 4))
129
    days= daynr - (first_daynr+ (7-weekday));
130
  else
131
    days= daynr - (first_daynr - weekday);
132
133
  if (week_year && days >= 52*7)
134
  {
135
    weekday= (weekday + calc_days_in_year(*year)) % 7;
136
    if ((!first_weekday && weekday < 4) || (first_weekday && weekday == 0))
137
    {
138
      (*year)++;
139
      return 1;
140
    }
141
  }
142
  return days/7+1;
143
}
144
145
	/* Change a daynr to year, month and day */
146
	/* Daynr 0 is returned as date 00.00.00 */
147
482 by Brian Aker
Remove uint.
148
void get_date_from_daynr(long daynr,uint32_t *ret_year,uint32_t *ret_month,
149
			 uint32_t *ret_day)
1 by brian
clean slate
150
{
482 by Brian Aker
Remove uint.
151
  uint32_t year,temp,leap_day,day_of_year,days_in_year;
481 by Brian Aker
Remove all of uchar.
152
  unsigned char *month_pos;
1 by brian
clean slate
153
154
  if (daynr <= 365L || daynr >= 3652500)
155
  {						/* Fix if wrong daynr */
156
    *ret_year= *ret_month = *ret_day =0;
157
  }
158
  else
159
  {
160
    year= (uint) (daynr*100 / 36525L);
161
    temp=(((year-1)/100+1)*3)/4;
162
    day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp;
163
    while (day_of_year > (days_in_year= calc_days_in_year(year)))
164
    {
165
      day_of_year-=days_in_year;
166
      (year)++;
167
    }
168
    leap_day=0;
169
    if (days_in_year == 366)
170
    {
171
      if (day_of_year > 31+28)
172
      {
173
	day_of_year--;
174
	if (day_of_year == 31+28)
175
	  leap_day=1;		/* Handle leapyears leapday */
176
      }
177
    }
178
    *ret_month=1;
179
    for (month_pos= days_in_month ;
180
	 day_of_year > (uint) *month_pos ;
181
	 day_of_year-= *(month_pos++), (*ret_month)++)
182
      ;
183
    *ret_year=year;
184
    *ret_day=day_of_year+leap_day;
185
  }
51.1.71 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
186
  return;
1 by brian
clean slate
187
}
188
189
	/* Functions to handle periods */
190
520.1.6 by Brian Aker
Fixed ulong in time calculation.
191
uint32_t convert_period_to_month(uint32_t period)
1 by brian
clean slate
192
{
520.1.6 by Brian Aker
Fixed ulong in time calculation.
193
  uint32_t a,b;
1 by brian
clean slate
194
  if (period == 0)
195
    return 0L;
196
  if ((a=period/100) < YY_PART_YEAR)
197
    a+=2000;
198
  else if (a < 100)
199
    a+=1900;
200
  b=period%100;
201
  return a*12+b-1;
202
}
203
204
520.1.6 by Brian Aker
Fixed ulong in time calculation.
205
uint32_t convert_month_to_period(uint32_t month)
1 by brian
clean slate
206
{
520.1.6 by Brian Aker
Fixed ulong in time calculation.
207
  uint32_t year;
1 by brian
clean slate
208
  if (month == 0L)
209
    return 0L;
210
  if ((year=month/12) < 100)
211
  {
212
    year+=(year < YY_PART_YEAR) ? 2000 : 1900;
213
  }
214
  return year*100+month%12+1;
215
}
216
217
218
/*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
219
  Convert a timestamp string to a DRIZZLE_TIME value and produce a warning 
1 by brian
clean slate
220
  if string was truncated during conversion.
221
222
  NOTE
223
    See description of str_to_datetime() for more information.
224
*/
225
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
226
enum enum_drizzle_timestamp_type
482 by Brian Aker
Remove uint.
227
str_to_datetime_with_warn(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
228
                          uint32_t flags)
1 by brian
clean slate
229
{
230
  int was_cut;
520.1.22 by Brian Aker
Second pass of thd cleanup
231
  Session *session= current_session;
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
232
  enum enum_drizzle_timestamp_type ts_type;
1 by brian
clean slate
233
  
234
  ts_type= str_to_datetime(str, length, l_time,
520.1.22 by Brian Aker
Second pass of thd cleanup
235
                           (flags | (session->variables.sql_mode &
1 by brian
clean slate
236
                                     (MODE_INVALID_DATES |
237
                                      MODE_NO_ZERO_DATE))),
238
                           &was_cut);
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
239
  if (was_cut || ts_type <= DRIZZLE_TIMESTAMP_ERROR)
520.1.22 by Brian Aker
Second pass of thd cleanup
240
    make_truncated_value_warning(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
461 by Monty Taylor
Removed NullS. bu-bye.
241
                                 str, length, ts_type,  NULL);
1 by brian
clean slate
242
  return ts_type;
243
}
244
245
246
/*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
247
  Convert a datetime from broken-down DRIZZLE_TIME representation to corresponding 
1 by brian
clean slate
248
  TIMESTAMP value.
249
250
  SYNOPSIS
251
    TIME_to_timestamp()
520.1.22 by Brian Aker
Second pass of thd cleanup
252
      session             - current thread
1 by brian
clean slate
253
      t               - datetime in broken-down representation, 
254
      in_dst_time_gap - pointer to bool which is set to true if t represents
255
                        value which doesn't exists (falls into the spring 
256
                        time-gap) or to false otherwise.
257
   
258
  RETURN
259
     Number seconds in UTC since start of Unix Epoch corresponding to t.
260
     0 - t contains datetime value which is out of TIMESTAMP range.
261
     
262
*/
520.4.14 by Monty Taylor
Removed korr.h and tztime.h from common_includes. Also removed the HAVE_DTRACE block and stuck it in autoconf.
263
my_time_t TIME_to_timestamp(Session *session, const DRIZZLE_TIME *t,
264
                            bool *in_dst_time_gap)
1 by brian
clean slate
265
{
266
  my_time_t timestamp;
267
268
  *in_dst_time_gap= 0;
269
520.1.22 by Brian Aker
Second pass of thd cleanup
270
  timestamp= session->variables.time_zone->TIME_to_gmt_sec(t, in_dst_time_gap);
1 by brian
clean slate
271
  if (timestamp)
272
  {
273
    return timestamp;
274
  }
275
276
  /* If we are here we have range error. */
277
  return(0);
278
}
279
280
281
/*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
282
  Convert a time string to a DRIZZLE_TIME struct and produce a warning
1 by brian
clean slate
283
  if string was cut during conversion.
284
285
  NOTE
286
    See str_to_time() for more info.
287
*/
288
bool
482 by Brian Aker
Remove uint.
289
str_to_time_with_warn(const char *str, uint32_t length, DRIZZLE_TIME *l_time)
1 by brian
clean slate
290
{
291
  int warning;
292
  bool ret_val= str_to_time(str, length, l_time, &warning);
293
  if (ret_val || warning)
520.1.22 by Brian Aker
Second pass of thd cleanup
294
    make_truncated_value_warning(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
461 by Monty Taylor
Removed NullS. bu-bye.
295
                                 str, length, DRIZZLE_TIMESTAMP_TIME, NULL);
1 by brian
clean slate
296
  return ret_val;
297
}
298
299
300
/*
301
  Convert a system time structure to TIME
302
*/
303
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
304
void localtime_to_TIME(DRIZZLE_TIME *to, struct tm *from)
1 by brian
clean slate
305
{
306
  to->neg=0;
307
  to->second_part=0;
308
  to->year=	(int) ((from->tm_year+1900) % 10000);
309
  to->month=	(int) from->tm_mon+1;
310
  to->day=	(int) from->tm_mday;
311
  to->hour=	(int) from->tm_hour;
312
  to->minute=	(int) from->tm_min;
313
  to->second=   (int) from->tm_sec;
314
}
315
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
316
void calc_time_from_sec(DRIZZLE_TIME *to, long seconds, long microseconds)
1 by brian
clean slate
317
{
318
  long t_seconds;
319
  // to->neg is not cleared, it may already be set to a useful value
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
320
  to->time_type= DRIZZLE_TIMESTAMP_TIME;
1 by brian
clean slate
321
  to->year= 0;
322
  to->month= 0;
323
  to->day= 0;
324
  to->hour= seconds/3600L;
325
  t_seconds= seconds%3600L;
326
  to->minute= t_seconds/60L;
327
  to->second= t_seconds%60L;
328
  to->second_part= microseconds;
329
}
330
331
332
/*
333
  Parse a format string specification
334
335
  SYNOPSIS
336
    parse_date_time_format()
337
    format_type		Format of string (time, date or datetime)
338
    format_str		String to parse
339
    format_length	Length of string
340
    date_time_format	Format to fill in
341
342
  NOTES
343
    Fills in date_time_format->positions for all date time parts.
344
345
    positions marks the position for a datetime element in the format string.
346
    The position array elements are in the following order:
347
    YYYY-DD-MM HH-MM-DD.FFFFFF AM
348
    0    1  2  3  4  5  6      7
349
350
    If positions[0]= 5, it means that year will be the forth element to
351
    read from the parsed date string.
352
353
  RETURN
354
    0	ok
355
    1	error
356
*/
357
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
358
bool parse_date_time_format(enum enum_drizzle_timestamp_type format_type, 
482 by Brian Aker
Remove uint.
359
			    const char *format, uint32_t format_length,
1 by brian
clean slate
360
			    DATE_TIME_FORMAT *date_time_format)
361
{
482 by Brian Aker
Remove uint.
362
  uint32_t offset= 0, separators= 0;
1 by brian
clean slate
363
  const char *ptr= format, *format_str;
364
  const char *end= ptr+format_length;
481 by Brian Aker
Remove all of uchar.
365
  unsigned char *dt_pos= date_time_format->positions;
1 by brian
clean slate
366
  /* need_p is set if we are using AM/PM format */
367
  bool need_p= 0, allow_separator= 0;
465 by Monty Taylor
Made a few macros into template functions.
368
  uint32_t part_map= 0, separator_map= 0;
1 by brian
clean slate
369
  const char *parts[16];
370
371
  date_time_format->time_separator= 0;
372
  date_time_format->flag= 0;			// For future
373
374
  /*
375
    Fill position with 'dummy' arguments to found out if a format tag is
376
    used twice (This limit's the format to 255 characters, but this is ok)
377
  */
378
  dt_pos[0]= dt_pos[1]= dt_pos[2]= dt_pos[3]=
379
    dt_pos[4]= dt_pos[5]= dt_pos[6]= dt_pos[7]= 255;
380
381
  for (; ptr != end; ptr++)
382
  {
383
    if (*ptr == '%' && ptr+1 != end)
384
    {
482 by Brian Aker
Remove uint.
385
      uint32_t position;
1 by brian
clean slate
386
      switch (*++ptr) {
387
      case 'y':					// Year
388
      case 'Y':
389
	position= 0;
390
	break;
391
      case 'c':					// Month
392
      case 'm':
393
	position= 1;
394
	break;
395
      case 'd':
396
      case 'e':
397
	position= 2;
398
	break;
399
      case 'h':
400
      case 'I':
401
      case 'l':
402
	need_p= 1;				// Need AM/PM
403
	/* Fall through */
404
      case 'k':
405
      case 'H':
406
	position= 3;
407
	break;
408
      case 'i':
409
	position= 4;
410
	break;
411
      case 's':
412
      case 'S':
413
	position= 5;
414
	break;
415
      case 'f':
416
	position= 6;
417
	if (dt_pos[5] != offset-1 || ptr[-2] != '.')
418
	  return 1;				// Wrong usage of %f
419
	break;
420
      case 'p':					// AM/PM
421
	if (offset == 0)			// Can't be first
422
	  return 0;
423
	position= 7;
424
	break;
425
      default:
426
	return 1;				// Unknown controll char
427
      }
428
      if (dt_pos[position] != 255)		// Don't allow same tag twice
429
	return 1;
430
      parts[position]= ptr-1;
431
432
      /*
433
	If switching from time to date, ensure that all time parts
434
	are used
435
      */
436
      if (part_map && position <= 2 && !(part_map & (1 | 2 | 4)))
437
	offset=5;
465 by Monty Taylor
Made a few macros into template functions.
438
      part_map|= UINT32_C(1) << position;
1 by brian
clean slate
439
      dt_pos[position]= offset++;
440
      allow_separator= 1;
441
    }
442
    else
443
    {
444
      /*
445
	Don't allow any characters in format as this could easily confuse
446
	the date reader
447
      */
448
      if (!allow_separator)
449
	return 1;				// No separator here
450
      allow_separator= 0;			// Don't allow two separators
451
      separators++;
452
      /* Store in separator_map which parts are punct characters */
383.1.12 by Brian Aker
Much closer toward UTF8 being around all the time...
453
      if (my_ispunct(&my_charset_utf8_general_ci, *ptr))
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
454
	separator_map|= 1 << (offset-1);
383.1.12 by Brian Aker
Much closer toward UTF8 being around all the time...
455
      else if (!my_isspace(&my_charset_utf8_general_ci, *ptr))
1 by brian
clean slate
456
	return 1;
457
    }
458
  }
459
460
  /* If no %f, specify it after seconds.  Move %p up, if necessary */
461
  if ((part_map & 32) && !(part_map & 64))
462
  {
463
    dt_pos[6]= dt_pos[5] +1;
464
    parts[6]= parts[5];				// For later test in (need_p)
465
    if (dt_pos[6] == dt_pos[7])			// Move %p one step up if used
466
      dt_pos[7]++;
467
  }
468
469
  /*
470
    Check that we have not used a non legal format specifier and that all
471
    format specifiers have been used
472
473
    The last test is to ensure that %p is used if and only if
474
    it's needed.
475
  */
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
476
  if ((format_type == DRIZZLE_TIMESTAMP_DATETIME &&
465 by Monty Taylor
Made a few macros into template functions.
477
       !test_all_bits(part_map, (uint32_t) (1 | 2 | 4 | 8 | 16 | 32))) ||
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
478
      (format_type == DRIZZLE_TIMESTAMP_DATE && part_map != (1 | 2 | 4)) ||
479
      (format_type == DRIZZLE_TIMESTAMP_TIME &&
465 by Monty Taylor
Made a few macros into template functions.
480
       !test_all_bits(part_map, (uint32_t) (8 | 16 | 32))) ||
1 by brian
clean slate
481
      !allow_separator ||			// %option should be last
482
      (need_p && dt_pos[6] +1 != dt_pos[7]) ||
483
      (need_p ^ (dt_pos[7] != 255)))
484
    return 1;
485
486
  if (dt_pos[6] != 255)				// If fractional seconds
487
  {
488
    /* remove fractional seconds from later tests */
482 by Brian Aker
Remove uint.
489
    uint32_t pos= dt_pos[6] -1;
1 by brian
clean slate
490
    /* Remove separator before %f from sep map */
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
491
    separator_map= ((separator_map & ((1 << pos)-1)) |
492
		    ((separator_map & ~((1 << pos)-1)) >> 1));
1 by brian
clean slate
493
    if (part_map & 64)			      
494
    {
495
      separators--;				// There is always a separator
496
      need_p= 1;				// force use of separators
497
    }
498
  }
499
500
  /*
501
    Remove possible separator before %p from sep_map
502
    (This can either be at position 3, 4, 6 or 7) h.m.d.%f %p
503
  */
504
  if (dt_pos[7] != 255)
505
  {
506
    if (need_p && parts[7] != parts[6]+2)
507
      separators--;
508
  }     
509
  /*
510
    Calculate if %p is in first or last part of the datetime field
511
512
    At this point we have either %H-%i-%s %p 'year parts' or
513
    'year parts' &H-%i-%s %p" as %f was removed above
514
  */
515
  offset= dt_pos[6] <= 3 ? 3 : 6;
516
  /* Remove separator before %p from sep map */
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
517
  separator_map= ((separator_map & ((1 << offset)-1)) |
518
		  ((separator_map & ~((1 << offset)-1)) >> 1));
1 by brian
clean slate
519
520
  format_str= 0;
521
  switch (format_type) {
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
522
  case DRIZZLE_TIMESTAMP_DATE:
1 by brian
clean slate
523
    format_str= known_date_time_formats[INTERNAL_FORMAT].date_format;
524
    /* fall through */
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
525
  case DRIZZLE_TIMESTAMP_TIME:
1 by brian
clean slate
526
    if (!format_str)
527
      format_str=known_date_time_formats[INTERNAL_FORMAT].time_format;
528
529
    /*
530
      If there is no separators, allow the internal format as we can read
531
      this.  If separators are used, they must be between each part
532
    */
533
    if (format_length == 6 && !need_p &&
534
	!my_strnncoll(&my_charset_bin,
481 by Brian Aker
Remove all of uchar.
535
		      (const unsigned char *) format, 6, 
536
		      (const unsigned char *) format_str, 6))
1 by brian
clean slate
537
      return 0;
538
    if (separator_map == (1 | 2))
539
    {
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
540
      if (format_type == DRIZZLE_TIMESTAMP_TIME)
1 by brian
clean slate
541
      {
542
	if (*(format+2) != *(format+5))
543
	  break;				// Error
544
	/* Store the character used for time formats */
545
	date_time_format->time_separator= *(format+2);
546
      }
547
      return 0;
548
    }
549
    break;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
550
  case DRIZZLE_TIMESTAMP_DATETIME:
1 by brian
clean slate
551
    /*
552
      If there is no separators, allow the internal format as we can read
553
      this.  If separators are used, they must be between each part.
554
      Between DATE and TIME we also allow space as separator
555
    */
556
    if ((format_length == 12 && !need_p &&
557
	 !my_strnncoll(&my_charset_bin, 
481 by Brian Aker
Remove all of uchar.
558
		       (const unsigned char *) format, 12,
559
		       (const unsigned char*) known_date_time_formats[INTERNAL_FORMAT].datetime_format,
1 by brian
clean slate
560
		       12)) ||
561
	(separators == 5 && separator_map == (1 | 2 | 8 | 16)))
562
      return 0;
563
    break;
564
  default:
51.1.71 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
565
    assert(1);
1 by brian
clean slate
566
    break;
567
  }
568
  return 1;					// Error
569
}
570
571
572
/*
573
  Create a DATE_TIME_FORMAT object from a format string specification
574
575
  SYNOPSIS
576
    date_time_format_make()
577
    format_type		Format to parse (time, date or datetime)
578
    format_str		String to parse
579
    format_length	Length of string
580
581
  NOTES
477 by Monty Taylor
Removed my_free(). It turns out that it had been def'd to ignore the flags passed to it in the second arg anyway. Gotta love that.
582
    The returned object should be freed with free()
1 by brian
clean slate
583
584
  RETURN
585
    NULL ponter:	Error
586
    new object
587
*/
588
589
DATE_TIME_FORMAT
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
590
*date_time_format_make(enum enum_drizzle_timestamp_type format_type,
482 by Brian Aker
Remove uint.
591
		       const char *format_str, uint32_t format_length)
1 by brian
clean slate
592
{
593
  DATE_TIME_FORMAT tmp;
594
595
  if (format_length && format_length < 255 &&
596
      !parse_date_time_format(format_type, format_str,
597
			      format_length, &tmp))
598
  {
599
    tmp.format.str=    (char*) format_str;
600
    tmp.format.length= format_length;
520.1.21 by Brian Aker
THD -> Session rename
601
    return date_time_format_copy((Session *)0, &tmp);
1 by brian
clean slate
602
  }
603
  return 0;
604
}
605
606
607
/*
608
  Create a copy of a DATE_TIME_FORMAT object
609
610
  SYNOPSIS
611
    date_and_time_format_copy()
520.1.22 by Brian Aker
Second pass of thd cleanup
612
    session			Set if variable should be allocated in thread mem
1 by brian
clean slate
613
    format		format to copy
614
615
  NOTES
477 by Monty Taylor
Removed my_free(). It turns out that it had been def'd to ignore the flags passed to it in the second arg anyway. Gotta love that.
616
    The returned object should be freed with free()
1 by brian
clean slate
617
618
  RETURN
619
    NULL ponter:	Error
620
    new object
621
*/
622
520.1.22 by Brian Aker
Second pass of thd cleanup
623
DATE_TIME_FORMAT *date_time_format_copy(Session *session, DATE_TIME_FORMAT *format)
1 by brian
clean slate
624
{
625
  DATE_TIME_FORMAT *new_format;
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
626
  uint32_t length= sizeof(*format) + format->format.length + 1;
1 by brian
clean slate
627
520.1.22 by Brian Aker
Second pass of thd cleanup
628
  if (session)
629
    new_format= (DATE_TIME_FORMAT *) session->alloc(length);
1 by brian
clean slate
630
  else
631
    new_format=  (DATE_TIME_FORMAT *) my_malloc(length, MYF(MY_WME));
632
  if (new_format)
633
  {
634
    /* Put format string after current pos */
635
    new_format->format.str= (char*) (new_format+1);
212.6.6 by Mats Kindahl
Removing redundant use of casts in drizzled/ for memcmp(), memcpy(), memset(), and memmove().
636
    memcpy(new_format->positions, format->positions,
1 by brian
clean slate
637
	   sizeof(format->positions));
638
    new_format->time_separator= format->time_separator;
639
    /* We make the string null terminated for easy printf in SHOW VARIABLES */
212.6.6 by Mats Kindahl
Removing redundant use of casts in drizzled/ for memcmp(), memcpy(), memset(), and memmove().
640
    memcpy(new_format->format.str, format->format.str,
1 by brian
clean slate
641
	   format->format.length);
642
    new_format->format.str[format->format.length]= 0;
643
    new_format->format.length= format->format.length;
644
  }
645
  return new_format;
646
}
647
648
649
KNOWN_DATE_TIME_FORMAT known_date_time_formats[6]=
650
{
651
  {"USA", "%m.%d.%Y", "%Y-%m-%d %H.%i.%s", "%h:%i:%s %p" },
652
  {"JIS", "%Y-%m-%d", "%Y-%m-%d %H:%i:%s", "%H:%i:%s" },
653
  {"ISO", "%Y-%m-%d", "%Y-%m-%d %H:%i:%s", "%H:%i:%s" },
654
  {"EUR", "%d.%m.%Y", "%Y-%m-%d %H.%i.%s", "%H.%i.%s" },
655
  {"INTERNAL", "%Y%m%d",   "%Y%m%d%H%i%s", "%H%i%s" },
656
  { 0, 0, 0, 0 }
657
};
658
659
660
/*
661
   Return format string according format name.
662
   If name is unknown, result is NULL
663
*/
664
665
const char *get_date_time_format_str(KNOWN_DATE_TIME_FORMAT *format,
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
666
				     enum enum_drizzle_timestamp_type type)
1 by brian
clean slate
667
{
668
  switch (type) {
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
669
  case DRIZZLE_TIMESTAMP_DATE:
1 by brian
clean slate
670
    return format->date_format;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
671
  case DRIZZLE_TIMESTAMP_DATETIME:
1 by brian
clean slate
672
    return format->datetime_format;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
673
  case DRIZZLE_TIMESTAMP_TIME:
1 by brian
clean slate
674
    return format->time_format;
675
  default:
51.1.71 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
676
    assert(0);				// Impossible
1 by brian
clean slate
677
    return 0;
678
  }
679
}
680
681
/****************************************************************************
682
  Functions to create default time/date/datetime strings
683
 
684
  NOTE:
685
    For the moment the DATE_TIME_FORMAT argument is ignored becasue
686
    MySQL doesn't support comparing of date/time/datetime strings that
687
    are not in arbutary order as dates are compared as strings in some
688
    context)
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
689
    This functions don't check that given DRIZZLE_TIME structure members are
1 by brian
clean slate
690
    in valid range. If they are not, return value won't reflect any 
691
    valid date either. Additionally, make_time doesn't take into
692
    account time->day member: it's assumed that days have been converted
693
    to hours already.
694
****************************************************************************/
695
696
void make_time(const DATE_TIME_FORMAT *format __attribute__((unused)),
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
697
               const DRIZZLE_TIME *l_time, String *str)
1 by brian
clean slate
698
{
482 by Brian Aker
Remove uint.
699
  uint32_t length= (uint) my_time_to_str(l_time, (char*) str->ptr());
1 by brian
clean slate
700
  str->length(length);
701
  str->set_charset(&my_charset_bin);
702
}
703
704
705
void make_date(const DATE_TIME_FORMAT *format __attribute__((unused)),
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
706
               const DRIZZLE_TIME *l_time, String *str)
1 by brian
clean slate
707
{
482 by Brian Aker
Remove uint.
708
  uint32_t length= (uint) my_date_to_str(l_time, (char*) str->ptr());
1 by brian
clean slate
709
  str->length(length);
710
  str->set_charset(&my_charset_bin);
711
}
712
713
714
void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)),
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
715
                   const DRIZZLE_TIME *l_time, String *str)
1 by brian
clean slate
716
{
482 by Brian Aker
Remove uint.
717
  uint32_t length= (uint) my_datetime_to_str(l_time, (char*) str->ptr());
1 by brian
clean slate
718
  str->length(length);
719
  str->set_charset(&my_charset_bin);
720
}
721
722
520.1.22 by Brian Aker
Second pass of thd cleanup
723
void make_truncated_value_warning(Session *session, DRIZZLE_ERROR::enum_warning_level level,
1 by brian
clean slate
724
                                  const char *str_val,
482 by Brian Aker
Remove uint.
725
				  uint32_t str_length,
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
726
                                  enum enum_drizzle_timestamp_type time_type,
1 by brian
clean slate
727
                                  const char *field_name)
728
{
261.4.1 by Felipe
- Renamed MYSQL_ERROR to DRIZZLE_ERROR.
729
  char warn_buff[DRIZZLE_ERRMSG_SIZE];
1 by brian
clean slate
730
  const char *type_str;
383.1.12 by Brian Aker
Much closer toward UTF8 being around all the time...
731
  CHARSET_INFO *cs= &my_charset_utf8_general_ci;
1 by brian
clean slate
732
  char buff[128];
205 by Brian Aker
uint32 -> uin32_t
733
  String str(buff,(uint32_t) sizeof(buff), system_charset_info);
1 by brian
clean slate
734
  str.copy(str_val, str_length, system_charset_info);
735
  str[str_length]= 0;               // Ensure we have end 0 for snprintf
736
737
  switch (time_type) {
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
738
    case DRIZZLE_TIMESTAMP_DATE: 
1 by brian
clean slate
739
      type_str= "date";
740
      break;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
741
    case DRIZZLE_TIMESTAMP_TIME:
1 by brian
clean slate
742
      type_str= "time";
743
      break;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
744
    case DRIZZLE_TIMESTAMP_DATETIME:  // FALLTHROUGH
1 by brian
clean slate
745
    default:
746
      type_str= "datetime";
747
      break;
748
  }
749
  if (field_name)
750
    cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
751
                       ER(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
752
                       type_str, str.c_ptr(), field_name,
520.1.22 by Brian Aker
Second pass of thd cleanup
753
                       (uint32_t) session->row_count);
1 by brian
clean slate
754
  else
755
  {
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
756
    if (time_type > DRIZZLE_TIMESTAMP_ERROR)
1 by brian
clean slate
757
      cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
758
                         ER(ER_TRUNCATED_WRONG_VALUE),
759
                         type_str, str.c_ptr());
760
    else
761
      cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
762
                         ER(ER_WRONG_VALUE), type_str, str.c_ptr());
763
  }
520.1.22 by Brian Aker
Second pass of thd cleanup
764
  push_warning(session, level,
1 by brian
clean slate
765
               ER_TRUNCATED_WRONG_VALUE, warn_buff);
766
}
767
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
768
bool date_add_interval(DRIZZLE_TIME *ltime, interval_type int_type, INTERVAL interval)
1 by brian
clean slate
769
{
770
  long period, sign;
771
772
  ltime->neg= 0;
773
774
  sign= (interval.neg ? -1 : 1);
775
776
  switch (int_type) {
777
  case INTERVAL_SECOND:
778
  case INTERVAL_SECOND_MICROSECOND:
779
  case INTERVAL_MICROSECOND:
780
  case INTERVAL_MINUTE:
781
  case INTERVAL_HOUR:
782
  case INTERVAL_MINUTE_MICROSECOND:
783
  case INTERVAL_MINUTE_SECOND:
784
  case INTERVAL_HOUR_MICROSECOND:
785
  case INTERVAL_HOUR_SECOND:
786
  case INTERVAL_HOUR_MINUTE:
787
  case INTERVAL_DAY_MICROSECOND:
788
  case INTERVAL_DAY_SECOND:
789
  case INTERVAL_DAY_MINUTE:
790
  case INTERVAL_DAY_HOUR:
791
  {
152 by Brian Aker
longlong replacement
792
    int64_t sec, days, daynr, microseconds, extra_sec;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
793
    ltime->time_type= DRIZZLE_TIMESTAMP_DATETIME; // Return full date
1 by brian
clean slate
794
    microseconds= ltime->second_part + sign*interval.second_part;
795
    extra_sec= microseconds/1000000L;
796
    microseconds= microseconds%1000000L;
797
798
    sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
799
	 ltime->second +
152 by Brian Aker
longlong replacement
800
	 sign* (int64_t) (interval.day*3600*24L +
398.1.8 by Monty Taylor
Enabled -Wlong-long.
801
                           interval.hour*3600L+interval.minute*60L+
1 by brian
clean slate
802
                           interval.second))+ extra_sec;
803
    if (microseconds < 0)
804
    {
398.1.8 by Monty Taylor
Enabled -Wlong-long.
805
      microseconds+= 1000000L;
1 by brian
clean slate
806
      sec--;
807
    }
398.1.8 by Monty Taylor
Enabled -Wlong-long.
808
    days= sec/(3600*24L);
809
    sec-= days*3600*24L;
1 by brian
clean slate
810
    if (sec < 0)
811
    {
812
      days--;
398.1.8 by Monty Taylor
Enabled -Wlong-long.
813
      sec+= 3600*24L;
1 by brian
clean slate
814
    }
815
    ltime->second_part= (uint) microseconds;
816
    ltime->second= (uint) (sec % 60);
817
    ltime->minute= (uint) (sec/60 % 60);
818
    ltime->hour=   (uint) (sec/3600);
819
    daynr= calc_daynr(ltime->year,ltime->month,1) + days;
820
    /* Day number from year 0 to 9999-12-31 */
151 by Brian Aker
Ulonglong to uint64_t
821
    if ((uint64_t) daynr > MAX_DAY_NUMBER)
1 by brian
clean slate
822
      goto invalid_date;
823
    get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
824
                        &ltime->day);
825
    break;
826
  }
827
  case INTERVAL_DAY:
828
  case INTERVAL_WEEK:
829
    period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
830
             sign * (long) interval.day);
831
    /* Daynumber from year 0 to 9999-12-31 */
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
832
    if (period > MAX_DAY_NUMBER)
1 by brian
clean slate
833
      goto invalid_date;
834
    get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
835
    break;
836
  case INTERVAL_YEAR:
837
    ltime->year+= sign * (long) interval.year;
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
838
    if (ltime->year >= 10000L)
1 by brian
clean slate
839
      goto invalid_date;
840
    if (ltime->month == 2 && ltime->day == 29 &&
841
	calc_days_in_year(ltime->year) != 366)
842
      ltime->day=28;				// Was leap-year
843
    break;
844
  case INTERVAL_YEAR_MONTH:
845
  case INTERVAL_QUARTER:
846
  case INTERVAL_MONTH:
847
    period= (ltime->year*12 + sign * (long) interval.year*12 +
848
	     ltime->month-1 + sign * (long) interval.month);
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
849
    if (period >= 120000L)
1 by brian
clean slate
850
      goto invalid_date;
851
    ltime->year= (uint) (period / 12);
852
    ltime->month= (uint) (period % 12L)+1;
853
    /* Adjust day if the new month doesn't have enough days */
854
    if (ltime->day > days_in_month[ltime->month-1])
855
    {
856
      ltime->day = days_in_month[ltime->month-1];
857
      if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
858
	ltime->day++;				// Leap-year
859
    }
860
    break;
861
  default:
862
    goto null_date;
863
  }
864
865
  return 0;					// Ok
866
867
invalid_date:
520.1.22 by Brian Aker
Second pass of thd cleanup
868
  push_warning_printf(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
1 by brian
clean slate
869
                      ER_DATETIME_FUNCTION_OVERFLOW,
870
                      ER(ER_DATETIME_FUNCTION_OVERFLOW),
871
                      "datetime");
872
null_date:
873
  return 1;
874
}
875
876
877
/*
878
  Calculate difference between two datetime values as seconds + microseconds.
879
880
  SYNOPSIS
881
    calc_time_diff()
882
      l_time1         - TIME/DATE/DATETIME value
883
      l_time2         - TIME/DATE/DATETIME value
884
      l_sign          - 1 absolute values are substracted,
885
                        -1 absolute values are added.
886
      seconds_out     - Out parameter where difference between
887
                        l_time1 and l_time2 in seconds is stored.
888
      microseconds_out- Out parameter where microsecond part of difference
889
                        between l_time1 and l_time2 is stored.
890
891
  NOTE
892
    This function calculates difference between l_time1 and l_time2 absolute
893
    values. So one should set l_sign and correct result if he want to take
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
894
    signs into account (i.e. for DRIZZLE_TIME values).
1 by brian
clean slate
895
896
  RETURN VALUES
897
    Returns sign of difference.
898
    1 means negative result
899
    0 means positive result
900
901
*/
902
903
bool
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
904
calc_time_diff(DRIZZLE_TIME *l_time1, DRIZZLE_TIME *l_time2, int l_sign, int64_t *seconds_out,
1 by brian
clean slate
905
               long *microseconds_out)
906
{
907
  long days;
908
  bool neg;
152 by Brian Aker
longlong replacement
909
  int64_t microseconds;
1 by brian
clean slate
910
911
  /*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
912
    We suppose that if first argument is DRIZZLE_TIMESTAMP_TIME
1 by brian
clean slate
913
    the second argument should be TIMESTAMP_TIME also.
914
    We should check it before calc_time_diff call.
915
  */
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
916
  if (l_time1->time_type == DRIZZLE_TIMESTAMP_TIME)  // Time value
1 by brian
clean slate
917
    days= (long)l_time1->day - l_sign * (long)l_time2->day;
918
  else
919
  {
920
    days= calc_daynr((uint) l_time1->year,
921
		     (uint) l_time1->month,
922
		     (uint) l_time1->day);
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
923
    if (l_time2->time_type == DRIZZLE_TIMESTAMP_TIME)
1 by brian
clean slate
924
      days-= l_sign * (long)l_time2->day;
925
    else
926
      days-= l_sign*calc_daynr((uint) l_time2->year,
927
			       (uint) l_time2->month,
928
			       (uint) l_time2->day);
929
  }
930
398.1.8 by Monty Taylor
Enabled -Wlong-long.
931
  microseconds= ((int64_t)days*86400L +
152 by Brian Aker
longlong replacement
932
                 (int64_t)(l_time1->hour*3600L +
1 by brian
clean slate
933
                            l_time1->minute*60L +
934
                            l_time1->second) -
152 by Brian Aker
longlong replacement
935
                 l_sign*(int64_t)(l_time2->hour*3600L +
1 by brian
clean slate
936
                                   l_time2->minute*60L +
398.1.8 by Monty Taylor
Enabled -Wlong-long.
937
                                   l_time2->second)) * 1000000L +
152 by Brian Aker
longlong replacement
938
                (int64_t)l_time1->second_part -
939
                l_sign*(int64_t)l_time2->second_part;
1 by brian
clean slate
940
941
  neg= 0;
942
  if (microseconds < 0)
943
  {
944
    microseconds= -microseconds;
945
    neg= 1;
946
  }
947
  *seconds_out= microseconds/1000000L;
948
  *microseconds_out= (long) (microseconds%1000000L);
949
  return neg;
950
}
951
952
953
/*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
954
  Compares 2 DRIZZLE_TIME structures
1 by brian
clean slate
955
956
  SYNOPSIS
957
    my_time_compare()
958
959
      a - first time
960
      b - second time
961
962
  RETURN VALUE
963
   -1   - a < b
964
    0   - a == b
965
    1   - a > b
966
967
  NOTES
968
    TIME.second_part is not considered during comparison
969
*/
970
971
int
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
972
my_time_compare(DRIZZLE_TIME *a, DRIZZLE_TIME *b)
1 by brian
clean slate
973
{
157 by Brian Aker
Second pass cleanup on removal of my_uint types
974
  uint64_t a_t= TIME_to_uint64_t_datetime(a);
975
  uint64_t b_t= TIME_to_uint64_t_datetime(b);
1 by brian
clean slate
976
977
  if (a_t > b_t)
978
    return 1;
979
  else if (a_t < b_t)
980
    return -1;
981
982
  return 0;
983
}
984
985
#endif