aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/nabstime.c
diff options
context:
space:
mode:
authorMarc G. Fournier <scrappy@hub.org>1996-07-09 06:22:35 +0000
committerMarc G. Fournier <scrappy@hub.org>1996-07-09 06:22:35 +0000
commitd31084e9d1118b25fd16580d9d8c2924b5740dff (patch)
tree3179e66307d54df9c7b966543550e601eb55e668 /src/backend/utils/adt/nabstime.c
downloadpostgresql-PG95-1_01.tar.gz
postgresql-PG95-1_01.zip
Postgres95 1.01 Distribution - Virgin SourcesPG95-1_01
Diffstat (limited to 'src/backend/utils/adt/nabstime.c')
-rw-r--r--src/backend/utils/adt/nabstime.c866
1 files changed, 866 insertions, 0 deletions
diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c
new file mode 100644
index 00000000000..fa66ff5fa40
--- /dev/null
+++ b/src/backend/utils/adt/nabstime.c
@@ -0,0 +1,866 @@
+/*-------------------------------------------------------------------------
+ *
+ * nabstime.c--
+ * parse almost any absolute date getdate(3) can (& some it can't)
+ *
+ * Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include "postgres.h"
+#include "access/xact.h"
+#include "utils/nabstime.h"
+#include "utils/palloc.h"
+
+#define MAXDATEFIELDS 25
+
+#define ISSPACE(c) ((c) == ' ' || (c) == '\n' || (c) == '\t')
+
+/* this is fast but dirty. note the return's in the middle. */
+#define GOBBLE_NUM(cp, c, x, ip) \
+ (c) = *(cp)++; \
+ if ((c) < '0' || (c) > '9') \
+ return -1; /* missing digit */ \
+ (x) = (c) - '0'; \
+ (c) = *(cp)++; \
+ if ((c) >= '0' && (c) <= '9') { \
+ (x) = 10*(x) + (c) - '0'; \
+ (c) = *(cp)++; \
+ } \
+ if ((c) != ':' && (c) != '\0' && !ISSPACE(c)) \
+ return -1; /* missing colon */ \
+ *(ip) = (x) /* N.B.: no semi-colon here */
+
+#define EPOCH 1970
+#define DAYS_PER_400YRS (time_t)146097
+#define DAYS_PER_4YRS (time_t)1461
+#define SECS_PER_DAY 86400
+#define SECS_PER_HOUR 3600
+#define DIVBY4(n) ((n) >> 2)
+#define YRNUM(c, y) (DIVBY4(DAYS_PER_400YRS*(c)) + DIVBY4(DAYS_PER_4YRS*(y)))
+#define DAYNUM(c,y,mon,d) (YRNUM((c), (y)) + mdays[mon] + (d))
+#define EPOCH_DAYNUM DAYNUM(19, 69, 10, 1) /* really January 1, 1970 */
+#define MIN_DAYNUM -24856 /* December 13, 1901 */
+#define MAX_DAYNUM 24854 /* January 18, 2038 */
+
+/* definitions for squeezing values into "value" */
+#define ABS_SIGNBIT 0200
+#define VALMASK 0177
+#define NEG(n) ((n)|ABS_SIGNBIT)
+#define SIGNEDCHAR(c) ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))
+#define FROMVAL(tp) (-SIGNEDCHAR((tp)->value) * 10) /* uncompress */
+#define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/10): (v)/10))
+#define IsLeapYear(yr) ((yr%4) == 0)
+
+char nmdays[] = {
+ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+/* days since start of year. mdays[0] is March, mdays[11] is February */
+static short mdays[] = {
+ 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337
+};
+
+/* exports */
+static int dtok_numparsed;
+
+/*
+ * to keep this table reasonably small, we divide the lexval for TZ and DTZ
+ * entries by 10 and truncate the text field at MAXTOKLEN characters.
+ * the text field is not guaranteed to be NUL-terminated.
+ */
+static datetkn datetktbl[] = {
+/* text token lexval */
+{ "acsst", DTZ, 63}, /* Cent. Australia */
+{ "acst", TZ, 57}, /* Cent. Australia */
+{ "adt", DTZ, NEG(18)}, /* Atlantic Daylight Time */
+{ "aesst", DTZ, 66}, /* E. Australia */
+{ "aest", TZ, 60}, /* Australia Eastern Std Time */
+{ "ahst", TZ, 60}, /* Alaska-Hawaii Std Time */
+{ "am", AMPM, AM},
+{ "apr", MONTH, 4},
+{ "april", MONTH, 4},
+{ "ast", TZ, NEG(24)}, /* Atlantic Std Time (Canada) */
+{ "at", PG_IGNORE, 0}, /* "at" (throwaway) */
+{ "aug", MONTH, 8},
+{ "august", MONTH, 8},
+{ "awsst", DTZ, 54}, /* W. Australia */
+{ "awst", TZ, 48}, /* W. Australia */
+{ "bst", TZ, 6}, /* British Summer Time */
+{ "bt", TZ, 18}, /* Baghdad Time */
+{ "cadt", DTZ, 63}, /* Central Australian DST */
+{ "cast", TZ, 57}, /* Central Australian ST */
+{ "cat", TZ, NEG(60)}, /* Central Alaska Time */
+{ "cct", TZ, 48}, /* China Coast */
+{ "cdt", DTZ, NEG(30)}, /* Central Daylight Time */
+{ "cet", TZ, 6}, /* Central European Time */
+{ "cetdst", DTZ, 12}, /* Central European Dayl.Time */
+{ "cst", TZ, NEG(36)}, /* Central Standard Time */
+{ "dec", MONTH, 12},
+{ "decemb", MONTH, 12},
+{ "dnt", TZ, 6}, /* Dansk Normal Tid */
+{ "dst", PG_IGNORE, 0},
+{ "east", TZ, NEG(60)}, /* East Australian Std Time */
+{ "edt", DTZ, NEG(24)}, /* Eastern Daylight Time */
+{ "eet", TZ, 12}, /* East. Europe, USSR Zone 1 */
+{ "eetdst", DTZ, 18}, /* Eastern Europe */
+{ "est", TZ, NEG(30)}, /* Eastern Standard Time */
+{ "feb", MONTH, 2},
+{ "februa", MONTH, 2},
+{ "fri", PG_IGNORE, 5},
+{ "friday", PG_IGNORE, 5},
+{ "fst", TZ, 6}, /* French Summer Time */
+{ "fwt", DTZ, 12}, /* French Winter Time */
+{ "gmt", TZ, 0}, /* Greenwish Mean Time */
+{ "gst", TZ, 60}, /* Guam Std Time, USSR Zone 9 */
+{ "hdt", DTZ, NEG(54)}, /* Hawaii/Alaska */
+{ "hmt", DTZ, 18}, /* Hellas ? ? */
+{ "hst", TZ, NEG(60)}, /* Hawaii Std Time */
+{ "idle", TZ, 72}, /* Intl. Date Line, East */
+{ "idlw", TZ, NEG(72)}, /* Intl. Date Line, West */
+{ "ist", TZ, 12}, /* Israel */
+{ "it", TZ, 22}, /* Iran Time */
+{ "jan", MONTH, 1},
+{ "januar", MONTH, 1},
+{ "jst", TZ, 54}, /* Japan Std Time,USSR Zone 8 */
+{ "jt", TZ, 45}, /* Java Time */
+{ "jul", MONTH, 7},
+{ "july", MONTH, 7},
+{ "jun", MONTH, 6},
+{ "june", MONTH, 6},
+{ "kst", TZ, 54}, /* Korea Standard Time */
+{ "ligt", TZ, 60}, /* From Melbourne, Australia */
+{ "mar", MONTH, 3},
+{ "march", MONTH, 3},
+{ "may", MONTH, 5},
+{ "mdt", DTZ, NEG(36)}, /* Mountain Daylight Time */
+{ "mest", DTZ, 12}, /* Middle Europe Summer Time */
+{ "met", TZ, 6}, /* Middle Europe Time */
+{ "metdst", DTZ, 12}, /* Middle Europe Daylight Time*/
+{ "mewt", TZ, 6}, /* Middle Europe Winter Time */
+{ "mez", TZ, 6}, /* Middle Europe Zone */
+{ "mon", PG_IGNORE, 1},
+{ "monday", PG_IGNORE, 1},
+{ "mst", TZ, NEG(42)}, /* Mountain Standard Time */
+{ "mt", TZ, 51}, /* Moluccas Time */
+{ "ndt", DTZ, NEG(15)}, /* Nfld. Daylight Time */
+{ "nft", TZ, NEG(21)}, /* Newfoundland Standard Time */
+{ "nor", TZ, 6}, /* Norway Standard Time */
+{ "nov", MONTH, 11},
+{ "novemb", MONTH, 11},
+{ "nst", TZ, NEG(21)}, /* Nfld. Standard Time */
+{ "nt", TZ, NEG(66)}, /* Nome Time */
+{ "nzdt", DTZ, 78}, /* New Zealand Daylight Time */
+{ "nzst", TZ, 72}, /* New Zealand Standard Time */
+{ "nzt", TZ, 72}, /* New Zealand Time */
+{ "oct", MONTH, 10},
+{ "octobe", MONTH, 10},
+{ "on", PG_IGNORE, 0}, /* "on" (throwaway) */
+{ "pdt", DTZ, NEG(42)}, /* Pacific Daylight Time */
+{ "pm", AMPM, PM},
+{ "pst", TZ, NEG(48)}, /* Pacific Standard Time */
+{ "sadt", DTZ, 63}, /* S. Australian Dayl. Time */
+{ "sast", TZ, 57}, /* South Australian Std Time */
+{ "sat", PG_IGNORE, 6},
+{ "saturd", PG_IGNORE, 6},
+{ "sep", MONTH, 9},
+{ "sept", MONTH, 9},
+{ "septem", MONTH, 9},
+{ "set", TZ, NEG(6)}, /* Seychelles Time ?? */
+{ "sst", DTZ, 12}, /* Swedish Summer Time */
+{ "sun", PG_IGNORE, 0},
+{ "sunday", PG_IGNORE, 0},
+{ "swt", TZ, 6}, /* Swedish Winter Time */
+{ "thu", PG_IGNORE, 4},
+{ "thur", PG_IGNORE, 4},
+{ "thurs", PG_IGNORE, 4},
+{ "thursd", PG_IGNORE, 4},
+{ "tue", PG_IGNORE, 2},
+{ "tues", PG_IGNORE, 2},
+{ "tuesda", PG_IGNORE, 2},
+{ "ut", TZ, 0},
+{ "utc", TZ, 0},
+{ "wadt", DTZ, 48}, /* West Australian DST */
+{ "wast", TZ, 42}, /* West Australian Std Time */
+{ "wat", TZ, NEG(6)}, /* West Africa Time */
+{ "wdt", DTZ, 54}, /* West Australian DST */
+{ "wed", PG_IGNORE, 3},
+{ "wednes", PG_IGNORE, 3},
+{ "weds", PG_IGNORE, 3},
+{ "wet", TZ, 0}, /* Western Europe */
+{ "wetdst", DTZ, 6}, /* Western Europe */
+{ "wst", TZ, 48}, /* West Australian Std Time */
+{ "ydt", DTZ, NEG(48)}, /* Yukon Daylight Time */
+{ "yst", TZ, NEG(54)}, /* Yukon Standard Time */
+{ "zp4", TZ, NEG(24)}, /* GMT +4 hours. */
+{ "zp5", TZ, NEG(30)}, /* GMT +5 hours. */
+{ "zp6", TZ, NEG(36)}, /* GMT +6 hours. */
+};
+
+static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];
+
+/*
+ * parse and convert absolute date in timestr (the normal interface)
+ *
+ * Returns the number of seconds since epoch (January 1 1970 GMT)
+ */
+AbsoluteTime
+nabstimein(char* timestr)
+{
+ int tz = 0;
+ struct tm date;
+
+ if (!timestr)
+ return INVALID_ABSTIME;
+ while (ISSPACE(*timestr))
+ ++timestr;
+
+ if (!strcasecmp(timestr, "epoch"))
+ return EPOCH_ABSTIME;
+ if (!strcasecmp(timestr, "now"))
+ return GetCurrentTransactionStartTime();
+ if (!strcasecmp(timestr, "current"))
+ return CURRENT_ABSTIME;
+ if (!strcasecmp(timestr, "infinity"))
+ return NOEND_ABSTIME;
+ if (!strcasecmp(timestr, "-infinity"))
+ return NOSTART_ABSTIME;
+ if (prsabsdate(timestr, &date, &tz) < 0)
+ return INVALID_ABSTIME;
+ return dateconv(&date, tz);
+}
+
+/*
+ * just parse the absolute date in timestr and get back a broken-out date.
+ */
+int
+prsabsdate(char *timestr,
+ struct tm *tm,
+ int *tzp) /* - minutes west */
+{
+ register int nf;
+ char *fields[MAXDATEFIELDS];
+ static char delims[] = "- \t\n/,";
+
+ nf = split(timestr, fields, MAXDATEFIELDS, delims+1);
+ if (nf > MAXDATEFIELDS)
+ return -1;
+ if (tryabsdate(fields, nf, tm, tzp) < 0) {
+ register char *p = timestr;
+
+ /*
+ * could be a DEC-date; glue it all back together, split it
+ * with dash as a delimiter and try again. Yes, this is a
+ * hack, but so are DEC-dates.
+ */
+ while (--nf > 0) {
+ while (*p++ != '\0')
+ ;
+ p[-1] = ' ';
+ }
+ nf = split(timestr, fields, MAXDATEFIELDS, delims);
+ if (nf > MAXDATEFIELDS)
+ return -1;
+ if (tryabsdate(fields, nf, tm, tzp) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * try to parse pre-split timestr as an absolute date
+ */
+int
+tryabsdate(char *fields[], int nf, struct tm *tm, int *tzp)
+{
+ register int i;
+ register datetkn *tp;
+ register long flg = 0, ty;
+ int mer = HR24, bigval = -1;
+#ifndef USE_POSIX_TIME
+ struct timeb now; /* the old V7-ism */
+
+ (void) ftime(&now);
+ *tzp = now.timezone;
+#else /* USE_POSIX_TIME */
+#if defined(PORTNAME_hpux) || \
+ defined(PORTNAME_aix) || \
+ defined(PORTNAME_irix5) || \
+ defined(WIN32) || \
+ defined(PORTNAME_sparc_solaris)
+ tzset();
+#ifndef WIN32
+ *tzp = timezone / 60; /* this is an X/Open-ism */
+#else
+ *tzp = _timezone / 60; /* this is an X/Open-ism */
+#endif /* WIN32 */
+#else /* PORTNAME_hpux || PORTNAME_aix || PORTNAME_sparc_solaris || PORTNAME_irix5 */
+ time_t now = time((time_t *) NULL);
+ struct tm *tmnow = localtime(&now);
+
+ *tzp = - tmnow->tm_gmtoff / 60; /* tm_gmtoff is Sun/DEC-ism */
+#endif /* PORTNAME_hpux || PORTNAME_aix */
+#endif /* USE_POSIX_TIME */
+
+ tm->tm_mday = tm->tm_mon = tm->tm_year = -1; /* mandatory */
+ tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
+ tm->tm_isdst = -1; /* assume we don't know. */
+ dtok_numparsed = 0;
+
+ for (i = 0; i < nf; i++) {
+ if (fields[i][0] == '\0')
+ continue;
+ tp = datetoktype(fields[i], &bigval);
+ ty = (1L << tp->type) & ~(1L << PG_IGNORE);
+ if (flg&ty)
+ return -1; /* repeated type */
+ flg |= ty;
+ switch (tp->type) {
+ case YEAR:
+ tm->tm_year = bigval;
+ break;
+ case DAY:
+ tm->tm_mday = bigval;
+ break;
+ case MONTH:
+ tm->tm_mon = tp->value;
+ break;
+ case TIME:
+ if (parsetime(fields[i], tm) < 0)
+ return -1;
+ break;
+ case DTZ:
+ tm->tm_isdst++;
+ /* FALLTHROUGH */
+ case TZ:
+ *tzp = FROMVAL(tp);
+ break;
+ case PG_IGNORE:
+ break;
+ case AMPM:
+ mer = tp->value;
+ break;
+ default:
+ return -1; /* bad token type: CANTHAPPEN */
+ }
+ }
+ if (tm->tm_year == -1 || tm->tm_mon == -1 || tm->tm_mday == -1)
+ return -1; /* missing component */
+ if (mer == PM)
+ tm->tm_hour += 12;
+ return 0;
+}
+
+
+/* return -1 on failure */
+int
+parsetime(char *time, struct tm *tm)
+{
+ register char c;
+ register int x;
+
+ tm->tm_sec = 0;
+ GOBBLE_NUM(time, c, x, &tm->tm_hour);
+ if (c != ':')
+ return -1; /* only hour; too short */
+ GOBBLE_NUM(time, c, x, &tm->tm_min);
+ if (c != ':')
+ return 0; /* no seconds; okay */
+ GOBBLE_NUM(time, c, x, &tm->tm_sec);
+ /* this may be considered too strict. garbage at end of time? */
+ return (c == '\0' || ISSPACE(c)? 0: -1);
+}
+
+
+/*
+ * split - divide a string into fields, like awk split()
+ */
+int /* number of fields, including overflow */
+split(char *string,
+ char *fields[], /* list is not NULL-terminated */
+ int nfields, /* number of entries available in fields[] */
+ char *sep) /* "" white, "c" single char, "ab" [ab]+ */
+{
+ register char *p = string;
+ register char c; /* latest character */
+ register char sepc = sep[0];
+ register char sepc2;
+ register int fn;
+ register char **fp = fields;
+ register char *sepp;
+ register int trimtrail;
+
+ /* white space */
+ if (sepc == '\0') {
+ while ((c = *p++) == ' ' || c == '\t')
+ continue;
+ p--;
+ trimtrail = 1;
+ sep = " \t"; /* note, code below knows this is 2 long */
+ sepc = ' ';
+ } else
+ trimtrail = 0;
+ sepc2 = sep[1]; /* now we can safely pick this up */
+
+ /* catch empties */
+ if (*p == '\0')
+ return(0);
+
+ /* single separator */
+ if (sepc2 == '\0') {
+ fn = nfields;
+ for (;;) {
+ *fp++ = p;
+ fn--;
+ if (fn == 0)
+ break;
+ while ((c = *p++) != sepc)
+ if (c == '\0')
+ return(nfields - fn);
+ *(p-1) = '\0';
+ }
+ /* we have overflowed the fields vector -- just count them */
+ fn = nfields;
+ for (;;) {
+ while ((c = *p++) != sepc)
+ if (c == '\0')
+ return(fn);
+ fn++;
+ }
+ /* not reached */
+ }
+
+ /* two separators */
+ if (sep[2] == '\0') {
+ fn = nfields;
+ for (;;) {
+ *fp++ = p;
+ fn--;
+ while ((c = *p++) != sepc && c != sepc2)
+ if (c == '\0') {
+ if (trimtrail && **(fp-1) == '\0')
+ fn++;
+ return(nfields - fn);
+ }
+ if (fn == 0)
+ break;
+ *(p-1) = '\0';
+ while ((c = *p++) == sepc || c == sepc2)
+ continue;
+ p--;
+ }
+ /* we have overflowed the fields vector -- just count them */
+ fn = nfields;
+ while (c != '\0') {
+ while ((c = *p++) == sepc || c == sepc2)
+ continue;
+ p--;
+ fn++;
+ while ((c = *p++) != '\0' && c != sepc && c != sepc2)
+ continue;
+ }
+ /* might have to trim trailing white space */
+ if (trimtrail) {
+ p--;
+ while ((c = *--p) == sepc || c == sepc2)
+ continue;
+ p++;
+ if (*p != '\0') {
+ if (fn == nfields+1)
+ *p = '\0';
+ fn--;
+ }
+ }
+ return(fn);
+ }
+
+ /* n separators */
+ fn = 0;
+ for (;;) {
+ if (fn < nfields)
+ *fp++ = p;
+ fn++;
+ for (;;) {
+ c = *p++;
+ if (c == '\0')
+ return(fn);
+ sepp = sep;
+ while ((sepc = *sepp++) != '\0' && sepc != c)
+ continue;
+ if (sepc != '\0') /* it was a separator */
+ break;
+ }
+ if (fn < nfields)
+ *(p-1) = '\0';
+ for (;;) {
+ c = *p++;
+ sepp = sep;
+ while ((sepc = *sepp++) != '\0' && sepc != c)
+ continue;
+ if (sepc == '\0') /* it wasn't a separator */
+ break;
+ }
+ p--;
+ }
+
+ /* not reached */
+}
+
+/*
+ * Given an AbsoluteTime return the English text version of the date
+ */
+char *
+nabstimeout(AbsoluteTime time)
+{
+ /*
+ * Fri Jan 28 23:05:29 1994 PST
+ * 0 1 2
+ * 12345678901234567890123456789
+ *
+ * we allocate some extra -- timezones are usually 3 characters but
+ * this is not in the POSIX standard...
+ */
+ char buf[40];
+ char* result;
+
+ switch (time) {
+ case EPOCH_ABSTIME: (void) strcpy(buf, "epoch"); break;
+ case INVALID_ABSTIME: (void) strcpy(buf, "Invalid Abstime"); break;
+ case CURRENT_ABSTIME: (void) strcpy(buf, "current"); break;
+ case NOEND_ABSTIME: (void) strcpy(buf, "infinity"); break;
+ case NOSTART_ABSTIME: (void) strcpy(buf, "-infinity"); break;
+ default:
+ /* hack -- localtime happens to work for negative times */
+ (void) strftime(buf, sizeof(buf), "%a %b %d %H:%M:%S %Y %Z",
+ localtime((time_t *) &time));
+ break;
+ }
+ result = (char*)palloc(strlen(buf) + 1);
+ strcpy(result, buf);
+ return result;
+}
+
+/* turn a (struct tm) and a few variables into a time_t, with range checking */
+AbsoluteTime
+dateconv(register struct tm *tm, int zone)
+{
+ tm->tm_wday = tm->tm_yday = 0;
+
+ /* validate, before going out of range on some members */
+ if (tm->tm_year < 0 || tm->tm_mon < 1 || tm->tm_mon > 12 ||
+ tm->tm_mday < 1 || tm->tm_hour < 0 || tm->tm_hour >= 24 ||
+ tm->tm_min < 0 || tm->tm_min > 59 ||
+ tm->tm_sec < 0 || tm->tm_sec > 59)
+ return -1;
+
+ /*
+ * zone should really be -zone, and tz should be set to tp->value, not
+ * -tp->value. Or the table could be fixed.
+ */
+ tm->tm_min += zone; /* mktime lets it be out of range */
+
+ /* convert to seconds */
+ return qmktime(tm);
+}
+
+
+/*
+ * near-ANSI qmktime suitable for use by dateconv; not necessarily as paranoid
+ * as ANSI requires, and it may not canonicalise the struct tm. Ignores tm_wday
+ * and tm_yday.
+ */
+time_t
+qmktime(struct tm *tp)
+{
+ register int mon = tp->tm_mon;
+ register int day = tp->tm_mday, year = tp->tm_year;
+ register time_t daynum;
+ time_t secondnum;
+ register int century;
+
+ /* If it was a 2 digit year */
+ if (year < 100)
+ year += 1900;
+
+ /*
+ * validate day against days-per-month table, with leap-year
+ * correction
+ */
+ if (day > nmdays[mon])
+ if (mon != 2 || year % 4 == 0 &&
+ (year % 100 != 0 || year % 400 == 0) && day > 29)
+ return -1; /* day too large for month */
+
+ /* split year into century and year-of-century */
+ century = year / 100;
+ year %= 100;
+ /*
+ * We calculate the day number exactly, assuming the calendar has
+ * always had the current leap year rules. (The leap year rules are
+ * to compensate for the fact that the Earth's revolution around the
+ * Sun takes 365.2425 days). We first need to rotate months so March
+ * is 0, since we want the last month to have the reduced number of
+ * days.
+ */
+ if (mon > 2)
+ mon -= 3;
+ else {
+ mon += 9;
+ if (year == 0) {
+ century--;
+ year = 99;
+ } else
+ --year;
+ }
+ daynum = -EPOCH_DAYNUM + DAYNUM(century, year, mon, day);
+
+ /* check for time out of range */
+ if (daynum < MIN_DAYNUM || daynum > MAX_DAYNUM)
+ return INVALID_ABSTIME;
+
+ /* convert to seconds */
+ secondnum =
+ tp->tm_sec + (tp->tm_min +(daynum*24 + tp->tm_hour)*60)*60;
+
+ /* check for overflow */
+ if ((daynum == MAX_DAYNUM && secondnum < 0) ||
+ (daynum == MIN_DAYNUM && secondnum > 0))
+ return INVALID_ABSTIME;
+
+ /* check for "current", "infinity", "-infinity" */
+ if (!AbsoluteTimeIsReal(secondnum))
+ return INVALID_ABSTIME;
+
+ /* daylight correction */
+ if (tp->tm_isdst < 0) /* unknown; find out */
+ {
+ struct tm *result;
+
+ /* NT returns NULL for any time before 1/1/70 */
+ result = localtime(&secondnum);
+ if (result == NULL)
+ return INVALID_ABSTIME;
+ else
+ tp->tm_isdst = result->tm_isdst;
+ }
+ if (tp->tm_isdst > 0)
+ secondnum -= 60*60;
+
+ return secondnum;
+}
+
+datetkn *
+datetoktype(char *s, int *bigvalp)
+{
+ register char *cp = s;
+ register char c = *cp;
+ static datetkn t;
+ register datetkn *tp = &t;
+
+ if (isascii(c) && isdigit(c)) {
+ register int len = strlen(cp);
+
+ if (len > 3 && (cp[1] == ':' || cp[2] == ':'))
+ tp->type = TIME;
+ else {
+ if (bigvalp != NULL)
+ /* won't fit in tp->value */
+ *bigvalp = atoi(cp);
+ if (len == 4)
+ tp->type = YEAR;
+ else if (++dtok_numparsed == 1)
+ tp->type = DAY;
+ else
+ tp->type = YEAR;
+ }
+ } else if (c == '-' || c == '+') {
+ register int val = atoi(cp + 1);
+ register int hr = val / 100;
+ register int min = val % 100;
+
+ val = hr*60 + min;
+ if (c == '-')
+ val = -val;
+ tp->type = TZ;
+ TOVAL(tp, val);
+ } else {
+ char lowtoken[TOKMAXLEN+1];
+ register char *ltp = lowtoken, *endltp = lowtoken+TOKMAXLEN;
+
+ /* copy to lowtoken to avoid modifying s */
+ while ((c = *cp++) != '\0' && ltp < endltp)
+ *ltp++ = (isascii(c) && isupper(c)? tolower(c): c);
+ *ltp = '\0';
+ tp = datebsearch(lowtoken, datetktbl, szdatetktbl);
+ if (tp == NULL) {
+ tp = &t;
+ tp->type = PG_IGNORE;
+ }
+ }
+ return tp;
+}
+
+/*
+ * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
+ * is WAY faster than the generic bsearch().
+ */
+datetkn *
+datebsearch(char *key, datetkn *base, unsigned int nel)
+{
+ register datetkn *last = base + nel - 1, *position;
+ register int result;
+
+ while (last >= base) {
+ position = base + ((last - base) >> 1);
+ result = key[0] - position->token[0];
+ if (result == 0) {
+ result = strncmp(key, position->token, TOKMAXLEN);
+ if (result == 0)
+ return position;
+ }
+ if (result < 0)
+ last = position - 1;
+ else
+ base = position + 1;
+ }
+ return 0;
+}
+
+
+/*
+ * AbsoluteTimeIsBefore -- true iff time1 is before time2.
+ */
+
+bool
+AbsoluteTimeIsBefore(AbsoluteTime time1, AbsoluteTime time2)
+{
+ AbsoluteTime tm = GetCurrentTransactionStartTime();
+
+ Assert(AbsoluteTimeIsValid(time1));
+ Assert(AbsoluteTimeIsValid(time2));
+
+ if ((time1 == CURRENT_ABSTIME) || (time2 == CURRENT_ABSTIME))
+ return false;
+ if (time1 == CURRENT_ABSTIME)
+ return (tm < time2);
+ if (time2 == CURRENT_ABSTIME)
+ return (time1 < tm);
+
+ return (time1 < time2);
+}
+
+bool
+AbsoluteTimeIsAfter(AbsoluteTime time1, AbsoluteTime time2)
+{
+ AbsoluteTime tm = GetCurrentTransactionStartTime();
+
+ Assert(AbsoluteTimeIsValid(time1));
+ Assert(AbsoluteTimeIsValid(time2));
+
+ if ((time1 == CURRENT_ABSTIME) || (time2 == CURRENT_ABSTIME))
+ return false;
+ if (time1 == CURRENT_ABSTIME)
+ return (tm > time2);
+ if (time2 == CURRENT_ABSTIME)
+ return (time1 > tm);
+
+ return (time1 > time2);
+}
+
+
+/*
+ * abstimeeq - returns 1, iff arguments are equal
+ * abstimene - returns 1, iff arguments are not equal
+ * abstimelt - returns 1, iff t1 less than t2
+ * abstimegt - returns 1, iff t1 greater than t2
+ * abstimele - returns 1, iff t1 less than or equal to t2
+ * abstimege - returns 1, iff t1 greater than or equal to t2
+ *
+ */
+int32
+abstimeeq(AbsoluteTime t1, AbsoluteTime t2)
+{
+ if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
+ return 0;
+ if (t1 == CURRENT_ABSTIME)
+ t1 = GetCurrentTransactionStartTime();
+ if (t2 == CURRENT_ABSTIME)
+ t2 = GetCurrentTransactionStartTime();
+
+ return(t1 == t2);
+}
+
+int32
+abstimene(AbsoluteTime t1, AbsoluteTime t2)
+{
+ if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
+ return 0;
+ if (t1 == CURRENT_ABSTIME)
+ t1 = GetCurrentTransactionStartTime();
+ if (t2 == CURRENT_ABSTIME)
+ t2 = GetCurrentTransactionStartTime();
+
+ return(t1 != t2);
+}
+
+int32
+abstimelt(AbsoluteTime t1, AbsoluteTime t2)
+{
+ if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
+ return 0;
+ if (t1 == CURRENT_ABSTIME)
+ t1 = GetCurrentTransactionStartTime();
+ if (t2 == CURRENT_ABSTIME)
+ t2 = GetCurrentTransactionStartTime();
+
+ return(t1 < t2);
+}
+
+int32
+abstimegt(AbsoluteTime t1, AbsoluteTime t2)
+{
+ if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
+ return 0;
+ if (t1 == CURRENT_ABSTIME)
+ t1 = GetCurrentTransactionStartTime();
+ if (t2 == CURRENT_ABSTIME)
+ t2 = GetCurrentTransactionStartTime();
+
+ return(t1 > t2);
+}
+
+int32
+abstimele(AbsoluteTime t1, AbsoluteTime t2)
+{
+ if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
+ return 0;
+ if (t1 == CURRENT_ABSTIME)
+ t1 = GetCurrentTransactionStartTime();
+ if (t2 == CURRENT_ABSTIME)
+ t2 = GetCurrentTransactionStartTime();
+
+ return(t1 <= t2);
+}
+
+int32
+abstimege(AbsoluteTime t1, AbsoluteTime t2)
+{
+ if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
+ return 0;
+ if (t1 == CURRENT_ABSTIME)
+ t1 = GetCurrentTransactionStartTime();
+ if (t2 == CURRENT_ABSTIME)
+ t2 = GetCurrentTransactionStartTime();
+
+ return(t1 >= t2);
+}
+
+