~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>
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.
28
29
/* Some functions to calculate dates */
1 by brian
clean slate
30
31
#ifndef TESTTIME
32
33
/*
34
  Name description of interval names used in statements.
35
36
  'interval_type_to_name' is ordered and sorted on interval size and
37
  interval complexity.
38
  Order of elements in 'interval_type_to_name' should correspond to 
39
  the order of elements in 'interval_type' enum
40
  
41
  See also interval_type, interval_names
42
*/
43
44
LEX_STRING interval_type_to_name[INTERVAL_LAST] = {
45
  { C_STRING_WITH_LEN("YEAR")},
46
  { C_STRING_WITH_LEN("QUARTER")},
47
  { C_STRING_WITH_LEN("MONTH")},
48
  { C_STRING_WITH_LEN("WEEK")},
49
  { C_STRING_WITH_LEN("DAY")},
50
  { C_STRING_WITH_LEN("HOUR")},
51
  { C_STRING_WITH_LEN("MINUTE")},
52
  { C_STRING_WITH_LEN("SECOND")},
53
  { C_STRING_WITH_LEN("MICROSECOND")},
54
  { C_STRING_WITH_LEN("YEAR_MONTH")},
55
  { C_STRING_WITH_LEN("DAY_HOUR")},
56
  { C_STRING_WITH_LEN("DAY_MINUTE")},
57
  { C_STRING_WITH_LEN("DAY_SECOND")},
58
  { C_STRING_WITH_LEN("HOUR_MINUTE")},
59
  { C_STRING_WITH_LEN("HOUR_SECOND")},
60
  { C_STRING_WITH_LEN("MINUTE_SECOND")},
61
  { C_STRING_WITH_LEN("DAY_MICROSECOND")},
62
  { C_STRING_WITH_LEN("HOUR_MICROSECOND")},
63
  { C_STRING_WITH_LEN("MINUTE_MICROSECOND")},
64
  { C_STRING_WITH_LEN("SECOND_MICROSECOND")}
65
}; 
66
67
	/* Calc weekday from daynr */
68
	/* Returns 0 for monday, 1 for tuesday .... */
69
70
int calc_weekday(long daynr,bool sunday_first_day_of_week)
71
{
51.1.71 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
72
  return ((int) ((daynr + 5L + (sunday_first_day_of_week ? 1L : 0L)) % 7));
1 by brian
clean slate
73
}
74
75
/*
76
  The bits in week_format has the following meaning:
77
   WEEK_MONDAY_FIRST (0)  If not set	Sunday is first day of week
78
      		   	  If set	Monday is first day of week
79
   WEEK_YEAR (1)	  If not set	Week is in range 0-53
80
81
   	Week 0 is returned for the the last week of the previous year (for
82
	a date at start of january) In this case one can get 53 for the
83
	first week of next year.  This flag ensures that the week is
84
	relevant for the given year. Note that this flag is only
85
	releveant if WEEK_JANUARY is not set.
86
87
			  If set	 Week is in range 1-53.
88
89
	In this case one may get week 53 for a date in January (when
90
	the week is that last week of previous year) and week 1 for a
91
	date in December.
92
93
  WEEK_FIRST_WEEKDAY (2)  If not set	Weeks are numbered according
94
			   		to ISO 8601:1988
95
			  If set	The week that contains the first
96
					'first-day-of-week' is week 1.
97
	
98
	ISO 8601:1988 means that if the week containing January 1 has
99
	four or more days in the new year, then it is week 1;
100
	Otherwise it is the last week of the previous year, and the
101
	next week is week 1.
102
*/
103
482 by Brian Aker
Remove uint.
104
uint32_t calc_week(DRIZZLE_TIME *l_time, uint32_t week_behaviour, uint32_t *year)
1 by brian
clean slate
105
{
482 by Brian Aker
Remove uint.
106
  uint32_t days;
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
107
  uint32_t daynr= calc_daynr(l_time->year,l_time->month,l_time->day);
108
  uint32_t first_daynr= calc_daynr(l_time->year,1,1);
1 by brian
clean slate
109
  bool monday_first= test(week_behaviour & WEEK_MONDAY_FIRST);
110
  bool week_year= test(week_behaviour & WEEK_YEAR);
111
  bool first_weekday= test(week_behaviour & WEEK_FIRST_WEEKDAY);
112
482 by Brian Aker
Remove uint.
113
  uint32_t weekday=calc_weekday(first_daynr, !monday_first);
1 by brian
clean slate
114
  *year=l_time->year;
115
116
  if (l_time->month == 1 && l_time->day <= 7-weekday)
117
  {
118
    if ((!week_year) && ((first_weekday && weekday != 0) || (!first_weekday && weekday >= 4)))
119
      return 0;
120
    week_year= 1;
121
    (*year)--;
122
    first_daynr-= (days=calc_days_in_year(*year));
123
    weekday= (weekday + 53*7- days) % 7;
124
  }
125
126
  if ((first_weekday && weekday != 0) ||
127
      (!first_weekday && weekday >= 4))
128
    days= daynr - (first_daynr+ (7-weekday));
129
  else
130
    days= daynr - (first_daynr - weekday);
131
132
  if (week_year && days >= 52*7)
133
  {
134
    weekday= (weekday + calc_days_in_year(*year)) % 7;
135
    if ((!first_weekday && weekday < 4) || (first_weekday && weekday == 0))
136
    {
137
      (*year)++;
138
      return 1;
139
    }
140
  }
141
  return days/7+1;
142
}
143
144
	/* Change a daynr to year, month and day */
