]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/datetime.c
b5d32494f76b89cc1ac499e9e96785de30a93bc1
[postgresql] / src / backend / utils / adt / datetime.c
1 /*-------------------------------------------------------------------------
2  *
3  * datetime.c
4  *        Support functions for date/time types.
5  *
6  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.70 2001/10/05 06:38:59 thomas Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <ctype.h>
18 #include <math.h>
19 #include <sys/types.h>
20 #include <errno.h>
21 #include <float.h>
22 #include <limits.h>
23
24 #include "miscadmin.h"
25 #include "utils/guc.h"
26 #include "utils/datetime.h"
27
28
29 static int DecodeNumber(int flen, char *field,
30                                                 int fmask, int *tmask,
31                                                 struct tm * tm, double *fsec, int *is2digits);
32 static int DecodeNumberField(int len, char *str,
33                                                          int fmask, int *tmask,
34                                                          struct tm * tm, double *fsec, int *is2digits);
35 static int DecodeTime(char *str, int fmask, int *tmask,
36                                           struct tm * tm, double *fsec);
37 static int      DecodeTimezone(char *str, int *tzp);
38 static datetkn *datebsearch(char *key, datetkn *base, unsigned int nel);
39 static int      DecodeDate(char *str, int fmask, int *tmask, struct tm * tm);
40 static int      DecodePosixTimezone(char *str, int *val);
41
42
43 int                     day_tab[2][13] = {
44         {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0},
45         {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}};
46
47 char       *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
48                                                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
49
50 char       *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
51                                           "Thursday", "Friday", "Saturday", NULL};
52
53
54 /*****************************************************************************
55  *       PRIVATE ROUTINES                                                                                                                *
56  *****************************************************************************/
57
58 /* definitions for squeezing values into "value" */
59 #define ABS_SIGNBIT             ((char) 0200)
60 #define VALMASK                 ((char) 0177)
61 #define NEG(n)                  ((n)|ABS_SIGNBIT)
62 #define SIGNEDCHAR(c)   ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
63 #define FROMVAL(tp)             (-SIGNEDCHAR((tp)->value) * 10) /* uncompress */
64 #define TOVAL(tp, v)    ((tp)->value = ((v) < 0? NEG((-(v))/10): (v)/10))
65
66 /*
67  * to keep this table reasonably small, we divide the lexval for TZ and DTZ
68  * entries by 10 and truncate the text field at MAXTOKLEN characters.
69  * the text field is not guaranteed to be NULL-terminated.
70  */
71 static datetkn datetktbl[] = {
72 /*      text, token, lexval */
73         {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */
74         {"acsst", DTZ, 63},                     /* Cent. Australia */
75         {"acst", TZ, 57},                       /* Cent. Australia */
76         {DA_D, ADBC, AD},                       /* "ad" for years >= 0 */
77         {"abstime", IGNORE, 0},         /* "abstime" for pre-v6.1 "Invalid
78                                                                  * Abstime" */
79         {"adt", DTZ, NEG(18)},          /* Atlantic Daylight Time */
80         {"aesst", DTZ, 66},                     /* E. Australia */
81         {"aest", TZ, 60},                       /* Australia Eastern Std Time */
82         {"ahst", TZ, NEG(60)},          /* Alaska-Hawaii Std Time */
83         {"allballs", RESERV, DTK_ZULU},         /* 00:00:00 */
84         {"am", AMPM, AM},
85         {"apr", MONTH, 4},
86         {"april", MONTH, 4},
87         {"ast", TZ, NEG(24)},           /* Atlantic Std Time (Canada) */
88         {"at", IGNORE, 0},                      /* "at" (throwaway) */
89         {"aug", MONTH, 8},
90         {"august", MONTH, 8},
91         {"awsst", DTZ, 54},                     /* W. Australia */
92         {"awst", TZ, 48},                       /* W. Australia */
93         {DB_C, ADBC, BC},                       /* "bc" for years < 0 */
94         {"bst", TZ, 6},                         /* British Summer Time */
95         {"bt", TZ, 18},                         /* Baghdad Time */
96         {"cadt", DTZ, 63},                      /* Central Australian DST */
97         {"cast", TZ, 57},                       /* Central Australian ST */
98         {"cat", TZ, NEG(60)},           /* Central Alaska Time */
99         {"cct", TZ, 48},                        /* China Coast */
100         {"cdt", DTZ, NEG(30)},          /* Central Daylight Time */
101         {"cest", DTZ, 12},                      /* Central European Dayl.Time */
102         {"cet", TZ, 6},                         /* Central European Time */
103         {"cetdst", DTZ, 12},            /* Central European Dayl.Time */
104         {"cst", TZ, NEG(36)},           /* Central Standard Time */
105         {DCURRENT, RESERV, DTK_CURRENT},        /* "current" is always now */
106         {"d", UNITS, DAY},                      /* "day of month" for ISO input */
107         {"dec", MONTH, 12},
108         {"december", MONTH, 12},
109         {"dnt", TZ, 6},                         /* Dansk Normal Tid */
110         {"dow", RESERV, DTK_DOW},       /* day of week */
111         {"doy", RESERV, DTK_DOY},       /* day of year */
112         {"dst", DTZMOD, 6},
113         {"east", TZ, 60},                       /* East Australian Std Time */
114         {"edt", DTZ, NEG(24)},          /* Eastern Daylight Time */
115         {"eet", TZ, 12},                        /* East. Europe, USSR Zone 1 */
116         {"eetdst", DTZ, 18},            /* Eastern Europe */
117         {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */
118         {"est", TZ, NEG(30)},           /* Eastern Standard Time */
119         {"feb", MONTH, 2},
120         {"february", MONTH, 2},
121         {"fri", DOW, 5},
122         {"friday", DOW, 5},
123         {"fst", TZ, 6},                         /* French Summer Time */
124         {"fwt", DTZ, 12},                       /* French Winter Time  */
125         {"gmt", TZ, 0},                         /* Greenwish Mean Time */
126         {"gst", TZ, 60},                        /* Guam Std Time, USSR Zone 9 */
127         {"h", UNITS, HOUR},                     /* "hour" */
128         {"hdt", DTZ, NEG(54)},          /* Hawaii/Alaska */
129         {"hmt", DTZ, 18},                       /* Hellas ? ? */
130         {"hst", TZ, NEG(60)},           /* Hawaii Std Time */
131         {"idle", TZ, 72},                       /* Intl. Date Line, East */
132         {"idlw", TZ, NEG(72)},          /* Intl. Date Line, West */
133         {LATE, RESERV, DTK_LATE},       /* "infinity" reserved for "late time" */
134         {INVALID, RESERV, DTK_INVALID},
135         /* "invalid" reserved for invalid time */
136         {"ist", TZ, 12},                        /* Israel */
137         {"it", TZ, 21},                         /* Iran Time */
138         {"j", UNITS, JULIAN},
139         {"jan", MONTH, 1},
140         {"january", MONTH, 1},
141         {"jd", UNITS, JULIAN},
142         {"jst", TZ, 54},                        /* Japan Std Time,USSR Zone 8 */
143         {"jt", TZ, 45},                         /* Java Time */
144         {"jul", MONTH, 7},
145         {"julian", UNITS, JULIAN},
146         {"jun", MONTH, 6},
147         {"june", MONTH, 6},
148         {"kst", TZ, 54},                        /* Korea Standard Time */
149         {"ligt", TZ, 60},                       /* From Melbourne, Australia */
150         {"m", UNITS, MONTH},            /* "month" for ISO input */
151         {"mar", MONTH, 3},
152         {"march", MONTH, 3},
153         {"may", MONTH, 5},
154         {"mdt", DTZ, NEG(36)},          /* Mountain Daylight Time */
155         {"mest", DTZ, 12},                      /* Middle Europe Summer Time */
156         {"met", TZ, 6},                         /* Middle Europe Time */
157         {"metdst", DTZ, 12},            /* Middle Europe Daylight Time */
158         {"mewt", TZ, 6},                        /* Middle Europe Winter Time */
159         {"mez", TZ, 6},                         /* Middle Europe Zone */
160         {"mm", UNITS, MINUTE},          /* "minute" for ISO input */
161         {"mon", DOW, 1},
162         {"monday", DOW, 1},
163         {"mst", TZ, NEG(42)},           /* Mountain Standard Time */
164         {"mt", TZ, 51},                         /* Moluccas Time */
165         {"ndt", DTZ, NEG(15)},          /* Nfld. Daylight Time */
166         {"nft", TZ, NEG(21)},           /* Newfoundland Standard Time */
167         {"nor", TZ, 6},                         /* Norway Standard Time */
168         {"nov", MONTH, 11},
169         {"november", MONTH, 11},
170         {NOW, RESERV, DTK_NOW},         /* current transaction time */
171         {"nst", TZ, NEG(21)},           /* Nfld. Standard Time */
172         {"nt", TZ, NEG(66)},            /* Nome Time */
173         {"nzdt", DTZ, 78},                      /* New Zealand Daylight Time */
174         {"nzst", TZ, 72},                       /* New Zealand Standard Time */
175         {"nzt", TZ, 72},                        /* New Zealand Time */
176         {"oct", MONTH, 10},
177         {"october", MONTH, 10},
178         {"on", IGNORE, 0},                      /* "on" (throwaway) */
179         {"pdt", DTZ, NEG(42)},          /* Pacific Daylight Time */
180         {"pm", AMPM, PM},
181         {"pst", TZ, NEG(48)},           /* Pacific Standard Time */
182         {"s", UNITS, SECOND},           /* "seconds" for ISO input */
183         {"sadt", DTZ, 63},                      /* S. Australian Dayl. Time */
184         {"sast", TZ, 57},                       /* South Australian Std Time */
185         {"sat", DOW, 6},
186         {"saturday", DOW, 6},
187         {"sep", MONTH, 9},
188         {"sept", MONTH, 9},
189         {"september", MONTH, 9},
190         {"set", TZ, NEG(6)},            /* Seychelles Time ?? */
191         {"sst", DTZ, 12},                       /* Swedish Summer Time */
192         {"sun", DOW, 0},
193         {"sunday", DOW, 0},
194         {"swt", TZ, 6},                         /* Swedish Winter Time  */
195         {"t", DTK_ISO_TIME, 0},         /* Filler for ISO time fields */
196         {"thu", DOW, 4},
197         {"thur", DOW, 4},
198         {"thurs", DOW, 4},
199         {"thursday", DOW, 4},
200         {TODAY, RESERV, DTK_TODAY}, /* midnight */
201         {TOMORROW, RESERV, DTK_TOMORROW},       /* tomorrow midnight */
202         {"tue", DOW, 2},
203         {"tues", DOW, 2},
204         {"tuesday", DOW, 2},
205         {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
206         {"ut", TZ, 0},
207         {"utc", TZ, 0},
208         {"wadt", DTZ, 48},                      /* West Australian DST */
209         {"wast", TZ, 42},                       /* West Australian Std Time */
210         {"wat", TZ, NEG(6)},            /* West Africa Time */
211         {"wdt", DTZ, 54},                       /* West Australian DST */
212         {"wed", DOW, 3},
213         {"wednesday", DOW, 3},
214         {"weds", DOW, 3},
215         {"wet", TZ, 0},                         /* Western Europe */
216         {"wetdst", DTZ, 6},                     /* Western Europe */
217         {"wst", TZ, 48},                        /* West Australian Std Time */
218         {"y", UNITS, YEAR},                     /* "year" for ISO input */
219         {"ydt", DTZ, NEG(48)},          /* Yukon Daylight Time */
220         {YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
221         {"yst", TZ, NEG(54)},           /* Yukon Standard Time */
222         {"z", RESERV, DTK_ZULU},        /* 00:00:00 */
223         {"zp4", TZ, NEG(24)},           /* GMT +4  hours. */
224         {"zp5", TZ, NEG(30)},           /* GMT +5  hours. */
225         {"zp6", TZ, NEG(36)},           /* GMT +6  hours. */
226         {ZULU, RESERV, DTK_ZULU},       /* 00:00:00 */
227 };
228
229 static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
230
231 /* Used for SET australian_timezones to override North American ones */
232 static datetkn australian_datetktbl[] = {
233         {"cst", TZ, 63},                        /* Australia Central Std Time */
234         {"est", TZ, 60},                        /* Australia Eastern Std Time */
235         {"sat", TZ, 57},
236 };
237
238 static unsigned int australian_szdatetktbl = sizeof australian_datetktbl /
239                                                                                          sizeof australian_datetktbl[0];
240
241 static datetkn deltatktbl[] = {
242 /*      text, token, lexval */
243         {"@", IGNORE, 0},                       /* postgres relative time prefix */
244         {DAGO, AGO, 0},                         /* "ago" indicates negative time offset */
245         {"c", UNITS, DTK_CENTURY},      /* "century" relative time units */
246         {"cent", UNITS, DTK_CENTURY},           /* "century" relative time units */
247         {"centuries", UNITS, DTK_CENTURY},      /* "centuries" relative time units */
248         {DCENTURY, UNITS, DTK_CENTURY},         /* "century" relative time units */
249         {"d", UNITS, DTK_DAY},          /* "day" relative time units */
250         {DDAY, UNITS, DTK_DAY},         /* "day" relative time units */
251         {"days", UNITS, DTK_DAY},       /* "days" relative time units */
252         {"dec", UNITS, DTK_DECADE}, /* "decade" relative time units */
253         {"decs", UNITS, DTK_DECADE},/* "decades" relative time units */
254         {DDECADE, UNITS, DTK_DECADE},           /* "decade" relative time units */
255         {"decades", UNITS, DTK_DECADE},         /* "decades" relative time units */
256         {"h", UNITS, DTK_HOUR},         /* "hour" relative time units */
257         {DHOUR, UNITS, DTK_HOUR},       /* "hour" relative time units */
258         {"hours", UNITS, DTK_HOUR}, /* "hours" relative time units */
259         {"hr", UNITS, DTK_HOUR},        /* "hour" relative time units */
260         {"hrs", UNITS, DTK_HOUR},       /* "hours" relative time units */
261         {INVALID, RESERV, DTK_INVALID},         /* reserved for invalid time */
262         {"m", UNITS, DTK_MINUTE},       /* "minute" relative time units */
263         {"microsecon", UNITS, DTK_MICROSEC},            /* "microsecond" relative
264                                                                                                  * time units */
265         {"mil", UNITS, DTK_MILLENNIUM},         /* "millennium" relative time
266                                                                                  * units */
267         {"mils", UNITS, DTK_MILLENNIUM},        /* "millennia" relative time units */
268         {"millennia", UNITS, DTK_MILLENNIUM},           /* "millennia" relative
269                                                                                                  * time units */
270         {DMILLENNIUM, UNITS, DTK_MILLENNIUM},           /* "millennium" relative
271                                                                                                  * time units */
272         {"millisecon", UNITS, DTK_MILLISEC},            /* relative time units */
273         {"min", UNITS, DTK_MINUTE}, /* "minute" relative time units */
274         {"mins", UNITS, DTK_MINUTE},/* "minutes" relative time units */
275         {"mins", UNITS, DTK_MINUTE},/* "minutes" relative time units */
276         {DMINUTE, UNITS, DTK_MINUTE},           /* "minute" relative time units */
277         {"minutes", UNITS, DTK_MINUTE},         /* "minutes" relative time units */
278         {"mon", UNITS, DTK_MONTH},      /* "months" relative time units */
279         {"mons", UNITS, DTK_MONTH}, /* "months" relative time units */
280         {DMONTH, UNITS, DTK_MONTH}, /* "month" relative time units */
281         {"months", UNITS, DTK_MONTH},
282         {"ms", UNITS, DTK_MILLISEC},
283         {"msec", UNITS, DTK_MILLISEC},
284         {DMILLISEC, UNITS, DTK_MILLISEC},
285         {"mseconds", UNITS, DTK_MILLISEC},
286         {"msecs", UNITS, DTK_MILLISEC},
287         {"qtr", UNITS, DTK_QUARTER},/* "quarter" relative time */
288         {DQUARTER, UNITS, DTK_QUARTER},         /* "quarter" relative time */
289         {"reltime", IGNORE, 0},         /* for pre-v6.1 "Undefined Reltime" */
290         {"s", UNITS, DTK_SECOND},
291         {"sec", UNITS, DTK_SECOND},
292         {DSECOND, UNITS, DTK_SECOND},
293         {"seconds", UNITS, DTK_SECOND},
294         {"secs", UNITS, DTK_SECOND},
295         {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */
296         {"tz", UNITS, DTK_TZ},          /* "timezone" time offset */
297         {"tz_hour", UNITS, DTK_TZ_HOUR},        /* timezone hour units */
298         {"tz_minute", UNITS, DTK_TZ_MINUTE},            /* timezone minutes units */
299         {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
300         {"us", UNITS, DTK_MICROSEC},/* "microsecond" relative time units */
301         {"usec", UNITS, DTK_MICROSEC},          /* "microsecond" relative time
302                                                                                  * units */
303         {DMICROSEC, UNITS, DTK_MICROSEC},       /* "microsecond" relative time
304                                                                                  * units */
305         {"useconds", UNITS, DTK_MICROSEC},      /* "microseconds" relative time
306                                                                                  * units */
307         {"usecs", UNITS, DTK_MICROSEC},         /* "microseconds" relative time
308                                                                                  * units */
309         {"w", UNITS, DTK_WEEK},         /* "week" relative time units */
310         {DWEEK, UNITS, DTK_WEEK},       /* "week" relative time units */
311         {"weeks", UNITS, DTK_WEEK}, /* "weeks" relative time units */
312         {"y", UNITS, DTK_YEAR},         /* "year" relative time units */
313         {DYEAR, UNITS, DTK_YEAR},       /* "year" relative time units */
314         {"years", UNITS, DTK_YEAR}, /* "years" relative time units */
315         {"yr", UNITS, DTK_YEAR},        /* "year" relative time units */
316         {"yrs", UNITS, DTK_YEAR},       /* "years" relative time units */
317 };
318
319 static unsigned int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];
320
321 datetkn    *datecache[MAXDATEFIELDS] = {NULL};
322
323 datetkn    *deltacache[MAXDATEFIELDS] = {NULL};
324
325
326 /*
327  * Calendar time to Julian date conversions.
328  * Julian date is commonly used in astronomical applications,
329  *      since it is numerically accurate and computationally simple.
330  * The algorithms here will accurately convert between Julian day
331  *      and calendar date for all non-negative Julian days
332  *      (i.e. from Nov 23, -4713 on).
333  *
334  * Ref: Explanatory Supplement to the Astronomical Almanac, 1992.
335  *      University Science Books, 20 Edgehill Rd. Mill Valley CA 94941.
336  *
337  * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague
338  *      now at Aerospace Corp. (hi, Henry!)
339  *
340  * These routines will be used by other date/time packages
341  * - thomas 97/02/25
342  */
343
344 int
345 date2j(int y, int m, int d)
346 {
347         int                     m12 = (m - 14) / 12;
348
349         return ((1461 * (y + 4800 + m12)) / 4 + (367 * (m - 2 - 12 * (m12))) / 12
350                         - (3 * ((y + 4900 + m12) / 100)) / 4 + d - 32075);
351 }       /* date2j() */
352
353 void
354 j2date(int jd, int *year, int *month, int *day)
355 {
356         int                     j,
357                                 y,
358                                 m,
359                                 d;
360
361         int                     i,
362                                 l,
363                                 n;
364
365         l = jd + 68569;
366         n = (4 * l) / 146097;
367         l -= (146097 * n + 3) / 4;
368         i = (4000 * (l + 1)) / 1461001;
369         l += 31 - (1461 * i) / 4;
370         j = (80 * l) / 2447;
371         d = l - (2447 * j) / 80;
372         l = j / 11;
373         m = (j + 2) - (12 * l);
374         y = 100 * (n - 49) + i + l;
375
376         *year = y;
377         *month = m;
378         *day = d;
379         return;
380 }       /* j2date() */
381
382 int
383 j2day(int date)
384 {
385         int                     day;
386
387         day = (date + 1) % 7;
388
389         return day;
390 }       /* j2day() */
391
392
393 /*
394  * parse and convert date in timestr (the normal interface)
395  *
396  * Returns the number of seconds since epoch (J2000)
397  */
398
399 /* ParseDateTime()
400  * Break string into tokens based on a date/time context.
401  */
402 int
403 ParseDateTime(char *timestr, char *lowstr,
404                           char **field, int *ftype, int maxfields, int *numfields)
405 {
406         int                     nf = 0;
407         char       *cp = timestr;
408         char       *lp = lowstr;
409
410         /* outer loop through fields */
411         while (*cp != '\0')
412         {
413                 field[nf] = lp;
414
415                 /* leading digit? then date or time */
416                 if (isdigit((unsigned char) *cp) || (*cp == '.'))
417                 {
418                         *lp++ = *cp++;
419                         while (isdigit((unsigned char) *cp))
420                                 *lp++ = *cp++;
421                         /* time field? */
422                         if (*cp == ':')
423                         {
424                                 ftype[nf] = DTK_TIME;
425                                 *lp++ = *cp++;
426                                 while (isdigit((unsigned char) *cp) ||
427                                            (*cp == ':') || (*cp == '.'))
428                                         *lp++ = *cp++;
429
430                         }
431                         /* date field? allow embedded text month */
432                         else if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
433                         {
434                                 ftype[nf] = DTK_DATE;
435                                 *lp++ = *cp++;
436                                 /* second field is all digits? then no embedded text month */
437                                 if (isdigit((unsigned char) *cp))
438                                 {
439                                         while (isdigit((unsigned char) *cp) || (*cp == '-') ||
440                                                    (*cp == '/') || (*cp == '.'))
441                                                 *lp++ = *cp++;
442                                 }
443                                 else
444                                 {
445                                         while (isalnum((unsigned char) *cp) || (*cp == '-') ||
446                                                    (*cp == '/') || (*cp == '.'))
447                                                 *lp++ = tolower((unsigned char) *cp++);
448                                 }
449                         }
450
451                         /*
452                          * otherwise, number only and will determine year, month, or
453                          * day later
454                          */
455                         else
456                                 ftype[nf] = DTK_NUMBER;
457
458                 }
459
460                 /*
461                  * text? then date string, month, day of week, special, or
462                  * timezone
463                  */
464                 else if (isalpha((unsigned char) *cp))
465                 {
466                         ftype[nf] = DTK_STRING;
467                         *lp++ = tolower((unsigned char) *cp++);
468                         while (isalpha((unsigned char) *cp))
469                                 *lp++ = tolower((unsigned char) *cp++);
470
471                         /*
472                          * Full date string with leading text month? Could also be a
473                          * POSIX time zone...
474                          */
475                         if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
476                         {
477                                 ftype[nf] = DTK_DATE;
478                                 while (isdigit((unsigned char) *cp) ||
479                                            (*cp == '-') || (*cp == '/') || (*cp == '.'))
480                                         *lp++ = tolower((unsigned char) *cp++);
481                         }
482
483                         /* skip leading spaces */
484                 }
485                 else if (isspace((unsigned char) *cp))
486                 {
487                         cp++;
488                         continue;
489
490                         /* sign? then special or numeric timezone */
491                 }
492                 else if ((*cp == '+') || (*cp == '-'))
493                 {
494                         *lp++ = *cp++;
495                         /* soak up leading whitespace */
496                         while (isspace((unsigned char) *cp))
497                                 cp++;
498                         /* numeric timezone? */
499                         if (isdigit((unsigned char) *cp))
500                         {
501                                 ftype[nf] = DTK_TZ;
502                                 *lp++ = *cp++;
503                                 while (isdigit((unsigned char) *cp) ||
504                                            (*cp == ':') || (*cp == '.'))
505                                         *lp++ = *cp++;
506
507                                 /* special? */
508                         }
509                         else if (isalpha((unsigned char) *cp))
510                         {
511                                 ftype[nf] = DTK_SPECIAL;
512                                 *lp++ = tolower((unsigned char) *cp++);
513                                 while (isalpha((unsigned char) *cp))
514                                         *lp++ = tolower((unsigned char) *cp++);
515
516                                 /* otherwise something wrong... */
517                         }
518                         else
519                                 return -1;
520
521                         /* ignore punctuation but use as delimiter */
522                 }
523                 else if (ispunct((unsigned char) *cp))
524                 {
525                         cp++;
526                         continue;
527
528                 }
529                 else
530                         return -1;
531
532                 /* force in a delimiter */
533                 *lp++ = '\0';
534                 nf++;
535                 if (nf > MAXDATEFIELDS)
536                         return -1;
537         }
538
539         *numfields = nf;
540
541         return 0;
542 }       /* ParseDateTime() */
543
544
545 /* DecodeDateTime()
546  * Interpret previously parsed fields for general date and time.
547  * Return 0 if full date, 1 if only time, and -1 if problems.
548  *              External format(s):
549  *                              "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
550  *                              "Fri Feb-7-1997 15:23:27"
551  *                              "Feb-7-1997 15:23:27"
552  *                              "2-7-1997 15:23:27"
553  *                              "1997-2-7 15:23:27"
554  *                              "1997.038 15:23:27"             (day of year 1-366)
555  *              Also supports input in compact time:
556  *                              "970207 152327"
557  *                              "97038 152327"
558  *
559  * Use the system-provided functions to get the current time zone
560  *      if not specified in the input string.
561  * If the date is outside the time_t system-supported time range,
562  *      then assume GMT time zone. - thomas 1997/05/27
563  */
564 int
565 DecodeDateTime(char **field, int *ftype, int nf,
566                            int *dtype, struct tm * tm, double *fsec, int *tzp)
567 {
568         int                     fmask = 0,
569                                 tmask,
570                                 type;
571         int                     ptype = 0;              /* "prefix type" for ISO y2001m02d04 format */
572         int                     i;
573         int                     flen,
574                                 val;
575         int                     mer = HR24;
576         int                     haveTextMonth = FALSE;
577         int                     is2digits = FALSE;
578         int                     bc = FALSE;
579
580         /* We'll insist on at least all of the date fields,
581          * but initialize the remaining fields in case they are not
582          * set later...
583          */
584         *dtype = DTK_DATE;
585         tm->tm_hour = 0;
586         tm->tm_min = 0;
587         tm->tm_sec = 0;
588         *fsec = 0;
589         tm->tm_isdst = -1;      /* don't know daylight savings time status apriori */
590         if (tzp != NULL)
591                 *tzp = 0;
592
593         for (i = 0; i < nf; i++)
594         {
595                 switch (ftype[i])
596                 {
597                         case DTK_DATE:
598                                 /* Previous field was a label for "julian date"?
599                                  * then this should be a julian date with fractional day...
600                                  */
601                                 if (ptype == JULIAN)
602                                 {
603                                         char *cp;
604                                         double dt, date, time;
605
606                                         dt = strtod(field[i], &cp);
607                                         if (*cp != '\0')
608                                                 return -1;
609
610                                         time = dt * 86400;
611                                         TMODULO(time, date, 86400e0);
612                                         j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
613                                         dt2time(time, &tm->tm_hour, &tm->tm_min, fsec);
614
615                                         tmask = DTK_DATE_M | DTK_TIME_M;
616                                         *dtype = DTK_DATE;
617                                 }
618
619                                 /* Already have a date? Then this might be a POSIX time
620                                  * zone with an embedded dash (e.g. "PST-3" == "EST")
621                                  * - thomas 2000-03-15
622                                  */
623                                 else if ((fmask & DTK_DATE_M) == DTK_DATE_M)
624                                 {
625                                         if ((tzp == NULL)
626                                                 || (DecodePosixTimezone(field[i], tzp) != 0))
627                                                 return -1;
628
629                                         ftype[i] = DTK_TZ;
630                                         tmask = DTK_M(TZ);
631                                 }
632                                 else if (DecodeDate(field[i], fmask, &tmask, tm) != 0)
633                                 {
634                                         return -1;
635                                 }
636                                 break;
637
638                         case DTK_TIME:
639                                 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
640                                         return -1;
641
642                                 /* Check upper limit on hours; other limits checked in
643                                  * DecodeTime()
644                                  */
645                                 if (tm->tm_hour > 23)
646                                         return -1;
647                                 break;
648
649                         case DTK_TZ:
650                                 if (tzp == NULL)
651                                         return -1;
652
653                                 {
654                                         int                     tz;
655
656                                         if (DecodeTimezone(field[i], &tz) != 0)
657                                                 return -1;
658
659                                         /*
660                                          * Already have a time zone? Then maybe this is the
661                                          * second field of a POSIX time: EST+3 (equivalent to
662                                          * PST)
663                                          */
664                                         if ((i > 0) && ((fmask & DTK_M(TZ)) != 0)
665                                                 && (ftype[i - 1] == DTK_TZ)
666                                                 && (isalpha((unsigned char) *field[i - 1])))
667                                         {
668                                                 *tzp -= tz;
669                                                 tmask = 0;
670                                         }
671                                         else
672                                         {
673                                                 *tzp = tz;
674                                                 tmask = DTK_M(TZ);
675                                         }
676                                 }
677                                 break;
678
679                         case DTK_NUMBER:
680                                 flen = strlen(field[i]);
681
682                                 /* Was this an "ISO date" with embedded field labels?
683                                  * An example is "y2001m02d04" - thomas 2001-02-04
684                                  */
685                                 if (ptype != 0)
686                                 {
687                                         char *cp;
688                                         int val;
689
690                                         val = strtol(field[i], &cp, 10);
691                                         if (*cp != '\0')
692                                                 return -1;
693
694                                         switch (ptype) {
695                                                 case YEAR:
696                                                         tm->tm_year = val;
697                                                         tmask = DTK_M(ptype);
698                                                         break;
699
700                                                 case MONTH:
701                                                         tm->tm_mon = val;
702                                                         tmask = DTK_M(ptype);
703                                                         break;
704
705                                                 case DAY:
706                                                         tm->tm_mday = val;
707                                                         tmask = DTK_M(ptype);
708                                                         break;
709
710                                                 case HOUR:
711                                                         tm->tm_hour = val;
712                                                         tmask = DTK_M(ptype);
713                                                         break;
714
715                                                 case MINUTE:
716                                                         tm->tm_min = val;
717                                                         tmask = DTK_M(ptype);
718                                                         break;
719
720                                                 case SECOND:
721                                                         tm->tm_sec = val;
722                                                         tmask = DTK_M(ptype);
723                                                         break;
724
725                                                 case JULIAN:
726                                                         /* previous field was a label for "julian date"?
727                                                          * then this is a julian day with no fractional part
728                                                          * (see DTK_DATE for cases involving fractional parts)
729                                                          */
730                                                         j2date(val, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
731
732                                                         tmask = DTK_DATE_M;
733                                                         break;
734
735                                                 default:
736                                                         return -1;
737                                                         break;
738                                         }
739
740                                         ptype = 0;
741                                         *dtype = DTK_DATE;
742                                 }
743                                 /*
744                                  * long numeric string and either no date or no time read
745                                  * yet? then interpret as a concatenated date or time...
746                                  */
747                                 else if ((flen > 4) && !((fmask & DTK_DATE_M) && (fmask & DTK_TIME_M)))
748                                 {
749                                         if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0)
750                                                 return -1;
751
752                                 }
753                                 /* otherwise it is a single date/time field... */
754                                 else if (DecodeNumber(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0)
755                                 {
756                                         return -1;
757                                 }
758                                 break;
759
760                         case DTK_STRING:
761                         case DTK_SPECIAL:
762                                 type = DecodeSpecial(i, field[i], &val);
763                                 if (type == IGNORE)
764                                         continue;
765
766                                 tmask = DTK_M(type);
767                                 switch (type)
768                                 {
769                                         case RESERV:
770                                                 switch (val)
771                                                 {
772                                                         case DTK_CURRENT:
773                                                         case DTK_NOW:
774                                                                 tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
775                                                                 *dtype = DTK_DATE;
776                                                                 GetCurrentTimeUsec(tm, fsec);
777                                                                 if (tzp != NULL)
778                                                                         *tzp = CTimeZone;
779                                                                 break;
780
781                                                         case DTK_YESTERDAY:
782                                                                 tmask = DTK_DATE_M;
783                                                                 *dtype = DTK_DATE;
784                                                                 GetCurrentTime(tm);
785                                                                 j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - 1),
786                                                                 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
787                                                                 tm->tm_hour = 0;
788                                                                 tm->tm_min = 0;
789                                                                 tm->tm_sec = 0;
790                                                                 break;
791
792                                                         case DTK_TODAY:
793                                                                 tmask = DTK_DATE_M;
794                                                                 *dtype = DTK_DATE;
795                                                                 GetCurrentTime(tm);
796                                                                 tm->tm_hour = 0;
797                                                                 tm->tm_min = 0;
798                                                                 tm->tm_sec = 0;
799                                                                 break;
800
801                                                         case DTK_TOMORROW:
802                                                                 tmask = DTK_DATE_M;
803                                                                 *dtype = DTK_DATE;
804                                                                 GetCurrentTime(tm);
805                                                                 j2date((date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 1),
806                                                                 &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
807                                                                 tm->tm_hour = 0;
808                                                                 tm->tm_min = 0;
809                                                                 tm->tm_sec = 0;
810                                                                 break;
811
812                                                         case DTK_ZULU:
813                                                                 tmask = (DTK_TIME_M | DTK_M(TZ));
814                                                                 *dtype = DTK_DATE;
815                                                                 tm->tm_hour = 0;
816                                                                 tm->tm_min = 0;
817                                                                 tm->tm_sec = 0;
818                                                                 if (tzp != NULL)
819                                                                         *tzp = 0;
820                                                                 break;
821
822                                                         default:
823                                                                 *dtype = val;
824                                                 }
825
826                                                 break;
827
828                                         case MONTH:
829
830                                                 /*
831                                                  * already have a (numeric) month? then see if we
832                                                  * can substitute...
833                                                  */
834                                                 if ((fmask & DTK_M(MONTH)) && (!haveTextMonth)
835                                                         && (!(fmask & DTK_M(DAY)))
836                                                         && ((tm->tm_mon >= 1) && (tm->tm_mon <= 31)))
837                                                 {
838                                                         tm->tm_mday = tm->tm_mon;
839                                                         tmask = DTK_M(DAY);
840                                                 }
841                                                 haveTextMonth = TRUE;
842                                                 tm->tm_mon = val;
843                                                 break;
844
845                                         case DTZMOD:
846
847                                                 /*
848                                                  * daylight savings time modifier (solves "MET
849                                                  * DST" syntax)
850                                                  */
851                                                 tmask |= DTK_M(DTZ);
852                                                 tm->tm_isdst = 1;
853                                                 if (tzp == NULL)
854                                                         return -1;
855                                                 *tzp += val * 60;
856                                                 break;
857
858                                         case DTZ:
859
860                                                 /*
861                                                  * set mask for TZ here _or_ check for DTZ later
862                                                  * when getting default timezone
863                                                  */
864                                                 tmask |= DTK_M(TZ);
865                                                 tm->tm_isdst = 1;
866                                                 if (tzp == NULL)
867                                                         return -1;
868                                                 *tzp = val * 60;
869                                                 ftype[i] = DTK_TZ;
870                                                 break;
871
872                                         case TZ:
873                                                 tm->tm_isdst = 0;
874                                                 if (tzp == NULL)
875                                                         return -1;
876                                                 *tzp = val * 60;
877                                                 ftype[i] = DTK_TZ;
878                                                 break;
879
880                                         case IGNORE:
881                                                 break;
882
883                                         case AMPM:
884                                                 mer = val;
885                                                 break;
886
887                                         case ADBC:
888                                                 bc = (val == BC);
889                                                 break;
890
891                                         case DOW:
892                                                 tm->tm_wday = val;
893                                                 break;
894
895                                         case UNITS:
896                                                 ptype = val;
897                                                 tmask = 0;
898                                                 break;
899
900                                         case DTK_ISO_TIME:
901                                                 if ((i < 1) || (i >= (nf-1))
902                                                         || (ftype[i-1] != DTK_DATE)
903                                                         || (ftype[i+1] != DTK_TIME))
904                                                         return -1;
905                                                 break;
906
907                                         default:
908                                                 return -1;
909                                 }
910                                 break;
911
912                         default:
913                                 return -1;
914                 }
915
916                 if (tmask & fmask)
917                         return -1;
918                 fmask |= tmask;
919         }
920
921         /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
922         if (bc)
923         {
924                 if (tm->tm_year > 0)
925                         tm->tm_year = -(tm->tm_year - 1);
926                 else
927                         elog(ERROR, "Inconsistant use of year %04d and 'BC'", tm->tm_year);
928         }
929         else if (is2digits)
930         {
931                 if (tm->tm_year < 70)
932                         tm->tm_year += 2000;
933                 else if (tm->tm_year < 100)
934                         tm->tm_year += 1900;
935         }
936
937         if ((mer != HR24) && (tm->tm_hour > 12))
938                 return -1;
939         if ((mer == AM) && (tm->tm_hour == 12))
940                 tm->tm_hour = 0;
941         else if ((mer == PM) && (tm->tm_hour != 12))
942                 tm->tm_hour += 12;
943
944         /* do additional checking for full date specs... */
945         if (*dtype == DTK_DATE)
946         {
947                 if ((fmask & DTK_DATE_M) != DTK_DATE_M)
948                         return ((fmask & DTK_TIME_M) == DTK_TIME_M) ? 1 : -1;
949
950                 /*
951                  * check for valid day of month, now that we know for sure the
952                  * month and year...
953                  */
954                 if ((tm->tm_mday < 1)
955                  || (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]))
956                         return -1;
957
958                 /* timezone not specified? then find local timezone if possible */
959                 if (((fmask & DTK_DATE_M) == DTK_DATE_M)
960                         && (tzp != NULL) && (!(fmask & DTK_M(TZ))))
961                 {
962
963                         /*
964                          * daylight savings time modifier but no standard timezone?
965                          * then error
966                          */
967                         if (fmask & DTK_M(DTZMOD))
968                                 return -1;
969
970                         *tzp = DetermineLocalTimeZone(tm);
971                 }
972         }
973
974         return 0;
975 }       /* DecodeDateTime() */
976
977
978 /* DetermineLocalTimeZone()
979  * Given a struct tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min, and
980  * tm_sec fields are set, attempt to determine the applicable local zone
981  * (ie, regular or daylight-savings time) at that time.  Set the struct tm's
982  * tm_isdst field accordingly, and return the actual timezone offset.
983  *
984  * This subroutine exists mainly to centralize uses of mktime() and defend
985  * against mktime() bugs on various platforms...
986  */
987 int
988 DetermineLocalTimeZone(struct tm * tm)
989 {
990         int                     tz;
991
992         if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
993         {
994 #if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
995                 /*
996                  * Some buggy mktime() implementations may change the year/month/day
997                  * when given a time right at a DST boundary.  To prevent corruption
998                  * of the caller's data, give mktime() a copy...
999                  */
1000                 struct tm       tt,
1001                                    *tmp = &tt;
1002
1003                 *tmp = *tm;
1004                 /* change to Unix conventions for year/month */
1005                 tmp->tm_year -= 1900;
1006                 tmp->tm_mon -= 1;
1007
1008                 /* indicate timezone unknown */
1009                 tmp->tm_isdst = -1;
1010
1011                 mktime(tmp);
1012
1013                 tm->tm_isdst = tmp->tm_isdst;
1014
1015 #if defined(HAVE_TM_ZONE)
1016                 /* tm_gmtoff is Sun/DEC-ism */
1017                 if (tmp->tm_isdst >= 0)
1018                         tz = -(tmp->tm_gmtoff);
1019                 else
1020                         tz = 0;                         /* assume GMT if mktime failed */
1021 #elif defined(HAVE_INT_TIMEZONE)
1022                 tz = ((tmp->tm_isdst > 0) ? (TIMEZONE_GLOBAL - 3600) : TIMEZONE_GLOBAL);
1023 #endif   /* HAVE_INT_TIMEZONE */
1024
1025 #else                                                   /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
1026                 tm->tm_isdst = 0;
1027                 tz = CTimeZone;
1028 #endif
1029         }
1030         else
1031         {
1032                 /* Given date is out of range, so assume GMT */
1033                 tm->tm_isdst = 0;
1034                 tz = 0;
1035         }
1036
1037         return tz;
1038 }
1039
1040
1041 /* DecodeTimeOnly()
1042  * Interpret parsed string as time fields only.
1043  * Note that support for time zone is here for
1044  * SQL92 TIME WITH TIME ZONE, but it reveals
1045  * bogosity with SQL92 date/time standards, since
1046  * we must infer a time zone from current time.
1047  * XXX Later, we should probably support
1048  * SET TIME ZONE <integer>
1049  * which of course is a screwed up convention.
1050  * - thomas 2000-03-10
1051  */
1052 int
1053 DecodeTimeOnly(char **field, int *ftype, int nf,
1054                            int *dtype, struct tm * tm, double *fsec, int *tzp)
1055 {
1056         int                     fmask,
1057                                 tmask,
1058                                 type;
1059         int                     i;
1060         int                     flen,
1061                                 val;
1062         int                     is2digits = FALSE;
1063         int                     mer = HR24;
1064
1065         *dtype = DTK_TIME;
1066         tm->tm_hour = 0;
1067         tm->tm_min = 0;
1068         tm->tm_sec = 0;
1069         *fsec = 0;
1070         tm->tm_isdst = -1;                      /* don't know daylight savings time status
1071                                                                  * apriori */
1072         if (tzp != NULL)
1073                 *tzp = 0;
1074
1075         fmask = DTK_DATE_M;
1076
1077         for (i = 0; i < nf; i++)
1078         {
1079                 switch (ftype[i])
1080                 {
1081                         case DTK_DATE:
1082
1083                                 /*
1084                                  * This might be a POSIX time zone with an embedded dash
1085                                  * (e.g. "PST-3" == "EST") - thomas 2000-03-15
1086                                  */
1087                                 if ((tzp == NULL)
1088                                         || (DecodePosixTimezone(field[i], tzp) != 0))
1089                                         return -1;
1090
1091                                 ftype[i] = DTK_TZ;
1092                                 tmask = DTK_M(TZ);
1093                                 break;
1094
1095                         case DTK_TIME:
1096                                 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
1097                                         return -1;
1098                                 break;
1099
1100                         case DTK_TZ:
1101                                 if (tzp == NULL)
1102                                         return -1;
1103
1104                                 {
1105                                         int                     tz;
1106
1107                                         if (DecodeTimezone(field[i], &tz) != 0)
1108                                                 return -1;
1109
1110                                         /*
1111                                          * Already have a time zone? Then maybe this is the
1112                                          * second field of a POSIX time: EST+3 (equivalent to
1113                                          * PST)
1114                                          */
1115                                         if ((i > 0) && ((fmask & DTK_M(TZ)) != 0)
1116                                                 && (ftype[i - 1] == DTK_TZ) && (isalpha((unsigned char) *field[i - 1])))
1117                                         {
1118                                                 *tzp -= tz;
1119                                                 tmask = 0;
1120                                         }
1121                                         else
1122                                         {
1123                                                 *tzp = tz;
1124                                                 tmask = DTK_M(TZ);
1125                                         }
1126                                 }
1127                                 break;
1128
1129                         case DTK_NUMBER:
1130                                 flen = strlen(field[i]);
1131
1132                                 if (DecodeNumberField(flen, field[i], fmask, &tmask, tm, fsec, &is2digits) != 0)
1133                                         return -1;
1134                                 break;
1135
1136                         case DTK_STRING:
1137                         case DTK_SPECIAL:
1138                                 type = DecodeSpecial(i, field[i], &val);
1139                                 if (type == IGNORE)
1140                                         continue;
1141
1142                                 tmask = DTK_M(type);
1143                                 switch (type)
1144                                 {
1145                                         case RESERV:
1146                                                 switch (val)
1147                                                 {
1148                                                         case DTK_NOW:
1149                                                                 tmask = DTK_TIME_M;
1150                                                                 *dtype = DTK_TIME;
1151                                                                 GetCurrentTimeUsec(tm, fsec);
1152                                                                 break;
1153
1154                                                         case DTK_ZULU:
1155                                                                 tmask = (DTK_TIME_M | DTK_M(TZ));
1156                                                                 *dtype = DTK_TIME;
1157                                                                 tm->tm_hour = 0;
1158                                                                 tm->tm_min = 0;
1159                                                                 tm->tm_sec = 0;
1160                                                                 tm->tm_isdst = 0;
1161                                                                 break;
1162
1163                                                         default:
1164                                                                 return -1;
1165                                                 }
1166
1167                                                 break;
1168
1169                                         case DTZMOD:
1170
1171                                                 /*
1172                                                  * daylight savings time modifier (solves "MET
1173                                                  * DST" syntax)
1174                                                  */
1175                                                 tmask |= DTK_M(DTZ);
1176                                                 tm->tm_isdst = 1;
1177                                                 if (tzp == NULL)
1178                                                         return -1;
1179                                                 *tzp += val * 60;
1180                                                 break;
1181
1182                                         case DTZ:
1183
1184                                                 /*
1185                                                  * set mask for TZ here _or_ check for DTZ later
1186                                                  * when getting default timezone
1187                                                  */
1188                                                 tmask |= DTK_M(TZ);
1189                                                 tm->tm_isdst = 1;
1190                                                 if (tzp == NULL)
1191                                                         return -1;
1192                                                 *tzp = val * 60;
1193                                                 ftype[i] = DTK_TZ;
1194                                                 break;
1195
1196                                         case TZ:
1197                                                 tm->tm_isdst = 0;
1198                                                 if (tzp == NULL)
1199                                                         return -1;
1200                                                 *tzp = val * 60;
1201                                                 ftype[i] = DTK_TZ;
1202                                                 break;
1203
1204                                         case IGNORE:
1205                                                 break;
1206
1207                                         case AMPM:
1208                                                 mer = val;
1209                                                 break;
1210
1211                                         default:
1212                                                 return -1;
1213                                 }
1214                                 break;
1215
1216                         default:
1217                                 return -1;
1218                 }
1219
1220                 if (tmask & fmask)
1221                         return -1;
1222                 fmask |= tmask;
1223         }
1224
1225         if ((mer != HR24) && (tm->tm_hour > 12))
1226                 return -1;
1227         if ((mer == AM) && (tm->tm_hour == 12))
1228                 tm->tm_hour = 0;
1229         else if ((mer == PM) && (tm->tm_hour != 12))
1230                 tm->tm_hour += 12;
1231
1232         if (((tm->tm_hour < 0) || (tm->tm_hour > 23))
1233                 || ((tm->tm_min < 0) || (tm->tm_min > 59))
1234                 || ((tm->tm_sec < 0) || ((tm->tm_sec + *fsec) >= 60)))
1235                 return -1;
1236
1237         if ((fmask & DTK_TIME_M) != DTK_TIME_M)
1238                 return -1;
1239
1240         /* timezone not specified? then find local timezone if possible */
1241         if ((tzp != NULL) && (!(fmask & DTK_M(TZ))))
1242         {
1243                 struct tm       tt,
1244                                    *tmp = &tt;
1245
1246                 /*
1247                  * daylight savings time modifier but no standard timezone? then
1248                  * error
1249                  */
1250                 if (fmask & DTK_M(DTZMOD))
1251                         return -1;
1252
1253                 GetCurrentTime(tmp);
1254                 tmp->tm_hour = tm->tm_hour;
1255                 tmp->tm_min = tm->tm_min;
1256                 tmp->tm_sec = tm->tm_sec;
1257
1258                 *tzp = DetermineLocalTimeZone(tmp);
1259                 tm->tm_isdst = tmp->tm_isdst;
1260         }
1261
1262         return 0;
1263 }       /* DecodeTimeOnly() */
1264
1265 /* DecodeDate()
1266  * Decode date string which includes delimiters.
1267  * Insist on a complete set of fields.
1268  */
1269 static int
1270 DecodeDate(char *str, int fmask, int *tmask, struct tm * tm)
1271 {
1272         double          fsec;
1273
1274         int                     nf = 0;
1275         int                     i,
1276                                 len;
1277         int                     bc = FALSE;
1278         int                     is2digits = FALSE;
1279         int                     type,
1280                                 val,
1281                                 dmask = 0;
1282         char       *field[MAXDATEFIELDS];
1283
1284         /* parse this string... */
1285         while ((*str != '\0') && (nf < MAXDATEFIELDS))
1286         {
1287                 /* skip field separators */
1288                 while (!isalnum((unsigned char) *str))
1289                         str++;
1290
1291                 field[nf] = str;
1292                 if (isdigit((unsigned char) *str))
1293                 {
1294                         while (isdigit((unsigned char) *str))
1295                                 str++;
1296                 }
1297                 else if (isalpha((unsigned char) *str))
1298                 {
1299                         while (isalpha((unsigned char) *str))
1300                                 str++;
1301                 }
1302
1303                 /* Just get rid of any non-digit, non-alpha characters... */
1304                 if (*str != '\0')
1305                         *str++ = '\0';
1306                 nf++;
1307         }
1308
1309 #if 0
1310         /* don't allow too many fields */
1311         if (nf > 3)
1312                 return -1;
1313 #endif
1314
1315         *tmask = 0;
1316
1317         /* look first for text fields, since that will be unambiguous month */
1318         for (i = 0; i < nf; i++)
1319         {
1320                 if (isalpha((unsigned char) *field[i]))
1321                 {
1322                         type = DecodeSpecial(i, field[i], &val);
1323                         if (type == IGNORE)
1324                                 continue;
1325
1326                         dmask = DTK_M(type);
1327                         switch (type)
1328                         {
1329                                 case MONTH:
1330                                         tm->tm_mon = val;
1331                                         break;
1332
1333                                 case ADBC:
1334                                         bc = (val == BC);
1335                                         break;
1336
1337                                 default:
1338                                         return -1;
1339                         }
1340                         if (fmask & dmask)
1341                                 return -1;
1342
1343                         fmask |= dmask;
1344                         *tmask |= dmask;
1345
1346                         /* mark this field as being completed */
1347                         field[i] = NULL;
1348                 }
1349         }
1350
1351         /* now pick up remaining numeric fields */
1352         for (i = 0; i < nf; i++)
1353         {
1354                 if (field[i] == NULL)
1355                         continue;
1356
1357                 if ((len = strlen(field[i])) <= 0)
1358                         return -1;
1359
1360                 if (DecodeNumber(len, field[i], fmask, &dmask, tm, &fsec, &is2digits) != 0)
1361                         return -1;
1362
1363                 if (fmask & dmask)
1364                         return -1;
1365
1366                 fmask |= dmask;
1367                 *tmask |= dmask;
1368         }
1369
1370         if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M)
1371                 return -1;
1372
1373         /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
1374         if (bc)
1375         {
1376                 if (tm->tm_year > 0)
1377                         tm->tm_year = -(tm->tm_year - 1);
1378                 else
1379                         elog(ERROR, "Inconsistant use of year %04d and 'BC'", tm->tm_year);
1380         }
1381         else if (is2digits)
1382         {
1383                 if (tm->tm_year < 70)
1384                         tm->tm_year += 2000;
1385                 else if (tm->tm_year < 100)
1386                         tm->tm_year += 1900;
1387         }
1388
1389         return 0;
1390 }       /* DecodeDate() */
1391
1392
1393 /* DecodeTime()
1394  * Decode time string which includes delimiters.
1395  * Only check the lower limit on hours, since this same code
1396  *      can be used to represent time spans.
1397  */
1398 static int
1399 DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, double *fsec)
1400 {
1401         char       *cp;
1402
1403         *tmask = DTK_TIME_M;
1404
1405         tm->tm_hour = strtol(str, &cp, 10);
1406         if (*cp != ':')
1407                 return -1;
1408         str = cp + 1;
1409         tm->tm_min = strtol(str, &cp, 10);
1410         if (*cp == '\0')
1411         {
1412                 tm->tm_sec = 0;
1413                 *fsec = 0;
1414
1415         }
1416         else if (*cp != ':')
1417         {
1418                 return -1;
1419
1420         }
1421         else
1422         {
1423                 str = cp + 1;
1424                 tm->tm_sec = strtol(str, &cp, 10);
1425                 if (*cp == '\0')
1426                         *fsec = 0;
1427                 else if (*cp == '.')
1428                 {
1429                         str = cp;
1430                         *fsec = strtod(str, &cp);
1431                         if (cp == str)
1432                                 return -1;
1433                 }
1434                 else
1435                         return -1;
1436         }
1437
1438         /* do a sanity check */
1439         if ((tm->tm_hour < 0)
1440                 || (tm->tm_min < 0) || (tm->tm_min > 59)
1441                 || (tm->tm_sec < 0) || (tm->tm_sec > 59))
1442                 return -1;
1443
1444         return 0;
1445 }       /* DecodeTime() */
1446
1447
1448 /* DecodeNumber()
1449  * Interpret numeric field as a date value in context.
1450  */
1451 static int
1452 DecodeNumber(int flen, char *str, int fmask,
1453                          int *tmask, struct tm * tm, double *fsec, int *is2digits)
1454 {
1455         int                     val;
1456         char       *cp;
1457
1458         *tmask = 0;
1459
1460         val = strtol(str, &cp, 10);
1461         if (cp == str)
1462                 return -1;
1463         if (*cp == '.')
1464         {
1465                 *fsec = strtod(cp, &cp);
1466                 if (*cp != '\0')
1467                         return -1;
1468         }
1469
1470         /* Special case day of year? */
1471         if ((flen == 3) && (fmask & DTK_M(YEAR))
1472                 && ((val >= 1) && (val <= 366)))
1473         {
1474                 *tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));
1475                 tm->tm_yday = val;
1476                 j2date((date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1),
1477                            &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1478
1479         }
1480
1481         /*
1482          * Enough digits to be unequivocal year? Used to test for 4 digits or
1483          * more, but we now test first for a three-digit doy so anything
1484          * bigger than two digits had better be an explicit year.
1485          * - thomas 1999-01-09
1486          * Back to requiring a 4 digit year. We accept a two digit
1487          * year farther down. - thomas 2000-03-28
1488          */
1489         else if (flen >= 4)
1490         {
1491                 *tmask = DTK_M(YEAR);
1492
1493                 /* already have a year? then see if we can substitute... */
1494                 if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(DAY)))
1495                         && ((tm->tm_year >= 1) && (tm->tm_year <= 31)))
1496                 {
1497                         tm->tm_mday = tm->tm_year;
1498                         *tmask = DTK_M(DAY);
1499                 }
1500
1501                 tm->tm_year = val;
1502         }
1503
1504         /* already have year? then could be month */
1505         else if ((fmask & DTK_M(YEAR)) && (!(fmask & DTK_M(MONTH)))
1506                          && ((val >= 1) && (val <= 12)))
1507         {
1508                 *tmask = DTK_M(MONTH);
1509                 tm->tm_mon = val;
1510
1511         }
1512         /* no year and EuroDates enabled? then could be day */
1513         else if ((EuroDates || (fmask & DTK_M(MONTH)))
1514                          && (!(fmask & DTK_M(YEAR)) && !(fmask & DTK_M(DAY)))
1515                          && ((val >= 1) && (val <= 31)))
1516         {
1517                 *tmask = DTK_M(DAY);
1518                 tm->tm_mday = val;
1519         }
1520         else if ((!(fmask & DTK_M(MONTH)))
1521                          && ((val >= 1) && (val <= 12)))
1522         {
1523                 *tmask = DTK_M(MONTH);
1524                 tm->tm_mon = val;
1525         }
1526         else if ((!(fmask & DTK_M(DAY)))
1527                          && ((val >= 1) && (val <= 31)))
1528         {
1529                 *tmask = DTK_M(DAY);
1530                 tm->tm_mday = val;
1531         }
1532
1533         /*
1534          * Check for 2 or 4 or more digits, but currently we reach here only
1535          * if two digits. - thomas 2000-03-28
1536          */
1537         else if (!(fmask & DTK_M(YEAR))
1538                          && ((flen >= 4) || (flen == 2)))
1539         {
1540                 *tmask = DTK_M(YEAR);
1541                 tm->tm_year = val;
1542
1543                 /* adjust ONLY if exactly two digits... */
1544                 *is2digits = (flen == 2);
1545         }
1546         else
1547                 return -1;
1548
1549         return 0;
1550 }       /* DecodeNumber() */
1551
1552
1553 /* DecodeNumberField()
1554  * Interpret numeric string as a concatenated date field.
1555  */
1556 static int
1557 DecodeNumberField(int len, char *str, int fmask,
1558                                 int *tmask, struct tm * tm, double *fsec, int *is2digits)
1559 {
1560         char       *cp;
1561
1562         /* yyyymmdd? */
1563         if (len == 8)
1564         {
1565                 *tmask = DTK_DATE_M;
1566
1567                 tm->tm_mday = atoi(str + 6);
1568                 *(str + 6) = '\0';
1569                 tm->tm_mon = atoi(str + 4);
1570                 *(str + 4) = '\0';
1571                 tm->tm_year = atoi(str + 0);
1572                 /* yymmdd or hhmmss? */
1573         }
1574         else if (len == 6)
1575         {
1576                 if (fmask & DTK_DATE_M)
1577                 {
1578                         *tmask = DTK_TIME_M;
1579                         tm->tm_sec = atoi(str + 4);
1580                         *(str + 4) = '\0';
1581                         tm->tm_min = atoi(str + 2);
1582                         *(str + 2) = '\0';
1583                         tm->tm_hour = atoi(str + 0);
1584                 }
1585                 else
1586                 {
1587                         *tmask = DTK_DATE_M;
1588                         tm->tm_mday = atoi(str + 4);
1589                         *(str + 4) = '\0';
1590                         tm->tm_mon = atoi(str + 2);
1591                         *(str + 2) = '\0';
1592                         tm->tm_year = atoi(str + 0);
1593                         *is2digits = TRUE;
1594                 }
1595
1596         }
1597         else if ((len == 5) && !(fmask & DTK_DATE_M))
1598         {
1599                 *tmask = DTK_DATE_M;
1600                 tm->tm_mday = atoi(str + 2);
1601                 *(str + 2) = '\0';
1602                 tm->tm_mon = 1;
1603                 tm->tm_year = atoi(str + 0);
1604                 *is2digits = TRUE;
1605         }
1606         else if (strchr(str, '.') != NULL)
1607         {
1608                 *tmask = DTK_TIME_M;
1609                 tm->tm_sec = strtod((str + 4), &cp);
1610                 if (cp == (str + 4))
1611                         return -1;
1612                 if (*cp == '.')
1613                         *fsec = strtod(cp, NULL);
1614                 *(str + 4) = '\0';
1615                 tm->tm_min = strtod((str + 2), &cp);
1616                 *(str + 2) = '\0';
1617                 tm->tm_hour = strtod((str + 0), &cp);
1618
1619         }
1620         else
1621                 return -1;
1622
1623         return 0;
1624 }       /* DecodeNumberField() */
1625
1626
1627 /* DecodeTimezone()
1628  * Interpret string as a numeric timezone.
1629  */
1630 static int
1631 DecodeTimezone(char *str, int *tzp)
1632 {
1633         int                     tz;
1634         int                     hr,
1635                                 min;
1636         char       *cp;
1637         int                     len;
1638
1639         /* assume leading character is "+" or "-" */
1640         hr = strtol((str + 1), &cp, 10);
1641
1642         /* explicit delimiter? */
1643         if (*cp == ':')
1644         {
1645                 min = strtol((cp + 1), &cp, 10);
1646
1647                 /* otherwise, might have run things together... */
1648         }
1649         else if ((*cp == '\0') && ((len = strlen(str)) > 3))
1650         {
1651                 min = strtol((str + len - 2), &cp, 10);
1652                 *(str + len - 2) = '\0';
1653                 hr = strtol((str + 1), &cp, 10);
1654
1655         }
1656         else
1657                 min = 0;
1658
1659         tz = (hr * 60 + min) * 60;
1660         if (*str == '-')
1661                 tz = -tz;
1662
1663         *tzp = -tz;
1664         return *cp != '\0';
1665 }       /* DecodeTimezone() */
1666
1667
1668 /* DecodePosixTimezone()
1669  * Interpret string as a POSIX-compatible timezone:
1670  *      PST-hh:mm
1671  *      PST+h
1672  * - thomas 2000-03-15
1673  */
1674 static int
1675 DecodePosixTimezone(char *str, int *tzp)
1676 {
1677         int                     val,
1678                                 tz;
1679         int                     type;
1680         char       *cp;
1681         char            delim;
1682
1683         cp = str;
1684         while ((*cp != '\0') && isalpha((unsigned char) *cp))
1685                 cp++;
1686
1687         if (DecodeTimezone(cp, &tz) != 0)
1688                 return -1;
1689
1690         delim = *cp;
1691         *cp = '\0';
1692         type = DecodeSpecial(MAXDATEFIELDS - 1, str, &val);
1693         *cp = delim;
1694
1695         switch (type)
1696         {
1697                 case DTZ:
1698                 case TZ:
1699                         *tzp = (val * 60) - tz;
1700                         break;
1701
1702                 default:
1703                         return -1;
1704         }
1705
1706         return 0;
1707 }       /* DecodePosixTimezone() */
1708
1709
1710 /* DecodeSpecial()
1711  * Decode text string using lookup table.
1712  * Implement a cache lookup since it is likely that dates
1713  *      will be related in format.
1714  */
1715 int
1716 DecodeSpecial(int field, char *lowtoken, int *val)
1717 {
1718         int                     type;
1719         datetkn    *tp;
1720
1721         if ((datecache[field] != NULL)
1722                 && (strncmp(lowtoken, datecache[field]->token, TOKMAXLEN) == 0))
1723                 tp = datecache[field];
1724         else
1725         {
1726                 tp = NULL;
1727                 if (Australian_timezones)
1728                         tp = datebsearch(lowtoken, australian_datetktbl,
1729                                                                            australian_szdatetktbl);
1730                 if (!tp)
1731                         tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
1732         }
1733         datecache[field] = tp;
1734         if (tp == NULL)
1735         {
1736                 type = UNKNOWN_FIELD;
1737                 *val = 0;
1738         }
1739         else
1740         {
1741                 type = tp->type;
1742                 switch (type)
1743                 {
1744                         case TZ:
1745                         case DTZ:
1746                         case DTZMOD:
1747                                 *val = FROMVAL(tp);
1748                                 break;
1749
1750                         default:
1751                                 *val = tp->value;
1752                                 break;
1753                 }
1754         }
1755
1756         return type;
1757 }       /* DecodeSpecial() */
1758
1759
1760 /* DecodeDateDelta()
1761  * Interpret previously parsed fields for general time interval.
1762  * Return 0 if decoded and -1 if problems.
1763  *
1764  * Allow "date" field DTK_DATE since this could be just
1765  *      an unsigned floating point number. - thomas 1997-11-16
1766  *
1767  * Allow ISO-style time span, with implicit units on number of days
1768  *      preceeding an hh:mm:ss field. - thomas 1998-04-30
1769  */
1770 int
1771 DecodeDateDelta(char **field, int *ftype, int nf, int *dtype, struct tm * tm, double *fsec)
1772 {
1773         int                     is_before = FALSE;
1774
1775         char       *cp;
1776         int                     fmask = 0,
1777                                 tmask,
1778                                 type;
1779         int                     i;
1780         int                     val;
1781         double          fval;
1782         double          sec;
1783
1784         *dtype = DTK_DELTA;
1785
1786         type = IGNORE;
1787         tm->tm_year = 0;
1788         tm->tm_mon = 0;
1789         tm->tm_mday = 0;
1790         tm->tm_hour = 0;
1791         tm->tm_min = 0;
1792         tm->tm_sec = 0;
1793         *fsec = 0;
1794
1795         /* read through list backwards to pick up units before values */
1796         for (i = nf - 1; i >= 0; i--)
1797         {
1798                 switch (ftype[i])
1799                 {
1800                         case DTK_TIME:
1801                                 if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0)
1802                                         return -1;
1803                                 type = DTK_DAY;
1804                                 break;
1805
1806                         case DTK_TZ:
1807
1808                                 /*
1809                                  * Timezone is a token with a leading sign character and
1810                                  * otherwise the same as a non-signed time field
1811                                  */
1812                                 Assert((*field[i] == '-') || (*field[i] == '+'));
1813
1814                                 /*
1815                                  * A single signed number ends up here, but will be
1816                                  * rejected by DecodeTime(). So, work this out to drop
1817                                  * through to DTK_NUMBER, which *can* tolerate this.
1818                                  */
1819                                 cp = field[i] + 1;
1820                                 while ((*cp != '\0') && (*cp != ':') && (*cp != '.'))
1821                                         cp++;
1822                                 if ((*cp == ':')
1823                                         && (DecodeTime((field[i] + 1), fmask, &tmask, tm, fsec) == 0))
1824                                 {
1825                                         if (*field[i] == '-')
1826                                         {
1827                                                 /* flip the sign on all fields */
1828                                                 tm->tm_hour = -tm->tm_hour;
1829                                                 tm->tm_min = -tm->tm_min;
1830                                                 tm->tm_sec = -tm->tm_sec;
1831                                                 *fsec = -(*fsec);
1832                                         }
1833
1834                                         /*
1835                                          * Set the next type to be a day, if units are not
1836                                          * specified. This handles the case of '1 +02:03'
1837                                          * since we are reading right to left.
1838                                          */
1839                                         type = DTK_DAY;
1840                                         tmask = DTK_M(TZ);
1841                                         break;
1842                                 }
1843                                 else if (type == IGNORE)
1844                                 {
1845                                         if (*cp == '.')
1846                                         {
1847
1848                                                 /*
1849                                                  * Got a decimal point? Then assume some sort of
1850                                                  * seconds specification
1851                                                  */
1852                                                 type = DTK_SECOND;
1853                                         }
1854                                         else if (*cp == '\0')
1855                                         {
1856
1857                                                 /*
1858                                                  * Only a signed integer? Then must assume a
1859                                                  * timezone-like usage
1860                                                  */
1861                                                 type = DTK_HOUR;
1862                                         }
1863                                 }
1864                                 /* DROP THROUGH */
1865
1866                         case DTK_DATE:
1867                         case DTK_NUMBER:
1868                                 val = strtol(field[i], &cp, 10);
1869
1870                                 if (type == IGNORE)
1871                                         type = DTK_SECOND;
1872
1873                                 if (*cp == '.')
1874                                 {
1875                                         fval = strtod(cp, &cp);
1876                                         if (*cp != '\0')
1877                                                 return -1;
1878
1879                                         if (val < 0)
1880                                                 fval = -(fval);
1881                                 }
1882                                 else if (*cp == '\0')
1883                                         fval = 0;
1884                                 else
1885                                         return -1;
1886
1887                                 tmask = 0;              /* DTK_M(type); */
1888
1889                                 switch (type)
1890                                 {
1891                                         case DTK_MICROSEC:
1892                                                 *fsec += ((val + fval) * 1e-6);
1893                                                 break;
1894
1895                                         case DTK_MILLISEC:
1896                                                 *fsec += ((val + fval) * 1e-3);
1897                                                 break;
1898
1899                                         case DTK_SECOND:
1900                                                 tm->tm_sec += val;
1901                                                 *fsec += fval;
1902                                                 tmask = DTK_M(SECOND);
1903                                                 break;
1904
1905                                         case DTK_MINUTE:
1906                                                 tm->tm_min += val;
1907                                                 if (fval != 0)
1908                                                         tm->tm_sec += (fval * 60);
1909                                                 tmask = DTK_M(MINUTE);
1910                                                 break;
1911
1912                                         case DTK_HOUR:
1913                                                 tm->tm_hour += val;
1914                                                 if (fval != 0)
1915                                                         tm->tm_sec += (fval * 3600);
1916                                                 tmask = DTK_M(HOUR);
1917                                                 break;
1918
1919                                         case DTK_DAY:
1920                                                 tm->tm_mday += val;
1921                                                 if (fval != 0)
1922                                                         tm->tm_sec += (fval * 86400);
1923                                                 tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY));
1924                                                 break;
1925
1926                                         case DTK_WEEK:
1927                                                 tm->tm_mday += val * 7;
1928                                                 if (fval != 0)
1929                                                         tm->tm_sec += (fval * (7 * 86400));
1930                                                 tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY));
1931                                                 break;
1932
1933                                         case DTK_MONTH:
1934                                                 tm->tm_mon += val;
1935                                                 if (fval != 0)
1936                                                         tm->tm_sec += (fval * (30 * 86400));
1937                                                 tmask = DTK_M(MONTH);
1938                                                 break;
1939
1940                                         case DTK_YEAR:
1941                                                 tm->tm_year += val;
1942                                                 if (fval != 0)
1943                                                         tm->tm_mon += (fval * 12);
1944                                                 tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
1945                                                 break;
1946
1947                                         case DTK_DECADE:
1948                                                 tm->tm_year += val * 10;
1949                                                 if (fval != 0)
1950                                                         tm->tm_mon += (fval * 120);
1951                                                 tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
1952                                                 break;
1953
1954                                         case DTK_CENTURY:
1955                                                 tm->tm_year += val * 100;
1956                                                 if (fval != 0)
1957                                                         tm->tm_mon += (fval * 1200);
1958                                                 tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
1959                                                 break;
1960
1961                                         case DTK_MILLENNIUM:
1962                                                 tm->tm_year += val * 1000;
1963                                                 if (fval != 0)
1964                                                         tm->tm_mon += (fval * 12000);
1965                                                 tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR));
1966                                                 break;
1967
1968                                         default:
1969                                                 return -1;
1970                                 }
1971                                 break;
1972
1973                         case DTK_STRING:
1974                         case DTK_SPECIAL:
1975                                 type = DecodeUnits(i, field[i], &val);
1976                                 if (type == IGNORE)
1977                                         continue;
1978
1979                                 tmask = 0;              /* DTK_M(type); */
1980                                 switch (type)
1981                                 {
1982                                         case UNITS:
1983                                                 type = val;
1984                                                 break;
1985
1986                                         case AGO:
1987                                                 is_before = TRUE;
1988                                                 type = val;
1989                                                 break;
1990
1991                                         case RESERV:
1992                                                 tmask = (DTK_DATE_M || DTK_TIME_M);
1993                                                 *dtype = val;
1994                                                 break;
1995
1996                                         default:
1997                                                 return -1;
1998                                 }
1999                                 break;
2000
2001                         default:
2002                                 return -1;
2003                 }
2004
2005                 if (tmask & fmask)
2006                         return -1;
2007                 fmask |= tmask;
2008         }
2009
2010         if (*fsec != 0)
2011         {
2012                 TMODULO(*fsec, sec, 1e0);
2013                 tm->tm_sec += sec;
2014         }
2015
2016         if (is_before)
2017         {
2018                 *fsec = -(*fsec);
2019                 tm->tm_sec = -(tm->tm_sec);
2020                 tm->tm_min = -(tm->tm_min);
2021                 tm->tm_hour = -(tm->tm_hour);
2022                 tm->tm_mday = -(tm->tm_mday);
2023                 tm->tm_mon = -(tm->tm_mon);
2024                 tm->tm_year = -(tm->tm_year);
2025         }
2026
2027         /* ensure that at least one time field has been found */
2028         return (fmask != 0) ? 0 : -1;
2029 }       /* DecodeDateDelta() */
2030
2031
2032 /* DecodeUnits()
2033  * Decode text string using lookup table.
2034  * This routine supports time interval decoding.
2035  */
2036 int
2037 DecodeUnits(int field, char *lowtoken, int *val)
2038 {
2039         int                     type;
2040         datetkn    *tp;
2041
2042         if ((deltacache[field] != NULL)
2043                 && (strncmp(lowtoken, deltacache[field]->token, TOKMAXLEN) == 0))
2044                 tp = deltacache[field];
2045         else
2046         {
2047                 tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
2048         }
2049         deltacache[field] = tp;
2050         if (tp == NULL)
2051         {
2052                 type = UNKNOWN_FIELD;
2053                 *val = 0;
2054         }
2055         else
2056         {
2057                 type = tp->type;
2058                 if ((type == TZ) || (type == DTZ))
2059                         *val = FROMVAL(tp);
2060                 else
2061                         *val = tp->value;
2062         }
2063
2064         return type;
2065 }       /* DecodeUnits() */
2066
2067
2068 /* datebsearch()
2069  * Binary search -- from Knuth (6.2.1) Algorithm B.  Special case like this
2070  * is WAY faster than the generic bsearch().
2071  */
2072 static datetkn *
2073 datebsearch(char *key, datetkn *base, unsigned int nel)
2074 {
2075         datetkn    *last = base + nel - 1,
2076                            *position;
2077         int                     result;
2078
2079         while (last >= base)
2080         {
2081                 position = base + ((last - base) >> 1);
2082                 result = key[0] - position->token[0];
2083                 if (result == 0)
2084                 {
2085                         result = strncmp(key, position->token, TOKMAXLEN);
2086                         if (result == 0)
2087                                 return position;
2088                 }
2089                 if (result < 0)
2090                         last = position - 1;
2091                 else
2092                         base = position + 1;
2093         }
2094         return NULL;
2095 }
2096
2097
2098 /* EncodeDateOnly()
2099  * Encode date as local time.
2100  */
2101 int
2102 EncodeDateOnly(struct tm * tm, int style, char *str)
2103 {
2104         if ((tm->tm_mon < 1) || (tm->tm_mon > 12))
2105                 return -1;
2106
2107         switch (style)
2108         {
2109                 case USE_ISO_DATES:
2110                         /* compatible with ISO date formats */
2111                         if (tm->tm_year > 0)
2112                                 sprintf(str, "%04d-%02d-%02d",
2113                                                 tm->tm_year, tm->tm_mon, tm->tm_mday);
2114                         else
2115                                 sprintf(str, "%04d-%02d-%02d %s",
2116                                           -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
2117                         break;
2118
2119                 case USE_SQL_DATES:
2120                         /* compatible with Oracle/Ingres date formats */
2121                         if (EuroDates)
2122                                 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
2123                         else
2124                                 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
2125                         if (tm->tm_year > 0)
2126                                 sprintf((str + 5), "/%04d", tm->tm_year);
2127                         else
2128                                 sprintf((str + 5), "/%04d %s", -(tm->tm_year - 1), "BC");
2129                         break;
2130
2131                 case USE_GERMAN_DATES:
2132                         /* German-style date format */
2133                         sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
2134                         if (tm->tm_year > 0)
2135                                 sprintf((str + 5), ".%04d", tm->tm_year);
2136                         else
2137                                 sprintf((str + 5), ".%04d %s", -(tm->tm_year - 1), "BC");
2138                         break;
2139
2140                 case USE_POSTGRES_DATES:
2141                 default:
2142                         /* traditional date-only style for Postgres */
2143                         if (EuroDates)
2144                                 sprintf(str, "%02d-%02d", tm->tm_mday, tm->tm_mon);
2145                         else
2146                                 sprintf(str, "%02d-%02d", tm->tm_mon, tm->tm_mday);
2147                         if (tm->tm_year > 0)
2148                                 sprintf((str + 5), "-%04d", tm->tm_year);
2149                         else
2150                                 sprintf((str + 5), "-%04d %s", -(tm->tm_year - 1), "BC");
2151                         break;
2152         }
2153
2154         return TRUE;
2155 }       /* EncodeDateOnly() */
2156
2157
2158 /* EncodeTimeOnly()
2159  * Encode time fields only.
2160  */
2161 int
2162 EncodeTimeOnly(struct tm * tm, double fsec, int *tzp, int style, char *str)
2163 {
2164         double          sec;
2165
2166         if ((tm->tm_hour < 0) || (tm->tm_hour > 24))
2167                 return -1;
2168
2169         sec = (tm->tm_sec + fsec);
2170
2171         sprintf(str, "%02d:%02d", tm->tm_hour, tm->tm_min);
2172
2173         /* If we have fractional seconds, then include a decimal point
2174          * We will do up to 6 fractional digits, and we have rounded any inputs
2175          * to eliminate anything to the right of 6 digits anyway.
2176          * If there are no fractional seconds, then do not bother printing
2177          * a decimal point at all. - thomas 2001-09-29
2178          */
2179         if (fsec != 0) {
2180                 sprintf((str + strlen(str)), ":%013.10f", sec);
2181                 /* chop off trailing pairs of zeros... */
2182                 while ((strcmp((str + strlen(str) - 2), "00") == 0)
2183                            && (*(str + strlen(str) - 3) != '.'))
2184                 {
2185                         *(str + strlen(str) - 2) = '\0';
2186                 }
2187         }
2188         else
2189         {
2190                 sprintf((str + strlen(str)), ":%02.0f", sec);
2191         }
2192
2193         if (tzp != NULL)
2194         {
2195                 int                     hour,
2196                                         min;
2197
2198                 hour = -(*tzp / 3600);
2199                 min = ((abs(*tzp) / 60) % 60);
2200                 sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
2201         }
2202
2203         return TRUE;
2204 }       /* EncodeTimeOnly() */
2205
2206
2207 /* EncodeDateTime()
2208  * Encode date and time interpreted as local time.
2209  * Support several date styles:
2210  *      Postgres - day mon hh:mm:ss yyyy tz
2211  *      SQL - mm/dd/yyyy hh:mm:ss.ss tz
2212  *      ISO - yyyy-mm-dd hh:mm:ss+/-tz
2213  *      German - dd.mm/yyyy hh:mm:ss tz
2214  * Variants (affects order of month and day for Postgres and SQL styles):
2215  *      US - mm/dd/yyyy
2216  *      European - dd/mm/yyyy
2217  */
2218 int
2219 EncodeDateTime(struct tm * tm, double fsec, int *tzp, char **tzn, int style, char *str)
2220 {
2221         int                     day,
2222                                 hour,
2223                                 min;
2224         double          sec;
2225
2226         if ((tm->tm_mon < 1) || (tm->tm_mon > 12))
2227                 return -1;
2228
2229         sec = (tm->tm_sec + fsec);
2230
2231         switch (style)
2232         {
2233                         /* compatible with ISO date formats */
2234
2235                 case USE_ISO_DATES:
2236                         if (tm->tm_year > 0)
2237                         {
2238                                 sprintf(str, "%04d-%02d-%02d %02d:%02d",
2239                                                 tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
2240
2241                                 /* If we have fractional seconds, then include a decimal point
2242                                  * We will do up to 6 fractional digits, and we have rounded any inputs
2243                                  * to eliminate anything to the right of 6 digits anyway.
2244                                  * If there are no fractional seconds, then do not bother printing
2245                                  * a decimal point at all. - thomas 2001-09-29
2246                                  */
2247                                 if (fsec != 0) {
2248                                         sprintf((str + strlen(str)), ":%013.10f", sec);
2249                                         /* chop off trailing pairs of zeros... */
2250                                         while ((strcmp((str + strlen(str) - 2), "00") == 0)
2251                                                    && (*(str + strlen(str) - 3) != '.'))
2252                                         {
2253                                                 *(str + strlen(str) - 2) = '\0';
2254                                         }
2255                                 }
2256                                 else
2257                                 {
2258                                         sprintf((str + strlen(str)), ":%02.0f", sec);
2259                                 }
2260
2261                                 if ((*tzn != NULL) && (tm->tm_isdst >= 0))
2262                                 {
2263                                         if (tzp != NULL)
2264                                         {
2265                                                 hour = -(*tzp / 3600);
2266                                                 min = ((abs(*tzp) / 60) % 60);
2267                                         }
2268                                         else
2269                                         {
2270                                                 hour = 0;
2271                                                 min = 0;
2272                                         }
2273                                         sprintf((str + strlen(str)), ((min != 0) ? "%+03d:%02d" : "%+03d"), hour, min);
2274                                 }
2275
2276                         }
2277                         else
2278                         {
2279                                 if (tm->tm_hour || tm->tm_min)
2280                                         sprintf(str, "%04d-%02d-%02d %02d:%02d %s",
2281                                                         -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, "BC");
2282                                 else
2283                                         sprintf(str, "%04d-%02d-%02d %s",
2284                                           -(tm->tm_year - 1), tm->tm_mon, tm->tm_mday, "BC");
2285                         }
2286                         break;
2287
2288                         /* compatible with Oracle/Ingres date formats */
2289                 case USE_SQL_DATES:
2290                         if (EuroDates)
2291                                 sprintf(str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
2292                         else
2293                                 sprintf(str, "%02d/%02d", tm->tm_mon, tm->tm_mday);
2294
2295                         if (tm->tm_year > 0)
2296                         {
2297                                 sprintf((str + 5), "/%04d %02d:%02d",
2298                                                 tm->tm_year, tm->tm_hour, tm->tm_min);
2299
2300                                 /* If we have fractional seconds, then include a decimal point
2301                                  * We will do up to 6 fractional digits, and we have rounded any inputs
2302                                  * to eliminate anything to the right of 6 digits anyway.
2303                                  * If there are no fractional seconds, then do not bother printing
2304                                  * a decimal point at all. - thomas 2001-09-29
2305                                  */
2306                                 if (fsec != 0) {
2307                                         sprintf((str + strlen(str)), ":%013.10f", sec);
2308                                         /* chop off trailing pairs of zeros... */
2309                                         while ((strcmp((str + strlen(str) - 2), "00") == 0)
2310                                                    && (*(str + strlen(str) - 3) != '.'))
2311                                         {
2312                                                 *(str + strlen(str) - 2) = '\0';
2313                                         }
2314                                 }
2315                                 else
2316                                 {
2317                                         sprintf((str + strlen(str)), ":%02.0f", sec);
2318                                 }
2319
2320                                 if ((*tzn != NULL) && (tm->tm_isdst >= 0))
2321                                         sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn);
2322                         }
2323                         else
2324                                 sprintf((str + 5), "/%04d %02d:%02d %s",
2325                                           -(tm->tm_year - 1), tm->tm_hour, tm->tm_min, "BC");
2326                         break;
2327
2328                         /* German variant on European style */
2329                 case USE_GERMAN_DATES:
2330                         sprintf(str, "%02d.%02d", tm->tm_mday, tm->tm_mon);
2331                         if (tm->tm_year > 0)
2332                         {
2333                                 sprintf((str + 5), ".%04d %02d:%02d",
2334                                                 tm->tm_year, tm->tm_hour, tm->tm_min);
2335
2336                                 /* If we have fractional seconds, then include a decimal point
2337                                  * We will do up to 6 fractional digits, and we have rounded any inputs
2338                                  * to eliminate anything to the right of 6 digits anyway.
2339                                  * If there are no fractional seconds, then do not bother printing
2340                                  * a decimal point at all. - thomas 2001-09-29
2341                                  */
2342                                 if (fsec != 0) {
2343                                         sprintf((str + strlen(str)), ":%013.10f", sec);
2344                                         /* chop off trailing pairs of zeros... */
2345                                         while ((strcmp((str + strlen(str) - 2), "00") == 0)
2346                                                    && (*(str + strlen(str) - 3) != '.'))
2347                                         {
2348                                                 *(str + strlen(str) - 2) = '\0';
2349                                         }
2350                                 }
2351                                 else
2352                                 {
2353                                         sprintf((str + strlen(str)), ":%02.0f", sec);
2354                                 }
2355
2356                                 if ((*tzn != NULL) && (tm->tm_isdst >= 0))
2357                                         sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn);
2358                         }
2359                         else
2360                                 sprintf((str + 5), ".%04d %02d:%02d %s",
2361                                           -(tm->tm_year - 1), tm->tm_hour, tm->tm_min, "BC");
2362                         break;
2363
2364                         /* backward-compatible with traditional Postgres abstime dates */
2365                 case USE_POSTGRES_DATES:
2366                 default:
2367                         day = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
2368                         tm->tm_wday = j2day(day);
2369
2370                         strncpy(str, days[tm->tm_wday], 3);
2371                         strcpy((str + 3), " ");
2372
2373                         if (EuroDates)
2374                                 sprintf((str + 4), "%02d %3s", tm->tm_mday, months[tm->tm_mon - 1]);
2375                         else
2376                                 sprintf((str + 4), "%3s %02d", months[tm->tm_mon - 1], tm->tm_mday);
2377
2378                         if (tm->tm_year > 0)
2379                         {
2380                                 sprintf((str + 10), " %02d:%02d", tm->tm_hour, tm->tm_min);
2381
2382                                 /* If we have fractional seconds, then include a decimal point
2383                                  * We will do up to 6 fractional digits, and we have rounded any inputs
2384                                  * to eliminate anything to the right of 6 digits anyway.
2385                                  * If there are no fractional seconds, then do not bother printing
2386                                  * a decimal point at all. - thomas 2001-09-29
2387                                  */
2388                                 if (fsec != 0) {
2389                                         sprintf((str + strlen(str)), ":%013.10f", sec);
2390                                         /* chop off trailing pairs of zeros... */
2391                                         while ((strcmp((str + strlen(str) - 2), "00") == 0)
2392                                                    && (*(str + strlen(str) - 3) != '.'))
2393                                         {
2394                                                 *(str + strlen(str) - 2) = '\0';
2395                                         }
2396                                 }
2397                                 else
2398                                 {
2399                                         sprintf((str + strlen(str)), ":%02.0f", sec);
2400                                 }
2401
2402                                 sprintf((str + strlen(str)), " %04d", tm->tm_year);
2403                                 if ((*tzn != NULL) && (tm->tm_isdst >= 0))
2404                                         sprintf((str + strlen(str)), " %.*s", MAXTZLEN, *tzn);
2405                         }
2406                         else
2407                         {
2408                                 sprintf((str + 10), " %02d:%02d %04d %s",
2409                                           tm->tm_hour, tm->tm_min, -(tm->tm_year - 1), "BC");
2410                         }
2411                         break;
2412         }
2413
2414         return TRUE;
2415 }       /* EncodeDateTime() */
2416
2417
2418 /* EncodeTimeSpan()
2419  * Interpret time structure as a delta time and convert to string.
2420  *
2421  * Support "traditional Postgres" and ISO-8601 styles.
2422  * Actually, afaik ISO does not address time interval formatting,
2423  *      but this looks similar to the spec for absolute date/time.
2424  * - thomas 1998-04-30
2425  */
2426 int
2427 EncodeTimeSpan(struct tm * tm, double fsec, int style, char *str)
2428 {
2429         int                     is_before = FALSE;
2430         int                     is_nonzero = FALSE;
2431         char       *cp = str;
2432
2433         /*
2434          * The sign of year and month are guaranteed to match, since they are
2435          * stored internally as "month". But we'll need to check for is_before
2436          * and is_nonzero when determining the signs of hour/minute/seconds
2437          * fields.
2438          */
2439         switch (style)
2440         {
2441                         /* compatible with ISO date formats */
2442                 case USE_ISO_DATES:
2443                         if (tm->tm_year != 0)
2444                         {
2445                                 sprintf(cp, "%d year%s",
2446                                                 tm->tm_year, ((tm->tm_year != 1) ? "s" : ""));
2447                                 cp += strlen(cp);
2448                                 is_before = (tm->tm_year < 0);
2449                                 is_nonzero = TRUE;
2450                         }
2451
2452                         if (tm->tm_mon != 0)
2453                         {
2454                                 sprintf(cp, "%s%s%d mon%s", (is_nonzero ? " " : ""),
2455                                                 ((is_before && (tm->tm_mon > 0)) ? "+" : ""),
2456                                                 tm->tm_mon, ((tm->tm_mon != 1) ? "s" : ""));
2457                                 cp += strlen(cp);
2458                                 is_before = (tm->tm_mon < 0);
2459                                 is_nonzero = TRUE;
2460                         }
2461
2462                         if (tm->tm_mday != 0)
2463                         {
2464                                 sprintf(cp, "%s%s%d day%s", (is_nonzero ? " " : ""),
2465                                                 ((is_before && (tm->tm_mday > 0)) ? "+" : ""),
2466                                                 tm->tm_mday, ((tm->tm_mday != 1) ? "s" : ""));
2467                                 cp += strlen(cp);
2468                                 is_before = (tm->tm_mday < 0);
2469                                 is_nonzero = TRUE;
2470                         }
2471                         if ((!is_nonzero) || (tm->tm_hour != 0) || (tm->tm_min != 0)
2472                                 || (tm->tm_sec != 0) || (fsec != 0))
2473                         {
2474                                 int                     minus = ((tm->tm_hour < 0) || (tm->tm_min < 0)
2475                                                                          || (tm->tm_sec < 0) || (fsec < 0));
2476
2477                                 sprintf(cp, "%s%s%02d:%02d", (is_nonzero ? " " : ""),
2478                                                 (minus ? "-" : (is_before ? "+" : "")),
2479                                                 abs(tm->tm_hour), abs(tm->tm_min));
2480                                 cp += strlen(cp);
2481                                 /* Mark as "non-zero" since the fields are now filled in */
2482                                 is_nonzero = TRUE;
2483
2484                                 /* fractional seconds? */
2485                                 if (fsec != 0)
2486                                 {
2487                                         fsec += tm->tm_sec;
2488                                         sprintf(cp, ":%05.2f", fabs(fsec));
2489                                         cp += strlen(cp);
2490                                         is_nonzero = TRUE;
2491
2492                                         /* otherwise, integer seconds only? */
2493                                 }
2494                                 else if (tm->tm_sec != 0)
2495                                 {
2496                                         sprintf(cp, ":%02d", abs(tm->tm_sec));
2497                                         cp += strlen(cp);
2498                                         is_nonzero = TRUE;
2499                                 }
2500                         }
2501                         break;
2502
2503                 case USE_POSTGRES_DATES:
2504                 default:
2505                         strcpy(cp, "@ ");
2506                         cp += strlen(cp);
2507
2508                         if (tm->tm_year != 0)
2509                         {
2510                                 int                     year = tm->tm_year;
2511
2512                                 if (tm->tm_year < 0)
2513                                         year = -year;
2514
2515                                 sprintf(cp, "%d year%s", year,
2516                                                 ((year != 1) ? "s" : ""));
2517                                 cp += strlen(cp);
2518                                 is_before = (tm->tm_year < 0);
2519                                 is_nonzero = TRUE;
2520                         }
2521
2522                         if (tm->tm_mon != 0)
2523                         {
2524                                 int                     mon = tm->tm_mon;
2525
2526                                 if (is_before || ((!is_nonzero) && (tm->tm_mon < 0)))
2527                                         mon = -mon;
2528
2529                                 sprintf(cp, "%s%d mon%s", (is_nonzero ? " " : ""), mon,
2530                                                 ((mon != 1) ? "s" : ""));
2531                                 cp += strlen(cp);
2532                                 if (!is_nonzero)
2533                                         is_before = (tm->tm_mon < 0);
2534                                 is_nonzero = TRUE;
2535                         }
2536
2537                         if (tm->tm_mday != 0)
2538                         {
2539                                 int                     day = tm->tm_mday;
2540
2541                                 if (is_before || ((!is_nonzero) && (tm->tm_mday < 0)))
2542                                         day = -day;
2543
2544                                 sprintf(cp, "%s%d day%s", (is_nonzero ? " " : ""), day,
2545                                                 ((day != 1) ? "s" : ""));
2546                                 cp += strlen(cp);
2547                                 if (!is_nonzero)
2548                                         is_before = (tm->tm_mday < 0);
2549                                 is_nonzero = TRUE;
2550                         }
2551                         if (tm->tm_hour != 0)
2552                         {
2553                                 int                     hour = tm->tm_hour;
2554
2555                                 if (is_before || ((!is_nonzero) && (tm->tm_hour < 0)))
2556                                         hour = -hour;
2557
2558                                 sprintf(cp, "%s%d hour%s", (is_nonzero ? " " : ""), hour,
2559                                                 ((hour != 1) ? "s" : ""));
2560                                 cp += strlen(cp);
2561                                 if (!is_nonzero)
2562                                         is_before = (tm->tm_hour < 0);
2563                                 is_nonzero = TRUE;
2564                         }
2565
2566                         if (tm->tm_min != 0)
2567                         {
2568                                 int                     min = tm->tm_min;
2569
2570                                 if (is_before || ((!is_nonzero) && (tm->tm_min < 0)))
2571                                         min = -min;
2572
2573                                 sprintf(cp, "%s%d min%s", (is_nonzero ? " " : ""), min,
2574                                                 ((min != 1) ? "s" : ""));
2575                                 cp += strlen(cp);
2576                                 if (!is_nonzero)
2577                                         is_before = (tm->tm_min < 0);
2578                                 is_nonzero = TRUE;
2579                         }
2580
2581                         /* fractional seconds? */
2582                         if (fsec != 0)
2583                         {
2584                                 double          sec;
2585
2586                                 fsec += tm->tm_sec;
2587                                 sec = fsec;
2588                                 if (is_before || ((!is_nonzero) && (fsec < 0)))
2589                                         sec = -sec;
2590
2591                                 sprintf(cp, "%s%.2f secs", (is_nonzero ? " " : ""), sec);
2592                                 cp += strlen(cp);
2593                                 if (!is_nonzero)
2594                                         is_before = (fsec < 0);
2595                                 is_nonzero = TRUE;
2596
2597                                 /* otherwise, integer seconds only? */
2598                         }
2599                         else if (tm->tm_sec != 0)
2600                         {
2601                                 int                     sec = tm->tm_sec;
2602
2603                                 if (is_before || ((!is_nonzero) && (tm->tm_sec < 0)))
2604                                         sec = -sec;
2605
2606                                 sprintf(cp, "%s%d sec%s", (is_nonzero ? " " : ""), sec,
2607                                                 ((sec != 1) ? "s" : ""));
2608                                 cp += strlen(cp);
2609                                 if (!is_nonzero)
2610                                         is_before = (tm->tm_sec < 0);
2611                                 is_nonzero = TRUE;
2612                         }
2613                         break;
2614         }
2615
2616         /* identically zero? then put in a unitless zero... */
2617         if (!is_nonzero)
2618         {
2619                 strcat(cp, "0");
2620                 cp += strlen(cp);
2621         }
2622
2623         if (is_before && (style == USE_POSTGRES_DATES))
2624         {
2625                 strcat(cp, " ago");
2626                 cp += strlen(cp);
2627         }
2628
2629         return 0;
2630 }       /* EncodeTimeSpan() */
2631
2632
2633 void ClearDateCache(bool dummy)
2634 {
2635         int i;
2636
2637         for (i=0; i < MAXDATEFIELDS; i++)
2638                 datecache[i] = NULL;
2639 }