/*------------------------------------------------------------------------- * * 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.10 1997/01/06 00:20:13 scrappy Exp $ * *------------------------------------------------------------------------- */ #include #include #include #include #include #include "postgres.h" #include "access/xact.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 */ #ifdef HAVE_TZSET 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 /* !HAVE_TZSET */ time_t now = time((time_t *) NULL); struct tm *tmnow = localtime(&now); *tzp = - tmnow->tm_gmtoff / 60; /* tm_gmtoff is Sun/DEC-ism */ #endif #endif 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); }