145
	/* Daynr 0 is returned as date 00.00.00 */
146
482 by Brian Aker
Remove uint.
147
void get_date_from_daynr(long daynr,uint32_t *ret_year,uint32_t *ret_month,
148
			 uint32_t *ret_day)
1 by brian
clean slate
149
{
482 by Brian Aker
Remove uint.
150
  uint32_t year,temp,leap_day,day_of_year,days_in_year;
481 by Brian Aker
Remove all of uchar.
151
  unsigned char *month_pos;
1 by brian
clean slate
152
153
  if (daynr <= 365L || daynr >= 3652500)
154
  {						/* Fix if wrong daynr */
155
    *ret_year= *ret_month = *ret_day =0;
156
  }
157
  else
158
  {
159
    year= (uint) (daynr*100 / 36525L);
160
    temp=(((year-1)/100+1)*3)/4;
161
    day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp;
162
    while (day_of_year > (days_in_year= calc_days_in_year(year)))
163
    {
164
      day_of_year-=days_in_year;
165
      (year)++;
166
    }
167
    leap_day=0;
168
    if (days_in_year == 366)
169
    {
170
      if (day_of_year > 31+28)
171
      {
172
	day_of_year--;
173
	if (day_of_year == 31+28)
174
	  leap_day=1;		/* Handle leapyears leapday */
175
      }
176
    }
177
    *ret_month=1;
178
    for (month_pos= days_in_month ;
179
	 day_of_year > (uint) *month_pos ;
180
	 day_of_year-= *(month_pos++), (*ret_month)++)
181
      ;
182
    *ret_year=year;
183
    *ret_day=day_of_year+leap_day;
184
  }
51.1.71 by Jay Pipes
Standardized TRUE/FALSE, removed/replaced DBUG symbols
185
  return;
1 by brian
clean slate
186
}
187
188
	/* Functions to handle periods */
189
520.1.6 by Brian Aker
Fixed ulong in time calculation.
190
uint32_t convert_period_to_month(uint32_t period)
1 by brian
clean slate
191
{
520.1.6 by Brian Aker
Fixed ulong in time calculation.
192
  uint32_t a,b;
1 by brian
clean slate
193
  if (period == 0)
194
    return 0L;
195
  if ((a=period/100) < YY_PART_YEAR)
196
    a+=2000;
197
  else if (a < 100)
198
    a+=1900;
199
  b=period%100;
200
  return a*12+b-1;
201
}
202
203
520.1.6 by Brian Aker
Fixed ulong in time calculation.
204
uint32_t convert_month_to_period(uint32_t month)
1 by brian
clean slate
205
{
520.1.6 by Brian Aker
Fixed ulong in time calculation.
206
  uint32_t year;
1 by brian
clean slate
207
  if (month == 0L)
208
    return 0L;
209
  if ((year=month/12) < 100)
210
  {
211
    year+=(year < YY_PART_YEAR) ? 2000 : 1900;
212
  }
213
  return year*100+month%12+1;
214
}
215
216
217
/*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
218
  Convert a timestamp string to a DRIZZLE_TIME value and produce a warning 
1 by brian
clean slate
219
  if string was truncated during conversion.
220
221
  NOTE
222
    See description of str_to_datetime() for more information.
223
*/
224
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
225
enum enum_drizzle_timestamp_type
482 by Brian Aker
Remove uint.
226
str_to_datetime_with_warn(const char *str, uint32_t length, DRIZZLE_TIME *l_time,
227
                          uint32_t flags)
