Age Owner Branch data TLA Line data Source code
1 : : /* src/interfaces/ecpg/pgtypeslib/datetime.c */
2 : :
3 : : #include "postgres_fe.h"
4 : :
5 : : #include <time.h>
6 : : #include <ctype.h>
7 : : #include <limits.h>
8 : :
9 : : #include "dt.h"
10 : : #include "pgtypes_date.h"
11 : : #include "pgtypes_error.h"
12 : : #include "pgtypeslib_extern.h"
13 : :
14 : : date *
6422 meskes@postgresql.or 15 :CBC 1 : PGTYPESdate_new(void)
16 : : {
17 : : date *result;
18 : :
19 : 1 : result = (date *) pgtypes_alloc(sizeof(date));
20 : : /* result can be NULL if we run out of memory */
21 : 1 : return result;
22 : : }
23 : :
24 : : void
6402 bruce@momjian.us 25 : 1 : PGTYPESdate_free(date * d)
26 : : {
6422 meskes@postgresql.or 27 : 1 : free(d);
28 : 1 : }
29 : :
30 : : date
7523 31 : 6 : PGTYPESdate_from_timestamp(timestamp dt)
32 : : {
33 : : date dDate;
34 : :
7559 bruce@momjian.us 35 : 6 : dDate = 0; /* suppress compiler warning */
36 : :
6528 meskes@postgresql.or 37 [ + - + - ]: 6 : if (!TIMESTAMP_NOT_FINITE(dt))
38 : : {
39 : : /* Microseconds to days */
40 : 6 : dDate = (dt / USECS_PER_DAY);
41 : : }
42 : :
7686 43 : 6 : return dDate;
44 : : }
45 : :
46 : : date
7684 47 : 54 : PGTYPESdate_from_asc(char *str, char **endptr)
48 : : {
49 : : date dDate;
50 : : fsec_t fsec;
51 : : struct tm tt,
7696 52 : 54 : *tm = &tt;
53 : : int dtype;
54 : : int nf;
55 : : char *field[MAXDATEFIELDS];
56 : : int ftype[MAXDATEFIELDS];
57 : : char lowstr[MAXDATELEN + MAXDATEFIELDS];
58 : : char *realptr;
7559 bruce@momjian.us 59 [ + + ]: 54 : char **ptr = (endptr != NULL) ? endptr : &realptr;
60 : :
2433 peter_e@gmx.net 61 : 54 : bool EuroDates = false;
62 : :
7677 meskes@postgresql.or 63 : 54 : errno = 0;
3709 noah@leadboat.com 64 [ - + ]: 54 : if (strlen(str) > MAXDATELEN)
65 : : {
7686 meskes@postgresql.or 66 :UBC 0 : errno = PGTYPES_DATE_BAD_DATE;
7495 67 : 0 : return INT_MIN;
68 : : }
69 : :
5443 meskes@postgresql.or 70 [ + + + + ]:CBC 107 : if (ParseDateTime(str, lowstr, field, ftype, &nf, ptr) != 0 ||
5995 bruce@momjian.us 71 : 53 : DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, EuroDates) != 0)
72 : : {
7686 meskes@postgresql.or 73 : 5 : errno = PGTYPES_DATE_BAD_DATE;
7495 74 : 5 : return INT_MIN;
75 : : }
76 : :
7696 77 [ + - - ]: 49 : switch (dtype)
78 : : {
79 : 49 : case DTK_DATE:
80 : 49 : break;
81 : :
7696 meskes@postgresql.or 82 :UBC 0 : case DTK_EPOCH:
5548 83 [ # # ]: 0 : if (GetEpochTime(tm) < 0)
84 : : {
85 : 0 : errno = PGTYPES_DATE_BAD_DATE;
86 : 0 : return INT_MIN;
87 : : }
7696 88 : 0 : break;
89 : :
90 : 0 : default:
7686 91 : 0 : errno = PGTYPES_DATE_BAD_DATE;
7495 92 : 0 : return INT_MIN;
93 : : }
94 : :
7696 meskes@postgresql.or 95 :CBC 49 : dDate = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j(2000, 1, 1));
96 : :
97 : 49 : return dDate;
98 : : }
99 : :
100 : : char *
7523 101 : 95 : PGTYPESdate_to_asc(date dDate)
102 : : {
103 : : struct tm tt,
7559 bruce@momjian.us 104 : 95 : *tm = &tt;
105 : : char buf[MAXDATELEN + 1];
106 : 95 : int DateStyle = 1;
2433 peter_e@gmx.net 107 : 95 : bool EuroDates = false;
108 : :
6900 bruce@momjian.us 109 : 95 : j2date(dDate + date2j(2000, 1, 1), &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
7696 meskes@postgresql.or 110 : 95 : EncodeDateOnly(tm, DateStyle, buf, EuroDates);
111 : 95 : return pgtypes_strdup(buf);
112 : : }
113 : :
114 : : void
7523 115 : 1 : PGTYPESdate_julmdy(date jd, int *mdy)
116 : : {
117 : : int y,
118 : : m,
119 : : d;
120 : :
7570 121 : 1 : j2date((int) (jd + date2j(2000, 1, 1)), &y, &m, &d);
7577 122 : 1 : mdy[0] = m;
123 : 1 : mdy[1] = d;
124 : 1 : mdy[2] = y;
7696 125 : 1 : }
126 : :
127 : : void
6756 bruce@momjian.us 128 : 2 : PGTYPESdate_mdyjul(int *mdy, date * jdate)
129 : : {
130 : : /* month is mdy[0] */
131 : : /* day is mdy[1] */
132 : : /* year is mdy[2] */
133 : :
7523 meskes@postgresql.or 134 : 2 : *jdate = (date) (date2j(mdy[2], mdy[0], mdy[1]) - date2j(2000, 1, 1));
7696 135 : 2 : }
136 : :
137 : : int
7523 138 : 18 : PGTYPESdate_dayofweek(date dDate)
139 : : {
140 : : /*
141 : : * Sunday: 0 Monday: 1 Tuesday: 2 Wednesday: 3 Thursday: 4
142 : : * Friday: 5 Saturday: 6
143 : : */
7497 144 : 18 : return (int) (dDate + date2j(2000, 1, 1) + 1) % 7;
145 : : }
146 : :
147 : : void
6756 bruce@momjian.us 148 :UBC 0 : PGTYPESdate_today(date * d)
149 : : {
150 : : struct tm ts;
151 : :
7686 meskes@postgresql.or 152 : 0 : GetCurrentDateTime(&ts);
5548 153 [ # # ]: 0 : if (errno == 0)
154 : 0 : *d = date2j(ts.tm_year, ts.tm_mon, ts.tm_mday) - date2j(2000, 1, 1);
7695 155 : 0 : }
156 : :
157 : : #define PGTYPES_DATE_NUM_MAX_DIGITS 20 /* should suffice for most
158 : : * years... */
159 : :
160 : : #define PGTYPES_FMTDATE_DAY_DIGITS_LZ 1 /* LZ means "leading zeroes" */
161 : : #define PGTYPES_FMTDATE_DOW_LITERAL_SHORT 2
162 : : #define PGTYPES_FMTDATE_MONTH_DIGITS_LZ 3
163 : : #define PGTYPES_FMTDATE_MONTH_LITERAL_SHORT 4
164 : : #define PGTYPES_FMTDATE_YEAR_DIGITS_SHORT 5
165 : : #define PGTYPES_FMTDATE_YEAR_DIGITS_LONG 6
166 : :
167 : : int
4501 meskes@postgresql.or 168 :CBC 13 : PGTYPESdate_fmt_asc(date dDate, const char *fmtstring, char *outbuf)
169 : : {
170 : : static struct
171 : : {
172 : : char *format;
173 : : int component;
174 : : } mapping[] =
175 : : {
176 : : /*
177 : : * format items have to be sorted according to their length, since the
178 : : * first pattern that matches gets replaced by its value
179 : : */
180 : : {
181 : : "ddd", PGTYPES_FMTDATE_DOW_LITERAL_SHORT
182 : : },
183 : : {
184 : : "dd", PGTYPES_FMTDATE_DAY_DIGITS_LZ
185 : : },
186 : : {
187 : : "mmm", PGTYPES_FMTDATE_MONTH_LITERAL_SHORT
188 : : },
189 : : {
190 : : "mm", PGTYPES_FMTDATE_MONTH_DIGITS_LZ
191 : : },
192 : : {
193 : : "yyyy", PGTYPES_FMTDATE_YEAR_DIGITS_LONG
194 : : },
195 : : {
196 : : "yy", PGTYPES_FMTDATE_YEAR_DIGITS_SHORT
197 : : },
198 : : {
199 : : NULL, 0
200 : : }
201 : : };
202 : :
203 : : union un_fmt_comb replace_val;
204 : : int replace_type;
205 : :
206 : : int i;
207 : : int dow;
208 : : char *start_pattern;
209 : : struct tm tm;
210 : :
211 : : /* copy the string over */
7686 212 : 13 : strcpy(outbuf, fmtstring);
213 : :
214 : : /* get the date */
6900 bruce@momjian.us 215 : 13 : j2date(dDate + date2j(2000, 1, 1), &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
7686 meskes@postgresql.or 216 : 13 : dow = PGTYPESdate_dayofweek(dDate);
217 : :
7559 bruce@momjian.us 218 [ + + ]: 91 : for (i = 0; mapping[i].format != NULL; i++)
219 : : {
220 [ + + ]: 124 : while ((start_pattern = strstr(outbuf, mapping[i].format)) != NULL)
221 : : {
222 [ + + + + : 46 : switch (mapping[i].component)
+ + - ]
223 : : {
7686 meskes@postgresql.or 224 : 4 : case PGTYPES_FMTDATE_DOW_LITERAL_SHORT:
7562 225 : 4 : replace_val.str_val = pgtypes_date_weekdays_short[dow];
226 : 4 : replace_type = PGTYPES_TYPE_STRING_CONSTANT;
7686 227 : 4 : break;
228 : 14 : case PGTYPES_FMTDATE_DAY_DIGITS_LZ:
7562 229 : 14 : replace_val.uint_val = tm.tm_mday;
230 : 14 : replace_type = PGTYPES_TYPE_UINT_2_LZ;
7686 231 : 14 : break;
232 : 6 : case PGTYPES_FMTDATE_MONTH_LITERAL_SHORT:
7559 bruce@momjian.us 233 : 6 : replace_val.str_val = months[tm.tm_mon - 1];
7562 meskes@postgresql.or 234 : 6 : replace_type = PGTYPES_TYPE_STRING_CONSTANT;
7686 235 : 6 : break;
236 : 8 : case PGTYPES_FMTDATE_MONTH_DIGITS_LZ:
7562 237 : 8 : replace_val.uint_val = tm.tm_mon;
238 : 8 : replace_type = PGTYPES_TYPE_UINT_2_LZ;
7686 239 : 8 : break;
240 : 8 : case PGTYPES_FMTDATE_YEAR_DIGITS_LONG:
7562 241 : 8 : replace_val.uint_val = tm.tm_year;
242 : 8 : replace_type = PGTYPES_TYPE_UINT_4_LZ;
7686 243 : 8 : break;
244 : 6 : case PGTYPES_FMTDATE_YEAR_DIGITS_SHORT:
6452 245 : 6 : replace_val.uint_val = tm.tm_year % 100;
7562 246 : 6 : replace_type = PGTYPES_TYPE_UINT_2_LZ;
7686 247 : 6 : break;
7686 meskes@postgresql.or 248 :UBC 0 : default:
249 : :
250 : : /*
251 : : * should not happen, set something anyway
252 : : */
7562 253 : 0 : replace_val.str_val = " ";
254 : 0 : replace_type = PGTYPES_TYPE_STRING_CONSTANT;
255 : : }
7559 bruce@momjian.us 256 [ + - + + :CBC 46 : switch (replace_type)
- ]
257 : : {
7562 meskes@postgresql.or 258 : 10 : case PGTYPES_TYPE_STRING_MALLOCED:
259 : : case PGTYPES_TYPE_STRING_CONSTANT:
3368 tgl@sss.pgh.pa.us 260 : 10 : memcpy(start_pattern, replace_val.str_val,
261 : 10 : strlen(replace_val.str_val));
7559 bruce@momjian.us 262 [ - + ]: 10 : if (replace_type == PGTYPES_TYPE_STRING_MALLOCED)
7562 meskes@postgresql.or 263 :UBC 0 : free(replace_val.str_val);
7686 meskes@postgresql.or 264 :CBC 10 : break;
7562 meskes@postgresql.or 265 :UBC 0 : case PGTYPES_TYPE_UINT:
266 : : {
7559 bruce@momjian.us 267 : 0 : char *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);
268 : :
269 [ # # ]: 0 : if (!t)
7686 meskes@postgresql.or 270 : 0 : return -1;
271 : 0 : snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
272 : : "%u", replace_val.uint_val);
3368 tgl@sss.pgh.pa.us 273 : 0 : memcpy(start_pattern, t, strlen(t));
7686 meskes@postgresql.or 274 : 0 : free(t);
275 : : }
276 : 0 : break;
7562 meskes@postgresql.or 277 :CBC 28 : case PGTYPES_TYPE_UINT_2_LZ:
278 : : {
7559 bruce@momjian.us 279 : 28 : char *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);
280 : :
281 [ - + ]: 28 : if (!t)
7686 meskes@postgresql.or 282 :UBC 0 : return -1;
7686 meskes@postgresql.or 283 :CBC 28 : snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
284 : : "%02u", replace_val.uint_val);
3368 tgl@sss.pgh.pa.us 285 : 28 : memcpy(start_pattern, t, strlen(t));
7686 meskes@postgresql.or 286 : 28 : free(t);
287 : : }
288 : 28 : break;
7562 289 : 8 : case PGTYPES_TYPE_UINT_4_LZ:
290 : : {
7559 bruce@momjian.us 291 : 8 : char *t = pgtypes_alloc(PGTYPES_DATE_NUM_MAX_DIGITS);
292 : :
293 [ - + ]: 8 : if (!t)
7686 meskes@postgresql.or 294 :UBC 0 : return -1;
7686 meskes@postgresql.or 295 :CBC 8 : snprintf(t, PGTYPES_DATE_NUM_MAX_DIGITS,
296 : : "%04u", replace_val.uint_val);
3368 tgl@sss.pgh.pa.us 297 : 8 : memcpy(start_pattern, t, strlen(t));
7686 meskes@postgresql.or 298 : 8 : free(t);
299 : : }
300 : 8 : break;
7686 meskes@postgresql.or 301 :UBC 0 : default:
302 : :
303 : : /*
304 : : * doesn't happen (we set replace_type to
305 : : * PGTYPES_TYPE_STRING_CONSTANT in case of an error above)
306 : : */
307 : 0 : break;
308 : : }
309 : : }
310 : : }
7695 meskes@postgresql.or 311 :CBC 13 : return 0;
312 : : }
313 : :
314 : :
315 : : /*
316 : : * PGTYPESdate_defmt_asc
317 : : *
318 : : * function works as follows:
319 : : * - first we analyze the parameters
320 : : * - if this is a special case with no delimiters, add delimiters
321 : : * - find the tokens. First we look for numerical values. If we have found
322 : : * less than 3 tokens, we check for the months' names and thereafter for
323 : : * the abbreviations of the months' names.
324 : : * - then we see which parameter should be the date, the month and the
325 : : * year and from these values we calculate the date
326 : : */
327 : :
328 : : #define PGTYPES_DATE_MONTH_MAXLENGTH 20 /* probably even less :-) */
329 : : int
2357 peter_e@gmx.net 330 : 33 : PGTYPESdate_defmt_asc(date * d, const char *fmt, const char *str)
331 : : {
332 : : /*
333 : : * token[2] = { 4,6 } means that token 2 starts at position 4 and ends at
334 : : * (including) position 6
335 : : */
336 : : int token[3][2];
7559 bruce@momjian.us 337 : 33 : int token_values[3] = {-1, -1, -1};
338 : : char *fmt_token_order;
339 : : char *fmt_ystart,
340 : : *fmt_mstart,
341 : : *fmt_dstart;
342 : : unsigned int i;
343 : : int reading_digit;
344 : : int token_count;
345 : : char *str_copy;
346 : : struct tm tm;
347 : :
6756 348 : 33 : tm.tm_year = tm.tm_mon = tm.tm_mday = 0; /* keep compiler quiet */
349 : :
7559 350 [ + - + - : 33 : if (!d || !str || !fmt)
- + ]
351 : : {
7686 meskes@postgresql.or 352 :UBC 0 : errno = PGTYPES_DATE_ERR_EARGS;
353 : 0 : return -1;
354 : : }
355 : :
356 : : /* analyze the fmt string */
7686 meskes@postgresql.or 357 :CBC 33 : fmt_ystart = strstr(fmt, "yy");
358 : 33 : fmt_mstart = strstr(fmt, "mm");
359 : 33 : fmt_dstart = strstr(fmt, "dd");
360 : :
7559 bruce@momjian.us 361 [ + + + - : 33 : if (!fmt_ystart || !fmt_mstart || !fmt_dstart)
- + ]
362 : : {
7686 meskes@postgresql.or 363 : 1 : errno = PGTYPES_DATE_ERR_EARGS;
364 : 1 : return -1;
365 : : }
366 : :
7559 bruce@momjian.us 367 [ + + ]: 32 : if (fmt_ystart < fmt_mstart)
368 : : {
369 : : /* y m */
370 [ - + ]: 7 : if (fmt_dstart < fmt_ystart)
371 : : {
372 : : /* d y m */
7686 meskes@postgresql.or 373 :UBC 0 : fmt_token_order = "dym";
374 : : }
7559 bruce@momjian.us 375 [ + - ]:CBC 7 : else if (fmt_dstart > fmt_mstart)
376 : : {
377 : : /* y m d */
7686 meskes@postgresql.or 378 : 7 : fmt_token_order = "ymd";
379 : : }
380 : : else
381 : : {
382 : : /* y d m */
7686 meskes@postgresql.or 383 :UBC 0 : fmt_token_order = "ydm";
384 : : }
385 : : }
386 : : else
387 : : {
388 : : /* fmt_ystart > fmt_mstart */
389 : : /* m y */
7559 bruce@momjian.us 390 [ + + ]:CBC 25 : if (fmt_dstart < fmt_mstart)
391 : : {
392 : : /* d m y */
7686 meskes@postgresql.or 393 : 11 : fmt_token_order = "dmy";
394 : : }
7559 bruce@momjian.us 395 [ + + ]: 14 : else if (fmt_dstart > fmt_ystart)
396 : : {
397 : : /* m y d */
7686 meskes@postgresql.or 398 : 2 : fmt_token_order = "myd";
399 : : }
400 : : else
401 : : {
402 : : /* m d y */
403 : 12 : fmt_token_order = "mdy";
404 : : }
405 : : }
406 : :
407 : : /*
408 : : * handle the special cases where there is no delimiter between the
409 : : * digits. If we see this:
410 : : *
411 : : * only digits, 6 or 8 bytes then it might be ddmmyy and ddmmyyyy (or
412 : : * similar)
413 : : *
414 : : * we reduce it to a string with delimiters and continue processing
415 : : */
416 : :
417 : : /* check if we have only digits */
418 : 32 : reading_digit = 1;
7559 bruce@momjian.us 419 [ + + ]: 118 : for (i = 0; str[i]; i++)
420 : : {
7406 tgl@sss.pgh.pa.us 421 [ + + ]: 109 : if (!isdigit((unsigned char) str[i]))
422 : : {
7686 meskes@postgresql.or 423 : 23 : reading_digit = 0;
424 : 23 : break;
425 : : }
426 : : }
7559 bruce@momjian.us 427 [ + + ]: 32 : if (reading_digit)
428 : : {
429 : : int frag_length[3];
430 : : int target_pos;
431 : :
7686 meskes@postgresql.or 432 : 9 : i = strlen(str);
7559 bruce@momjian.us 433 [ + + + + ]: 9 : if (i != 8 && i != 6)
434 : : {
7686 meskes@postgresql.or 435 : 1 : errno = PGTYPES_DATE_ERR_ENOSHORTDATE;
436 : 1 : return -1;
437 : : }
438 : : /* okay, this really is the special case */
439 : :
440 : : /*
441 : : * as long as the string, one additional byte for the terminator and 2
442 : : * for the delimiters between the 3 fields
443 : : */
444 : 8 : str_copy = pgtypes_alloc(strlen(str) + 1 + 2);
7559 bruce@momjian.us 445 [ - + ]: 8 : if (!str_copy)
7686 meskes@postgresql.or 446 :UBC 0 : return -1;
447 : :
448 : : /* determine length of the fragments */
7559 bruce@momjian.us 449 [ + + ]:CBC 8 : if (i == 6)
450 : : {
451 : 4 : frag_length[0] = 2;
452 : 4 : frag_length[1] = 2;
453 : 4 : frag_length[2] = 2;
454 : : }
455 : : else
456 : : {
457 [ + + ]: 4 : if (fmt_token_order[0] == 'y')
458 : : {
459 : 1 : frag_length[0] = 4;
460 : 1 : frag_length[1] = 2;
461 : 1 : frag_length[2] = 2;
462 : : }
463 [ + + ]: 3 : else if (fmt_token_order[1] == 'y')
464 : : {
465 : 1 : frag_length[0] = 2;
466 : 1 : frag_length[1] = 4;
467 : 1 : frag_length[2] = 2;
468 : : }
469 : : else
470 : : {
471 : 2 : frag_length[0] = 2;
472 : 2 : frag_length[1] = 2;
473 : 2 : frag_length[2] = 4;
474 : : }
475 : : }
7686 meskes@postgresql.or 476 : 8 : target_pos = 0;
477 : :
478 : : /*
479 : : * XXX: Here we could calculate the positions of the tokens and save
480 : : * the for loop down there where we again check with isdigit() for
481 : : * digits.
482 : : */
7559 bruce@momjian.us 483 [ + + ]: 32 : for (i = 0; i < 3; i++)
484 : : {
485 : 24 : int start_pos = 0;
486 : :
487 [ + + ]: 24 : if (i >= 1)
488 : 16 : start_pos += frag_length[0];
489 [ + + ]: 24 : if (i == 2)
490 : 8 : start_pos += frag_length[1];
491 : :
7686 meskes@postgresql.or 492 : 24 : strncpy(str_copy + target_pos, str + start_pos,
7559 bruce@momjian.us 493 : 24 : frag_length[i]);
7686 meskes@postgresql.or 494 : 24 : target_pos += frag_length[i];
7559 bruce@momjian.us 495 [ + + ]: 24 : if (i != 2)
496 : : {
7686 meskes@postgresql.or 497 : 16 : str_copy[target_pos] = ' ';
498 : 16 : target_pos++;
499 : : }
500 : : }
501 : 8 : str_copy[target_pos] = '\0';
502 : : }
503 : : else
504 : : {
505 : 23 : str_copy = pgtypes_strdup(str);
7559 bruce@momjian.us 506 [ - + ]: 23 : if (!str_copy)
7686 meskes@postgresql.or 507 :UBC 0 : return -1;
508 : :
509 : : /* convert the whole string to lower case */
7559 bruce@momjian.us 510 [ + + ]:CBC 444 : for (i = 0; str_copy[i]; i++)
7282 tgl@sss.pgh.pa.us 511 : 421 : str_copy[i] = (char) pg_tolower((unsigned char) str_copy[i]);
512 : : }
513 : :
514 : : /* look for numerical tokens */
7686 meskes@postgresql.or 515 : 31 : reading_digit = 0;
7559 bruce@momjian.us 516 : 31 : token_count = 0;
517 [ + + ]: 524 : for (i = 0; i < strlen(str_copy); i++)
518 : : {
7406 tgl@sss.pgh.pa.us 519 [ + + + + ]: 493 : if (!isdigit((unsigned char) str_copy[i]) && reading_digit)
520 : : {
521 : : /* the token is finished */
7559 bruce@momjian.us 522 : 53 : token[token_count][1] = i - 1;
7686 meskes@postgresql.or 523 : 53 : reading_digit = 0;
524 : 53 : token_count++;
525 : : }
7406 tgl@sss.pgh.pa.us 526 [ + + + + ]: 440 : else if (isdigit((unsigned char) str_copy[i]) && !reading_digit)
527 : : {
528 : : /* we have found a token */
7686 meskes@postgresql.or 529 : 79 : token[token_count][0] = i;
530 : 79 : reading_digit = 1;
531 : : }
532 : : }
533 : :
534 : : /*
535 : : * we're at the end of the input string, but maybe we are still reading a
536 : : * number...
537 : : */
7559 bruce@momjian.us 538 [ + + ]: 31 : if (reading_digit)
539 : : {
540 : 26 : token[token_count][1] = i - 1;
7686 meskes@postgresql.or 541 : 26 : token_count++;
542 : : }
543 : :
544 : :
7559 bruce@momjian.us 545 [ + + ]: 31 : if (token_count < 2)
546 : : {
547 : : /*
548 : : * not all tokens found, no way to find 2 missing tokens with string
549 : : * matches
550 : : */
7686 meskes@postgresql.or 551 : 1 : free(str_copy);
6452 552 : 1 : errno = PGTYPES_DATE_ERR_ENOSHORTDATE;
7686 553 : 1 : return -1;
554 : : }
555 : :
7559 bruce@momjian.us 556 [ + + ]: 30 : if (token_count != 3)
557 : : {
558 : : /*
559 : : * not all tokens found but we may find another one with string
560 : : * matches by testing for the months names and months abbreviations
561 : : */
562 : 12 : char *month_lower_tmp = pgtypes_alloc(PGTYPES_DATE_MONTH_MAXLENGTH);
563 : : char *start_pos;
564 : : int j;
565 : : int offset;
566 : 12 : int found = 0;
567 : : char **list;
568 : :
569 [ - + ]: 12 : if (!month_lower_tmp)
570 : : {
571 : : /* free variables we alloc'ed before */
7686 meskes@postgresql.or 572 :UBC 0 : free(str_copy);
573 : 0 : return -1;
574 : : }
7686 meskes@postgresql.or 575 :CBC 12 : list = pgtypes_date_months;
7559 bruce@momjian.us 576 [ + - ]: 184 : for (i = 0; list[i]; i++)
577 : : {
578 [ + - ]: 1117 : for (j = 0; j < PGTYPES_DATE_MONTH_MAXLENGTH; j++)
579 : : {
7282 tgl@sss.pgh.pa.us 580 : 1117 : month_lower_tmp[j] = (char) pg_tolower((unsigned char) list[i][j]);
7559 bruce@momjian.us 581 [ + + ]: 1117 : if (!month_lower_tmp[j])
582 : : {
583 : : /* properly terminated */
7686 meskes@postgresql.or 584 : 184 : break;
585 : : }
586 : : }
7559 bruce@momjian.us 587 [ + + ]: 184 : if ((start_pos = strstr(str_copy, month_lower_tmp)))
588 : : {
7686 meskes@postgresql.or 589 : 12 : offset = start_pos - str_copy;
590 : :
591 : : /*
592 : : * sort the new token into the numeric tokens, shift them if
593 : : * necessary
594 : : */
7559 bruce@momjian.us 595 [ + + ]: 12 : if (offset < token[0][0])
596 : : {
7686 meskes@postgresql.or 597 : 6 : token[2][0] = token[1][0];
598 : 6 : token[2][1] = token[1][1];
599 : 6 : token[1][0] = token[0][0];
600 : 6 : token[1][1] = token[0][1];
601 : 6 : token_count = 0;
602 : : }
7559 bruce@momjian.us 603 [ + - ]: 6 : else if (offset < token[1][0])
604 : : {
7686 meskes@postgresql.or 605 : 6 : token[2][0] = token[1][0];
606 : 6 : token[2][1] = token[1][1];
607 : 6 : token_count = 1;
608 : : }
609 : : else
7559 bruce@momjian.us 610 :UBC 0 : token_count = 2;
7686 meskes@postgresql.or 611 :CBC 12 : token[token_count][0] = offset;
612 : 12 : token[token_count][1] = offset + strlen(month_lower_tmp) - 1;
613 : :
614 : : /*
615 : : * the value is the index of the month in the array of months
616 : : * + 1 (January is month 0)
617 : : */
7559 bruce@momjian.us 618 : 12 : token_values[token_count] = i + 1;
7686 meskes@postgresql.or 619 : 12 : found = 1;
620 : 12 : break;
621 : : }
622 : :
623 : : /*
624 : : * evil[tm] hack: if we read the pgtypes_date_months and haven't
625 : : * found a match, reset list to point to months (abbreviations)
626 : : * and reset the counter variable i
627 : : */
7559 bruce@momjian.us 628 [ + + ]: 172 : if (list == pgtypes_date_months)
629 : : {
630 [ + + ]: 118 : if (list[i + 1] == NULL)
631 : : {
7684 meskes@postgresql.or 632 : 6 : list = months;
7686 633 : 6 : i = -1;
634 : : }
635 : : }
636 : : }
7559 bruce@momjian.us 637 [ - + ]: 12 : if (!found)
638 : : {
7686 meskes@postgresql.or 639 :UBC 0 : free(month_lower_tmp);
640 : 0 : free(str_copy);
641 : 0 : errno = PGTYPES_DATE_ERR_ENOTDMY;
642 : 0 : return -1;
643 : : }
644 : :
645 : : /*
646 : : * here we found a month. token[token_count] and
647 : : * token_values[token_count] reflect the month's details.
648 : : *
649 : : * only the month can be specified with a literal. Here we can do a
650 : : * quick check if the month is at the right position according to the
651 : : * format string because we can check if the token that we expect to
652 : : * be the month is at the position of the only token that already has
653 : : * a value. If we wouldn't check here we could say "December 4 1990"
654 : : * with a fmt string of "dd mm yy" for 12 April 1990.
655 : : */
7559 bruce@momjian.us 656 [ - + ]:CBC 12 : if (fmt_token_order[token_count] != 'm')
657 : : {
658 : : /* deal with the error later on */
7686 meskes@postgresql.or 659 :UBC 0 : token_values[token_count] = -1;
660 : : }
7686 meskes@postgresql.or 661 :CBC 12 : free(month_lower_tmp);
662 : : }
663 : :
664 : : /* terminate the tokens with ASCII-0 and get their values */
7559 bruce@momjian.us 665 [ + + ]: 120 : for (i = 0; i < 3; i++)
666 : : {
7686 meskes@postgresql.or 667 : 90 : *(str_copy + token[i][1] + 1) = '\0';
668 : : /* A month already has a value set, check for token_value == -1 */
7559 bruce@momjian.us 669 [ + + ]: 90 : if (token_values[i] == -1)
670 : : {
7686 meskes@postgresql.or 671 : 78 : errno = 0;
672 : 78 : token_values[i] = strtol(str_copy + token[i][0], (char **) NULL, 10);
673 : : /* strtol sets errno in case of an error */
7559 bruce@momjian.us 674 [ - + ]: 78 : if (errno)
7686 meskes@postgresql.or 675 :UBC 0 : token_values[i] = -1;
676 : : }
7559 bruce@momjian.us 677 [ + + ]:CBC 90 : if (fmt_token_order[i] == 'd')
7686 meskes@postgresql.or 678 : 30 : tm.tm_mday = token_values[i];
7559 bruce@momjian.us 679 [ + + ]: 60 : else if (fmt_token_order[i] == 'm')
7686 meskes@postgresql.or 680 : 30 : tm.tm_mon = token_values[i];
7559 bruce@momjian.us 681 [ + - ]: 30 : else if (fmt_token_order[i] == 'y')
7686 meskes@postgresql.or 682 : 30 : tm.tm_year = token_values[i];
683 : : }
684 : 30 : free(str_copy);
685 : :
686 [ + - + + ]: 30 : if (tm.tm_mday < 1 || tm.tm_mday > 31)
687 : : {
688 : 1 : errno = PGTYPES_DATE_BAD_DAY;
689 : 1 : return -1;
690 : : }
691 : :
6842 bruce@momjian.us 692 [ + - + + ]: 29 : if (tm.tm_mon < 1 || tm.tm_mon > MONTHS_PER_YEAR)
693 : : {
7686 meskes@postgresql.or 694 : 1 : errno = PGTYPES_DATE_BAD_MONTH;
695 : 1 : return -1;
696 : : }
697 : :
7559 bruce@momjian.us 698 [ - + - - : 28 : if (tm.tm_mday == 31 && (tm.tm_mon == 4 || tm.tm_mon == 6 || tm.tm_mon == 9 || tm.tm_mon == 11))
- - - - -
- ]
699 : : {
7686 meskes@postgresql.or 700 :UBC 0 : errno = PGTYPES_DATE_BAD_DAY;
701 : 0 : return -1;
702 : : }
703 : :
7686 meskes@postgresql.or 704 [ + + - + ]:CBC 28 : if (tm.tm_mon == 2 && tm.tm_mday > 29)
705 : : {
7686 meskes@postgresql.or 706 :UBC 0 : errno = PGTYPES_DATE_BAD_DAY;
707 : 0 : return -1;
708 : : }
709 : :
7686 meskes@postgresql.or 710 :CBC 28 : *d = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - date2j(2000, 1, 1);
711 : :
7695 712 : 28 : return 0;
713 : : }
|