~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/interfaces/ecpg/pgtypeslib/datetime.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

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