1 by brian
clean slate
228
{
229
  int was_cut;
520.1.22 by Brian Aker
Second pass of thd cleanup
230
  Session *session= current_session;
398.1.1 by Monty Taylor
Remove typedef enum enum_drizzle_timestamp_type timestamp_type;
231
  enum enum_drizzle_timestamp_type ts_type;
1 by brian
clean slate
232
  
233
  ts_type= str_to_datetime(str, length, l_time,
520.1.22 by Brian Aker
Second pass of thd cleanup
234
                           (flags | (session->variables.sql_mode &
1 by brian
clean slate
235
                                     (MODE_INVALID_DATES |
236
                                      MODE_NO_ZERO_DATE))),
237
                           &was_cut);
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
238
  if (was_cut || ts_type <= DRIZZLE_TIMESTAMP_ERROR)
520.1.22 by Brian Aker
Second pass of thd cleanup
239
    make_truncated_value_warning(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
461 by Monty Taylor
Removed NullS. bu-bye.
240
                                 str, length, ts_type,  NULL);
1 by brian
clean slate
241
  return ts_type;
242
}
243
244
245
/*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
246
  Convert a datetime from broken-down DRIZZLE_TIME representation to corresponding 
1 by brian
clean slate
247
  TIMESTAMP value.
248
249
  SYNOPSIS
250
    TIME_to_timestamp()
520.1.22 by Brian Aker
Second pass of thd cleanup
251
      session             - current thread
1 by brian
clean slate
252
      t               - datetime in broken-down representation, 
253
      in_dst_time_gap - pointer to bool which is set to true if t represents
254
                        value which doesn't exists (falls into the spring 
255
                        time-gap) or to false otherwise.
256
   
257
  RETURN
258
     Number seconds in UTC since start of Unix Epoch corresponding to t.
259
     0 - t contains datetime value which is out of TIMESTAMP range.
260
     
261
*/
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.
262
my_time_t TIME_to_timestamp(Session *session, const DRIZZLE_TIME *t,
263
                            bool *in_dst_time_gap)
