2 PostgreSQL Data Base Management System (formerly known as Postgres, then
5 Copyright (c) 1994-7 Regents of the University of California
7 Permission to use, copy, modify, and distribute this software and its
8 documentation for any purpose, without fee, and without a written agreement
9 is hereby granted, provided that the above copyright notice and this
10 paragraph and the following two paragraphs appear in all copies.
12 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
13 DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
14 LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
15 DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
16 POSSIBILITY OF SUCH DAMAGE.
18 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
19 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
20 AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
22 PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 /*-------------------------------------------------------------------------
28 * Functions for the built-in type "dt".
30 * Copyright (c) 1994, Regents of the University of California
36 *-------------------------------------------------------------------------
43 #include <sys/types.h>
47 #include <sys/timeb.h>
51 static datetkn
*datebsearch(char *key
, datetkn
*base
, unsigned int nel
);
52 static int DecodeDate(char *str
, int fmask
, int *tmask
, struct tm
* tm
);
53 static int DecodeNumber(int flen
, char *field
,
54 int fmask
, int *tmask
, struct tm
* tm
, double *fsec
);
55 static int DecodeNumberField(int len
, char *str
,
56 int fmask
, int *tmask
, struct tm
* tm
, double *fsec
);
57 static int DecodeSpecial(int field
, char *lowtoken
, int *val
);
58 static int DecodeTime(char *str
, int fmask
, int *tmask
,
59 struct tm
* tm
, double *fsec
);
60 static int DecodeTimezone(char *str
, int *tzp
);
62 #define USE_DATE_CACHE 1
65 int mdays
[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0};
67 char *months
[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
68 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
};
70 char *days
[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
71 "Thursday", "Friday", "Saturday", NULL
};
74 #define UTIME_MINYEAR (1901)
75 #define UTIME_MINMONTH (12)
76 #define UTIME_MINDAY (14)
77 #define UTIME_MAXYEAR (2038)
78 #define UTIME_MAXMONTH (01)
79 #define UTIME_MAXDAY (18)
81 #define IS_VALID_UTIME(y,m,d) (((y > UTIME_MINYEAR) \
82 || ((y == UTIME_MINYEAR) && ((m > UTIME_MINMONTH) \
83 || ((m == UTIME_MINMONTH) && (d >= UTIME_MINDAY))))) \
84 && ((y < UTIME_MAXYEAR) \
85 || ((y == UTIME_MAXYEAR) && ((m < UTIME_MAXMONTH) \
86 || ((m == UTIME_MAXMONTH) && (d <= UTIME_MAXDAY))))))
91 /*****************************************************************************
93 *****************************************************************************/
95 /* definitions for squeezing values into "value" */
96 #define ABS_SIGNBIT (char) 0200
97 #define VALMASK (char) 0177
98 #define NEG(n) ((n)|ABS_SIGNBIT)
99 #define SIGNEDCHAR(c) ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
100 #define FROMVAL(tp) (-SIGNEDCHAR((tp)->value) * 10) /* uncompress */
101 #define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/10): (v)/10))
104 * to keep this table reasonably small, we divide the lexval for TZ and DTZ
105 * entries by 10 and truncate the text field at MAXTOKLEN characters.
106 * the text field is not guaranteed to be NULL-terminated.
108 static datetkn datetktbl
[] = {
109 /* text token lexval */
110 {EARLY
, RESERV
, DTK_EARLY
}, /* "-infinity" reserved for "early time" */
111 {"acsst", DTZ
, 63}, /* Cent. Australia */
112 {"acst", TZ
, 57}, /* Cent. Australia */
113 {DA_D
, ADBC
, AD
}, /* "ad" for years >= 0 */
114 {"abstime", IGNOREFIELD
, 0}, /* "abstime" for pre-v6.1 "Invalid
116 {"adt", DTZ
, NEG(18)}, /* Atlantic Daylight Time */
117 {"aesst", DTZ
, 66}, /* E. Australia */
118 {"aest", TZ
, 60}, /* Australia Eastern Std Time */
119 {"ahst", TZ
, 60}, /* Alaska-Hawaii Std Time */
120 {"allballs", RESERV
, DTK_ZULU
}, /* 00:00:00 */
124 {"ast", TZ
, NEG(24)}, /* Atlantic Std Time (Canada) */
125 {"at", IGNOREFIELD
, 0}, /* "at" (throwaway) */
127 {"august", MONTH
, 8},
128 {"awsst", DTZ
, 54}, /* W. Australia */
129 {"awst", TZ
, 48}, /* W. Australia */
130 {DB_C
, ADBC
, BC
}, /* "bc" for years < 0 */
131 {"bst", TZ
, 6}, /* British Summer Time */
132 {"bt", TZ
, 18}, /* Baghdad Time */
133 {"cadt", DTZ
, 63}, /* Central Australian DST */
134 {"cast", TZ
, 57}, /* Central Australian ST */
135 {"cat", TZ
, NEG(60)}, /* Central Alaska Time */
136 {"cct", TZ
, 48}, /* China Coast */
137 {"cdt", DTZ
, NEG(30)}, /* Central Daylight Time */
138 {"cet", TZ
, 6}, /* Central European Time */
139 {"cetdst", DTZ
, 12}, /* Central European Dayl.Time */
140 {"cst", TZ
, NEG(36)}, /* Central Standard Time */
141 {DCURRENT
, RESERV
, DTK_CURRENT
}, /* "current" is always now */
143 {"december", MONTH
, 12},
144 {"dnt", TZ
, 6}, /* Dansk Normal Tid */
145 {"dow", RESERV
, DTK_DOW
}, /* day of week */
146 {"doy", RESERV
, DTK_DOY
}, /* day of year */
148 {"east", TZ
, NEG(60)}, /* East Australian Std Time */
149 {"edt", DTZ
, NEG(24)}, /* Eastern Daylight Time */
150 {"eet", TZ
, 12}, /* East. Europe, USSR Zone 1 */
151 {"eetdst", DTZ
, 18}, /* Eastern Europe */
152 {EPOCH
, RESERV
, DTK_EPOCH
}, /* "epoch" reserved for system epoch time */
153 #if USE_AUSTRALIAN_RULES
154 {"est", TZ
, 60}, /* Australia Eastern Std Time */
156 {"est", TZ
, NEG(30)}, /* Eastern Standard Time */
159 {"february", MONTH
, 2},
162 {"fst", TZ
, 6}, /* French Summer Time */
163 {"fwt", DTZ
, 12}, /* French Winter Time */
164 {"gmt", TZ
, 0}, /* Greenwish Mean Time */
165 {"gst", TZ
, 60}, /* Guam Std Time, USSR Zone 9 */
166 {"hdt", DTZ
, NEG(54)}, /* Hawaii/Alaska */
167 {"hmt", DTZ
, 18}, /* Hellas ? ? */
168 {"hst", TZ
, NEG(60)}, /* Hawaii Std Time */
169 {"idle", TZ
, 72}, /* Intl. Date Line, East */
170 {"idlw", TZ
, NEG(72)}, /* Intl. Date Line,, est */
171 {LATE
, RESERV
, DTK_LATE
}, /* "infinity" reserved for "late time" */
172 {INVALID
, RESERV
, DTK_INVALID
}, /* "invalid" reserved for invalid
174 {"ist", TZ
, 12}, /* Israel */
175 {"it", TZ
, 22}, /* Iran Time */
177 {"january", MONTH
, 1},
178 {"jst", TZ
, 54}, /* Japan Std Time,USSR Zone 8 */
179 {"jt", TZ
, 45}, /* Java Time */
184 {"kst", TZ
, 54}, /* Korea Standard Time */
185 {"ligt", TZ
, 60}, /* From Melbourne, Australia */
189 {"mdt", DTZ
, NEG(36)}, /* Mountain Daylight Time */
190 {"mest", DTZ
, 12}, /* Middle Europe Summer Time */
191 {"met", TZ
, 6}, /* Middle Europe Time */
192 {"metdst", DTZ
, 12}, /* Middle Europe Daylight Time */
193 {"mewt", TZ
, 6}, /* Middle Europe Winter Time */
194 {"mez", TZ
, 6}, /* Middle Europe Zone */
197 {"mst", TZ
, NEG(42)}, /* Mountain Standard Time */
198 {"mt", TZ
, 51}, /* Moluccas Time */
199 {"ndt", DTZ
, NEG(15)}, /* Nfld. Daylight Time */
200 {"nft", TZ
, NEG(21)}, /* Newfoundland Standard Time */
201 {"nor", TZ
, 6}, /* Norway Standard Time */
203 {"november", MONTH
, 11},
204 {NOW
, RESERV
, DTK_NOW
}, /* current transaction time */
205 {"nst", TZ
, NEG(21)}, /* Nfld. Standard Time */
206 {"nt", TZ
, NEG(66)}, /* Nome Time */
207 {"nzdt", DTZ
, 78}, /* New Zealand Daylight Time */
208 {"nzst", TZ
, 72}, /* New Zealand Standard Time */
209 {"nzt", TZ
, 72}, /* New Zealand Time */
211 {"october", MONTH
, 10},
212 {"on", IGNOREFIELD
, 0}, /* "on" (throwaway) */
213 {"pdt", DTZ
, NEG(42)}, /* Pacific Daylight Time */
215 {"pst", TZ
, NEG(48)}, /* Pacific Standard Time */
216 {"sadt", DTZ
, 63}, /* S. Australian Dayl. Time */
217 {"sast", TZ
, 57}, /* South Australian Std Time */
219 {"saturday", DOW
, 6},
222 {"september", MONTH
, 9},
223 {"set", TZ
, NEG(6)}, /* Seychelles Time ?? */
224 {"sst", DTZ
, 12}, /* Swedish Summer Time */
227 {"swt", TZ
, 6}, /* Swedish Winter Time */
231 {"thursday", DOW
, 4},
232 {TODAY
, RESERV
, DTK_TODAY
}, /* midnight */
233 {TOMORROW
, RESERV
, DTK_TOMORROW
}, /* tomorrow midnight */
237 {"undefined", RESERV
, DTK_INVALID
}, /* "undefined" pre-v6.1 invalid
241 {"wadt", DTZ
, 48}, /* West Australian DST */
242 {"wast", TZ
, 42}, /* West Australian Std Time */
243 {"wat", TZ
, NEG(6)}, /* West Africa Time */
244 {"wdt", DTZ
, 54}, /* West Australian DST */
246 {"wednesday", DOW
, 3},
248 {"wet", TZ
, 0}, /* Western Europe */
249 {"wetdst", DTZ
, 6}, /* Western Europe */
250 {"wst", TZ
, 48}, /* West Australian Std Time */
251 {"ydt", DTZ
, NEG(48)}, /* Yukon Daylight Time */
252 {YESTERDAY
, RESERV
, DTK_YESTERDAY
}, /* yesterday midnight */
253 {"yst", TZ
, NEG(54)}, /* Yukon Standard Time */
254 {"zp4", TZ
, NEG(24)}, /* GMT +4 hours. */
255 {"zp5", TZ
, NEG(30)}, /* GMT +5 hours. */
256 {"zp6", TZ
, NEG(36)}, /* GMT +6 hours. */
257 {"z", RESERV
, DTK_ZULU
}, /* 00:00:00 */
258 {ZULU
, RESERV
, DTK_ZULU
}, /* 00:00:00 */
261 static unsigned int szdatetktbl
= sizeof datetktbl
/ sizeof datetktbl
[0];
266 datetkn
*datecache
[MAXDATEFIELDS
] = {NULL
};
268 datetkn
*deltacache
[MAXDATEFIELDS
] = {NULL
};
274 * Calendar time to Julian date conversions.
275 * Julian date is commonly used in astronomical applications,
276 * since it is numerically accurate and computationally simple.
277 * The algorithms here will accurately convert between Julian day
278 * and calendar date for all non-negative Julian days
279 * (i.e. from Nov 23, -4713 on).
281 * Ref: Explanatory Supplement to the Astronomical Almanac, 1992.
282 * University Science Books, 20 Edgehill Rd. Mill Valley CA 94941.
284 * Use the algorithm by Henry Fliegel, a former NASA/JPL colleague
285 * now at Aerospace Corp. (hi, Henry!)
287 * These routines will be used by other date/time packages - tgl 97/02/25
290 /* Set the minimum year to one greater than the year of the first valid day
291 * to avoid having to check year and day both. - tgl 97/05/08
294 #define JULIAN_MINYEAR (-4713)
295 #define JULIAN_MINMONTH (11)
296 #define JULIAN_MINDAY (23)
298 #define IS_VALID_JULIAN(y,m,d) ((y > JULIAN_MINYEAR) \
299 || ((y == JULIAN_MINYEAR) && ((m > JULIAN_MINMONTH) \
300 || ((m == JULIAN_MINMONTH) && (d >= JULIAN_MINDAY)))))
303 date2j(int y
, int m
, int d
)
305 int m12
= (m
- 14) / 12;
307 return ((1461 * (y
+ 4800 + m12
)) / 4 + (367 * (m
- 2 - 12 * (m12
))) / 12
308 - (3 * ((y
+ 4900 + m12
) / 100)) / 4 + d
- 32075);
312 j2date(int jd
, int *year
, int *month
, int *day
)
324 n
= (4 * l
) / 146097;
325 l
-= (146097 * n
+ 3) / 4;
326 i
= (4000 * (l
+ 1)) / 1461001;
327 l
+= 31 - (1461 * i
) / 4;
329 d
= l
- (2447 * j
) / 80;
331 m
= (j
+ 2) - (12 * l
);
332 y
= 100 * (n
- 49) + i
+ l
;
344 * parse and convert date in timestr (the normal interface)
346 * Returns the number of seconds since epoch (J2000)
350 * Break string into tokens based on a date/time context.
353 ParseDateTime(char *timestr
, char *lowstr
,
354 char **field
, int *ftype
, int maxfields
, int *numfields
)
361 printf("ParseDateTime- input string is %s\n", timestr
);
363 /* outer loop through fields */
368 /* leading digit? then date or time */
369 if (isdigit(*cp
) || (*cp
== '.'))
377 ftype
[nf
] = DTK_TIME
;
378 while (isdigit(*cp
) || (*cp
== ':') || (*cp
== '.'))
382 /* date field? allow embedded text month */
383 else if ((*cp
== '-') || (*cp
== '/') || (*cp
== '.'))
385 ftype
[nf
] = DTK_DATE
;
386 while (isalnum(*cp
) || (*cp
== '-') || (*cp
== '/') || (*cp
== '.'))
387 *lp
++ = tolower(*cp
++);
392 * otherwise, number only and will determine year, month, or
396 ftype
[nf
] = DTK_NUMBER
;
401 * text? then date string, month, day of week, special, or
404 else if (isalpha(*cp
))
406 ftype
[nf
] = DTK_STRING
;
407 *lp
++ = tolower(*cp
++);
409 *lp
++ = tolower(*cp
++);
411 /* full date string with leading text month? */
412 if ((*cp
== '-') || (*cp
== '/') || (*cp
== '.'))
414 ftype
[nf
] = DTK_DATE
;
415 while (isdigit(*cp
) || (*cp
== '-') || (*cp
== '/') || (*cp
== '.'))
416 *lp
++ = tolower(*cp
++);
419 /* skip leading spaces */
421 else if (isspace(*cp
))
426 /* sign? then special or numeric timezone */
428 else if ((*cp
== '+') || (*cp
== '-'))
431 /* soak up leading whitespace */
434 /* numeric timezone? */
439 while (isdigit(*cp
) || (*cp
== ':'))
444 else if (isalpha(*cp
))
446 ftype
[nf
] = DTK_SPECIAL
;
447 *lp
++ = tolower(*cp
++);
449 *lp
++ = tolower(*cp
++);
451 /* otherwise something wrong... */
456 /* ignore punctuation but use as delimiter */
458 else if (ispunct(*cp
))
467 /* force in a delimiter */
470 if (nf
> MAXDATEFIELDS
)
473 printf("ParseDateTime- set field[%d] to %s type %d\n", (nf
- 1), field
[nf
- 1], ftype
[nf
- 1]);
480 } /* ParseDateTime() */
484 * Interpret previously parsed fields for general date and time.
485 * Return 0 if full date, 1 if only time, and -1 if problems.
486 * External format(s):
487 * "<weekday> <month>-<day>-<year> <hour>:<minute>:<second>"
488 * "Fri Feb-7-1997 15:23:27"
489 * "Feb-7-1997 15:23:27"
490 * "2-7-1997 15:23:27"
491 * "1997-2-7 15:23:27"
492 * "1997.038 15:23:27" (day of year 1-366)
493 * Also supports input in compact time:
497 * Use the system-provided functions to get the current time zone
498 * if not specified in the input string.
499 * If the date is outside the time_t system-supported time range,
500 * then assume GMT time zone. - tgl 97/05/27
503 DecodeDateTime(char **field
, int *ftype
, int nf
,
504 int *dtype
, struct tm
* tm
, double *fsec
, int *tzp
)
520 tm
->tm_isdst
= -1; /* don't know daylight savings time status
525 for (i
= 0; i
< nf
; i
++)
528 printf("DecodeDateTime- field[%d] is %s (type %d)\n", i
, field
[i
], ftype
[i
]);
533 if (DecodeDate(field
[i
], fmask
, &tmask
, tm
) != 0)
538 if (DecodeTime(field
[i
], fmask
, &tmask
, tm
, fsec
) != 0)
542 * check upper limit on hours; other limits checked in
545 if (tm
->tm_hour
> 23)
552 if (DecodeTimezone(field
[i
], tzp
) != 0)
558 flen
= strlen(field
[i
]);
562 if (DecodeNumberField(flen
, field
[i
], fmask
, &tmask
, tm
, fsec
) != 0)
568 if (DecodeNumber(flen
, field
[i
], fmask
, &tmask
, tm
, fsec
) != 0)
575 type
= DecodeSpecial(i
, field
[i
], &val
);
577 printf("DecodeDateTime- special field[%d] %s type=%d value=%d\n", i
, field
[i
], type
, val
);
579 if (type
== IGNOREFIELD
)
587 printf("DecodeDateTime- RESERV field %s value is %d\n", field
[i
], val
);
600 printf("DecodeDateTime- month field %s value is %d\n", field
[i
], val
);
606 * daylight savings time modifier (solves "MET
620 * set mask for TZ here _or_ check for DTZ later
621 * when getting default timezone
662 printf("DecodeDateTime- field[%d] %s (%08x/%08x) value is %d\n",
663 i
, field
[i
], fmask
, tmask
, val
);
671 /* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */
673 tm
->tm_year
= -(tm
->tm_year
- 1);
675 if ((mer
!= HR24
) && (tm
->tm_hour
> 12))
677 if ((mer
== AM
) && (tm
->tm_hour
== 12))
679 else if ((mer
== PM
) && (tm
->tm_hour
!= 12))
683 printf("DecodeDateTime- mask %08x (%08x)", fmask
, DTK_DATE_M
);
684 printf(" set y%04d m%02d d%02d", tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
);
685 printf(" %02d:%02d:%02d\n", tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
);
688 if ((*dtype
== DTK_DATE
) && ((fmask
& DTK_DATE_M
) != DTK_DATE_M
))
689 return ((fmask
& DTK_TIME_M
) == DTK_TIME_M
) ? 1 : -1;
691 /* timezone not specified? then find local timezone if possible */
692 if ((*dtype
== DTK_DATE
) && ((fmask
& DTK_DATE_M
) == DTK_DATE_M
)
693 && (tzp
!= NULL
) && (!(fmask
& DTK_M(TZ
))))
697 * daylight savings time modifier but no standard timezone? then
700 if (fmask
& DTK_M(DTZMOD
))
703 if (IS_VALID_UTIME(tm
->tm_year
, tm
->tm_mon
, tm
->tm_mday
))
705 #ifdef USE_POSIX_TIME
713 #ifdef HAVE_INT_TIMEZONE
714 *tzp
= ((tm
->tm_isdst
> 0) ? (timezone
- 3600) : timezone
);
716 #else /* !HAVE_INT_TIMEZONE */
717 *tzp
= -(tm
->tm_gmtoff
); /* tm_gmtoff is Sun/DEC-ism */
720 #else /* !USE_POSIX_TIME */
732 } /* DecodeDateTime() */
736 * Interpret parsed string as time fields only.
739 DecodeTimeOnly(char **field
, int *ftype
, int nf
, int *dtype
, struct tm
* tm
, double *fsec
)
753 tm
->tm_isdst
= -1; /* don't know daylight savings time status
759 for (i
= 0; i
< nf
; i
++)
762 printf("DecodeTimeOnly- field[%d] is %s (type %d)\n", i
, field
[i
], ftype
[i
]);
767 if (DecodeTime(field
[i
], fmask
, &tmask
, tm
, fsec
) != 0)
772 flen
= strlen(field
[i
]);
774 if (DecodeNumberField(flen
, field
[i
], fmask
, &tmask
, tm
, fsec
) != 0)
780 type
= DecodeSpecial(i
, field
[i
], &val
);
782 printf("DecodeTimeOnly- special field[%d] %s type=%d value=%d\n", i
, field
[i
], type
, val
);
784 if (type
== IGNOREFIELD
)
792 printf("DecodeTimeOnly- RESERV field %s value is %d\n", field
[i
], val
);
824 printf("DecodeTimeOnly- field[%d] %s value is %d\n", i
, field
[i
], val
);
829 printf("DecodeTimeOnly- mask %08x (%08x)", fmask
, DTK_TIME_M
);
830 printf(" %02d:%02d:%02d (%f)\n", tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
, *fsec
);
833 if ((mer
!= HR24
) && (tm
->tm_hour
> 12))
835 if ((mer
== AM
) && (tm
->tm_hour
== 12))
837 else if ((mer
== PM
) && (tm
->tm_hour
!= 12))
840 if ((fmask
& DTK_TIME_M
) != DTK_TIME_M
)
844 } /* DecodeTimeOnly() */
848 * Decode date string which includes delimiters.
849 * Insist on a complete set of fields.
852 DecodeDate(char *str
, int fmask
, int *tmask
, struct tm
* tm
)
862 char *field
[MAXDATEFIELDS
];
864 /* parse this string... */
865 while ((*str
!= '\0') && (nf
< MAXDATEFIELDS
))
867 /* skip field separators */
868 while (!isalnum(*str
))
874 while (isdigit(*str
))
877 else if (isalpha(*str
))
879 while (isalpha(*str
))
888 /* don't allow too many fields */
894 /* look first for text fields, since that will be unambiguous month */
895 for (i
= 0; i
< nf
; i
++)
897 if (isalpha(*field
[i
]))
899 type
= DecodeSpecial(i
, field
[i
], &val
);
900 if (type
== IGNOREFIELD
)
908 printf("DecodeDate- month field %s value is %d\n", field
[i
], val
);
915 printf("DecodeDate- illegal field %s value is %d\n", field
[i
], val
);
925 /* mark this field as being completed */
930 /* now pick up remaining numeric fields */
931 for (i
= 0; i
< nf
; i
++)
933 if (field
[i
] == NULL
)
936 if ((len
= strlen(field
[i
])) <= 0)
939 if (DecodeNumber(len
, field
[i
], fmask
, &dmask
, tm
, &fsec
) != 0)
954 * Decode time string which includes delimiters.
955 * Only check the lower limit on hours, since this same code
956 * can be used to represent time spans.
959 DecodeTime(char *str
, int fmask
, int *tmask
, struct tm
* tm
, double *fsec
)
965 tm
->tm_hour
= strtol(str
, &cp
, 10);
969 tm
->tm_min
= strtol(str
, &cp
, 10);
984 tm
->tm_sec
= strtol(str
, &cp
, 10);
990 *fsec
= strtod(str
, &cp
);
998 /* do a sanity check */
999 if ((tm
->tm_hour
< 0)
1000 || (tm
->tm_min
< 0) || (tm
->tm_min
> 59)
1001 || (tm
->tm_sec
< 0) || (tm
->tm_sec
> 59))
1005 } /* DecodeTime() */
1009 * Interpret numeric field as a date value in context.
1012 DecodeNumber(int flen
, char *str
, int fmask
, int *tmask
, struct tm
* tm
, double *fsec
)
1019 val
= strtol(str
, &cp
, 10);
1024 *fsec
= strtod(cp
, &cp
);
1030 printf("DecodeNumber- %s is %d fmask=%08x tmask=%08x\n", str
, val
, fmask
, *tmask
);
1033 /* enough digits to be unequivocal year? */
1037 printf("DecodeNumber- match %d (%s) as year\n", val
, str
);
1039 *tmask
= DTK_M(YEAR
);
1041 /* already have a year? then see if we can substitute... */
1042 if (fmask
& DTK_M(YEAR
))
1044 if ((!(fmask
& DTK_M(DAY
)))
1045 && ((tm
->tm_year
>= 1) && (tm
->tm_year
<= 31)))
1048 printf("DecodeNumber- misidentified year previously; swap with day %d\n", tm
->tm_mday
);
1050 tm
->tm_mday
= tm
->tm_year
;
1051 *tmask
= DTK_M(DAY
);
1057 /* special case day of year? */
1059 else if ((flen
== 3) && (fmask
& DTK_M(YEAR
))
1060 && ((val
>= 1) && (val
<= 366)))
1062 *tmask
= (DTK_M(DOY
) | DTK_M(MONTH
) | DTK_M(DAY
));
1064 j2date((date2j(tm
->tm_year
, 1, 1) + tm
->tm_yday
- 1),
1065 &tm
->tm_year
, &tm
->tm_mon
, &tm
->tm_mday
);
1067 /* already have year? then could be month */
1069 else if ((fmask
& DTK_M(YEAR
)) && (!(fmask
& DTK_M(MONTH
)))
1070 && ((val
>= 1) && (val
<= 12)))
1073 printf("DecodeNumber- match %d (%s) as month\n", val
, str
);
1075 *tmask
= DTK_M(MONTH
);
1078 /* no year and EuroDates enabled? then could be day */
1080 else if ((EuroDates
|| (fmask
& DTK_M(MONTH
)))
1081 && (!(fmask
& DTK_M(YEAR
)) && !(fmask
& DTK_M(DAY
)))
1082 && ((val
>= 1) && (val
<= 31)))
1085 printf("DecodeNumber- match %d (%s) as day\n", val
, str
);
1087 *tmask
= DTK_M(DAY
);
1091 else if ((!(fmask
& DTK_M(MONTH
)))
1092 && ((val
>= 1) && (val
<= 12)))
1095 printf("DecodeNumber- (2) match %d (%s) as month\n", val
, str
);
1097 *tmask
= DTK_M(MONTH
);
1101 else if ((!(fmask
& DTK_M(DAY
)))
1102 && ((val
>= 1) && (val
<= 31)))
1105 printf("DecodeNumber- (2) match %d (%s) as day\n", val
, str
);
1107 *tmask
= DTK_M(DAY
);
1111 else if (!(fmask
& DTK_M(YEAR
)))
1114 printf("DecodeNumber- (2) match %d (%s) as year\n", val
, str
);
1116 *tmask
= DTK_M(YEAR
);
1118 if (tm
->tm_year
< 70)
1119 tm
->tm_year
+= 2000;
1120 else if (tm
->tm_year
< 100)
1121 tm
->tm_year
+= 1900;
1128 } /* DecodeNumber() */
1131 /* DecodeNumberField()
1132 * Interpret numeric string as a concatenated date field.
1135 DecodeNumberField(int len
, char *str
, int fmask
, int *tmask
, struct tm
* tm
, double *fsec
)
1143 printf("DecodeNumberField- %s is 8 character date fmask=%08x tmask=%08x\n", str
, fmask
, *tmask
);
1146 *tmask
= DTK_DATE_M
;
1148 tm
->tm_mday
= atoi(str
+ 6);
1150 tm
->tm_mon
= atoi(str
+ 4);
1152 tm
->tm_year
= atoi(str
+ 0);
1154 /* yymmdd or hhmmss? */
1159 printf("DecodeNumberField- %s is 6 characters fmask=%08x tmask=%08x\n", str
, fmask
, *tmask
);
1161 if (fmask
& DTK_DATE_M
)
1164 printf("DecodeNumberField- %s is time field fmask=%08x tmask=%08x\n", str
, fmask
, *tmask
);
1166 *tmask
= DTK_TIME_M
;
1167 tm
->tm_sec
= atoi(str
+ 4);
1169 tm
->tm_min
= atoi(str
+ 2);
1171 tm
->tm_hour
= atoi(str
+ 0);
1177 printf("DecodeNumberField- %s is date field fmask=%08x tmask=%08x\n", str
, fmask
, *tmask
);
1179 *tmask
= DTK_DATE_M
;
1180 tm
->tm_mday
= atoi(str
+ 4);
1182 tm
->tm_mon
= atoi(str
+ 2);
1184 tm
->tm_year
= atoi(str
+ 0);
1188 else if (strchr(str
, '.') != NULL
)
1191 printf("DecodeNumberField- %s is time field fmask=%08x tmask=%08x\n", str
, fmask
, *tmask
);
1193 *tmask
= DTK_TIME_M
;
1194 tm
->tm_sec
= strtod((str
+ 4), &cp
);
1195 if (cp
== (str
+ 4))
1198 *fsec
= strtod(cp
, NULL
);
1200 tm
->tm_min
= strtod((str
+ 2), &cp
);
1202 tm
->tm_hour
= strtod((str
+ 0), &cp
);
1209 } /* DecodeNumberField() */
1213 * Interpret string as a numeric timezone.
1216 DecodeTimezone(char *str
, int *tzp
)
1224 /* assume leading character is "+" or "-" */
1225 hr
= strtol((str
+ 1), &cp
, 10);
1227 /* explicit delimiter? */
1230 min
= strtol((cp
+ 1), &cp
, 10);
1232 /* otherwise, might have run things together... */
1234 else if ((*cp
== '\0') && ((len
= strlen(str
)) > 3))
1236 min
= strtol((str
+ len
- 2), &cp
, 10);
1237 *(str
+ len
- 2) = '\0';
1238 hr
= strtol((str
+ 1), &cp
, 10);
1244 tz
= (hr
* 60 + min
) * 60;
1250 } /* DecodeTimezone() */
1254 * Decode text string using lookup table.
1255 * Implement a cache lookup since it is likely that dates
1256 * will be related in format.
1259 DecodeSpecial(int field
, char *lowtoken
, int *val
)
1265 if ((datecache
[field
] != NULL
)
1266 && (strncmp(lowtoken
, datecache
[field
]->token
, TOKMAXLEN
) == 0))
1267 tp
= datecache
[field
];
1271 tp
= datebsearch(lowtoken
, datetktbl
, szdatetktbl
);
1274 datecache
[field
] = tp
;
1299 } /* DecodeSpecial() */
1304 * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
1305 * is WAY faster than the generic bsearch().
1308 datebsearch(char *key
, datetkn
*base
, unsigned int nel
)
1310 datetkn
*last
= base
+ nel
- 1,
1314 while (last
>= base
)
1316 position
= base
+ ((last
- base
) >> 1);
1317 result
= key
[0] - position
->token
[0];
1320 result
= strncmp(key
, position
->token
, TOKMAXLEN
);
1325 last
= position
- 1;
1327 base
= position
+ 1;