1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* nabstime.c--
|
|
|
|
* parse almost any absolute date getdate(3) can (& some it can't)
|
|
|
|
*
|
|
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
1996-11-08 07:02:30 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.7 1996/11/08 05:59:45 momjian Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "access/xact.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 */
|
1996-10-18 01:59:45 +02:00
|
|
|
#ifdef HAVE_TZSET
|
1996-07-09 08:22:35 +02:00
|
|
|
tzset();
|
1996-10-18 01:59:45 +02:00
|
|
|
#ifndef win32
|
1996-07-09 08:22:35 +02:00
|
|
|
*tzp = timezone / 60; /* this is an X/Open-ism */
|
|
|
|
#else
|
|
|
|
*tzp = _timezone / 60; /* this is an X/Open-ism */
|
1996-10-18 01:59:45 +02:00
|
|
|
#endif /* win32 */
|
|
|
|
#else /* !HAVE_TZSET */
|
1996-07-09 08:22:35 +02:00
|
|
|
time_t now = time((time_t *) NULL);
|
|
|
|
struct tm *tmnow = localtime(&now);
|
|
|
|
|
|
|
|
*tzp = - tmnow->tm_gmtoff / 60; /* tm_gmtoff is Sun/DEC-ism */
|
1996-10-18 01:59:45 +02:00
|
|
|
#endif
|
|
|
|
#endif
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
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])
|
1996-11-08 07:02:30 +01:00
|
|
|
if (mon != 2 ||
|
|
|
|
(year % 4 == 0 &&
|
|
|
|
((year % 100 != 0 || year % 400 == 0)) && day > 29))
|
1996-07-09 08:22:35 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|