1 by brian
clean slate
264
{
265
  my_time_t timestamp;
266
267
  *in_dst_time_gap= 0;
520.1.22 by Brian Aker
Second pass of thd cleanup
268
  session->time_zone_used= 1;
1 by brian
clean slate
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
768
/* Daynumber from year 0 to 9999-12-31 */
769
#define MAX_DAY_NUMBER 3652424L
770
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
771
bool date_add_interval(DRIZZLE_TIME *ltime, interval_type int_type, INTERVAL interval)
1 by brian
clean slate
772
{
773
  long period, sign;
774
775
  ltime->neg= 0;
776
777
  sign= (interval.neg ? -1 : 1);
778
779
  switch (int_type) {
780
  case INTERVAL_SECOND:
781
  case INTERVAL_SECOND_MICROSECOND:
782
  case INTERVAL_MICROSECOND:
783
  case INTERVAL_MINUTE:
784
  case INTERVAL_HOUR:
785
  case INTERVAL_MINUTE_MICROSECOND:
786
  case INTERVAL_MINUTE_SECOND:
787
  case INTERVAL_HOUR_MICROSECOND:
788
  case INTERVAL_HOUR_SECOND:
789
  case INTERVAL_HOUR_MINUTE:
790
  case INTERVAL_DAY_MICROSECOND:
791
  case INTERVAL_DAY_SECOND:
792
  case INTERVAL_DAY_MINUTE:
793
  case INTERVAL_DAY_HOUR:
794
  {
152 by Brian Aker
longlong replacement
795
    int64_t sec, days, daynr, microseconds, extra_sec;
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
796
    ltime->time_type= DRIZZLE_TIMESTAMP_DATETIME; // Return full date
1 by brian
clean slate
797
    microseconds= ltime->second_part + sign*interval.second_part;
798
    extra_sec= microseconds/1000000L;
799
    microseconds= microseconds%1000000L;
800
801
    sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
802
	 ltime->second +
152 by Brian Aker
longlong replacement
803
	 sign* (int64_t) (interval.day*3600*24L +
398.1.8 by Monty Taylor
Enabled -Wlong-long.
804
                           interval.hour*3600L+interval.minute*60L+
1 by brian
clean slate
805
                           interval.second))+ extra_sec;
806
    if (microseconds < 0)
807
    {
398.1.8 by Monty Taylor
Enabled -Wlong-long.
808
      microseconds+= 1000000L;
1 by brian
clean slate
809
      sec--;
810
    }
398.1.8 by Monty Taylor
Enabled -Wlong-long.
811
    days= sec/(3600*24L);
812
    sec-= days*3600*24L;
1 by brian
clean slate
813
    if (sec < 0)
814
    {
815
      days--;
398.1.8 by Monty Taylor
Enabled -Wlong-long.
816
      sec+= 3600*24L;
1 by brian
clean slate
817
    }
818
    ltime->second_part= (uint) microseconds;
819
    ltime->second= (uint) (sec % 60);
820
    ltime->minute= (uint) (sec/60 % 60);
821
    ltime->hour=   (uint) (sec/3600);
822
    daynr= calc_daynr(ltime->year,ltime->month,1) + days;
823
    /* Day number from year 0 to 9999-12-31 */
151 by Brian Aker
Ulonglong to uint64_t
824
    if ((uint64_t) daynr > MAX_DAY_NUMBER)
1 by brian
clean slate
825
      goto invalid_date;
826
    get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
827
                        &ltime->day);
828
    break;
829
  }
830
  case INTERVAL_DAY:
831
  case INTERVAL_WEEK:
832
    period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
833
             sign * (long) interval.day);
834
    /* 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)
835
    if (period > MAX_DAY_NUMBER)
1 by brian
clean slate
836
      goto invalid_date;
837
    get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
838
    break;
839
  case INTERVAL_YEAR:
840
    ltime->year+= sign * (long) interval.year;
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
841
    if (ltime->year >= 10000L)
1 by brian
clean slate
842
      goto invalid_date;
843
    if (ltime->month == 2 && ltime->day == 29 &&
844
	calc_days_in_year(ltime->year) != 366)
845
      ltime->day=28;				// Was leap-year
846
    break;
847
  case INTERVAL_YEAR_MONTH:
848
  case INTERVAL_QUARTER:
849
  case INTERVAL_MONTH:
850
    period= (ltime->year*12 + sign * (long) interval.year*12 +
851
	     ltime->month-1 + sign * (long) interval.month);
520.1.7 by Brian Aker
More ulong fixes (including a bug on row return on error)
852
    if (period >= 120000L)
1 by brian
clean slate
853
      goto invalid_date;
854
    ltime->year= (uint) (period / 12);
855
    ltime->month= (uint) (period % 12L)+1;
856
    /* Adjust day if the new month doesn't have enough days */
857
    if (ltime->day > days_in_month[ltime->month-1])
858
    {
859
      ltime->day = days_in_month[ltime->month-1];
860
      if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
861
	ltime->day++;				// Leap-year
862
    }
863
    break;
864
  default:
865
    goto null_date;
866
  }
867
868
  return 0;					// Ok
869
870
invalid_date:
520.1.22 by Brian Aker
Second pass of thd cleanup
871
  push_warning_printf(current_session, DRIZZLE_ERROR::WARN_LEVEL_WARN,
1 by brian
clean slate
872
                      ER_DATETIME_FUNCTION_OVERFLOW,
873
                      ER(ER_DATETIME_FUNCTION_OVERFLOW),
874
                      "datetime");
875
null_date:
876
  return 1;
877
}
878
879
880
/*
881
  Calculate difference between two datetime values as seconds + microseconds.
882
883
  SYNOPSIS
884
    calc_time_diff()
885
      l_time1         - TIME/DATE/DATETIME value
886
      l_time2         - TIME/DATE/DATETIME value
887
      l_sign          - 1 absolute values are substracted,
888
                        -1 absolute values are added.
889
      seconds_out     - Out parameter where difference between
890
                        l_time1 and l_time2 in seconds is stored.
891
      microseconds_out- Out parameter where microsecond part of difference
892
                        between l_time1 and l_time2 is stored.
893
894
  NOTE
895
    This function calculates difference between l_time1 and l_time2 absolute
896
    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.
897
    signs into account (i.e. for DRIZZLE_TIME values).
1 by brian
clean slate
898
899
  RETURN VALUES
900
    Returns sign of difference.
901
    1 means negative result
902
    0 means positive result
903
904
*/
905
906
bool
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
907
calc_time_diff(DRIZZLE_TIME *l_time1, DRIZZLE_TIME *l_time2, int l_sign, int64_t *seconds_out,
1 by brian
clean slate
908
               long *microseconds_out)
909
{
910
  long days;
911
  bool neg;
152 by Brian Aker
longlong replacement
912
  int64_t microseconds;
1 by brian
clean slate
913
914
  /*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
915
    We suppose that if first argument is DRIZZLE_TIMESTAMP_TIME
1 by brian
clean slate
916
    the second argument should be TIMESTAMP_TIME also.
917
    We should check it before calc_time_diff call.
918
  */
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
919
  if (l_time1->time_type == DRIZZLE_TIMESTAMP_TIME)  // Time value
1 by brian
clean slate
920
    days= (long)l_time1->day - l_sign * (long)l_time2->day;
921
  else
922
  {
923
    days= calc_daynr((uint) l_time1->year,
924
		     (uint) l_time1->month,
925
		     (uint) l_time1->day);
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
926
    if (l_time2->time_type == DRIZZLE_TIMESTAMP_TIME)
1 by brian
clean slate
927
      days-= l_sign * (long)l_time2->day;
928
    else
929
      days-= l_sign*calc_daynr((uint) l_time2->year,
930
			       (uint) l_time2->month,
931
			       (uint) l_time2->day);
932
  }
933
398.1.8 by Monty Taylor
Enabled -Wlong-long.
934
  microseconds= ((int64_t)days*86400L +
152 by Brian Aker
longlong replacement
935
                 (int64_t)(l_time1->hour*3600L +
1 by brian
clean slate
936
                            l_time1->minute*60L +
937
                            l_time1->second) -
152 by Brian Aker
longlong replacement
938
                 l_sign*(int64_t)(l_time2->hour*3600L +
1 by brian
clean slate
939
                                   l_time2->minute*60L +
398.1.8 by Monty Taylor
Enabled -Wlong-long.
940
                                   l_time2->second)) * 1000000L +
152 by Brian Aker
longlong replacement
941
                (int64_t)l_time1->second_part -
942
                l_sign*(int64_t)l_time2->second_part;
1 by brian
clean slate
943
944
  neg= 0;
945
  if (microseconds < 0)
946
  {
947
    microseconds= -microseconds;
948
    neg= 1;
949
  }
950
  *seconds_out= microseconds/1000000L;
951
  *microseconds_out= (long) (microseconds%1000000L);
952
  return neg;
953
}
954
955
956
/*
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
957
  Compares 2 DRIZZLE_TIME structures
1 by brian
clean slate
958
959
  SYNOPSIS
960
    my_time_compare()
961
962
      a - first time
963
      b - second time
964
965
  RETURN VALUE
966
   -1   - a < b
967
    0   - a == b
968
    1   - a > b
969
970
  NOTES
971
    TIME.second_part is not considered during comparison
972
*/
973
974
int
236.1.24 by Monty Taylor
Renamed MYSQL_TIME to DRIZZLE_TIME.
975
my_time_compare(DRIZZLE_TIME *a, DRIZZLE_TIME *b)
1 by brian
clean slate
976
{
157 by Brian Aker
Second pass cleanup on removal of my_uint types
977
  uint64_t a_t= TIME_to_uint64_t_datetime(a);
978
  uint64_t b_t= TIME_to_uint64_t_datetime(b);
1 by brian
clean slate
979
980
  if (a_t > b_t)
981
    return 1;
982
  else if (a_t < b_t)
983
    return -1;
984
985
  return 0;
986
}
987
988
#endif