datetime.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,681 行 · 第 1/5 页
C
2,681 行
/*------------------------------------------------------------------------- * * datetime.c * Support functions for date/time types. * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/utils/adt/datetime.c,v 1.184.2.2 2008/06/09 19:34:09 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <ctype.h>#include <float.h>#include <limits.h>#include <math.h>#include "access/heapam.h"#include "access/xact.h"#include "catalog/pg_type.h"#include "funcapi.h"#include "miscadmin.h"#include "utils/builtins.h"#include "utils/datetime.h"#include "utils/memutils.h"#include "utils/tzparser.h"static int DecodeNumber(int flen, char *field, bool haveTextMonth, int fmask, int *tmask, struct pg_tm * tm, fsec_t *fsec, bool *is2digits);static int DecodeNumberField(int len, char *str, int fmask, int *tmask, struct pg_tm * tm, fsec_t *fsec, bool *is2digits);static int DecodeTime(char *str, int fmask, int *tmask, struct pg_tm * tm, fsec_t *fsec);static int DecodeTimezone(char *str, int *tzp);static const datetkn *datebsearch(const char *key, const datetkn *base, int nel);static int DecodeDate(char *str, int fmask, int *tmask, bool *is2digits, struct pg_tm * tm);static int ValidateDate(int fmask, bool is2digits, bool bc, struct pg_tm * tm);static void TrimTrailingZeros(char *str);const int day_tab[2][13] ={ {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}};char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun","Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday", NULL};/***************************************************************************** * PRIVATE ROUTINES * *****************************************************************************//* * Definitions for squeezing values into "value" * We set aside a high bit for a sign, and scale the timezone offsets * in minutes by a factor of 15 (so can represent quarter-hour increments). */#define ABS_SIGNBIT ((char) 0200)#define VALMASK ((char) 0177)#define POS(n) (n)#define NEG(n) ((n)|ABS_SIGNBIT)#define SIGNEDCHAR(c) ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c))#define FROMVAL(tp) (-SIGNEDCHAR((tp)->value) * 15) /* uncompress */#define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/15): POS(v)/15))/* * datetktbl holds date/time keywords. * * Note that this table must be strictly alphabetically ordered to allow an * O(ln(N)) search algorithm to be used. * * The text field is NOT guaranteed to be NULL-terminated. * * To keep this table reasonably small, we divide the lexval for TZ and DTZ * entries by 15 (so they are on 15 minute boundaries) and truncate the text * field at TOKMAXLEN characters. * Formerly, we divided by 10 rather than 15 but there are a few time zones * which are 30 or 45 minutes away from an even hour, most are on an hour * boundary, and none on other boundaries. * * The static table contains no TZ or DTZ entries, rather those are loaded * from configuration files and stored in timezonetktbl, which has the same * format as the static datetktbl. */static datetkn *timezonetktbl = NULL;static int sztimezonetktbl = 0;static const datetkn datetktbl[] = {/* text, token, lexval */ {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */ {"abstime", IGNORE_DTF, 0}, /* for pre-v6.1 "Invalid Abstime" */ {DA_D, ADBC, AD}, /* "ad" for years > 0 */ {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */ {"am", AMPM, AM}, {"apr", MONTH, 4}, {"april", MONTH, 4}, {"at", IGNORE_DTF, 0}, /* "at" (throwaway) */ {"aug", MONTH, 8}, {"august", MONTH, 8}, {DB_C, ADBC, BC}, /* "bc" for years <= 0 */ {DCURRENT, RESERV, DTK_CURRENT}, /* "current" is always now */ {"d", UNITS, DTK_DAY}, /* "day of month" for ISO input */ {"dec", MONTH, 12}, {"december", MONTH, 12}, {"dow", RESERV, DTK_DOW}, /* day of week */ {"doy", RESERV, DTK_DOY}, /* day of year */ {"dst", DTZMOD, 6}, {EPOCH, RESERV, DTK_EPOCH}, /* "epoch" reserved for system epoch time */ {"feb", MONTH, 2}, {"february", MONTH, 2}, {"fri", DOW, 5}, {"friday", DOW, 5}, {"h", UNITS, DTK_HOUR}, /* "hour" */ {LATE, RESERV, DTK_LATE}, /* "infinity" reserved for "late time" */ {INVALID, RESERV, DTK_INVALID}, /* "invalid" reserved for bad time */ {"isodow", RESERV, DTK_ISODOW}, /* ISO day of week, Sunday == 7 */ {"isoyear", UNITS, DTK_ISOYEAR}, /* year in terms of the ISO week date */ {"j", UNITS, DTK_JULIAN}, {"jan", MONTH, 1}, {"january", MONTH, 1}, {"jd", UNITS, DTK_JULIAN}, {"jul", MONTH, 7}, {"julian", UNITS, DTK_JULIAN}, {"july", MONTH, 7}, {"jun", MONTH, 6}, {"june", MONTH, 6}, {"m", UNITS, DTK_MONTH}, /* "month" for ISO input */ {"mar", MONTH, 3}, {"march", MONTH, 3}, {"may", MONTH, 5}, {"mm", UNITS, DTK_MINUTE}, /* "minute" for ISO input */ {"mon", DOW, 1}, {"monday", DOW, 1}, {"nov", MONTH, 11}, {"november", MONTH, 11}, {NOW, RESERV, DTK_NOW}, /* current transaction time */ {"oct", MONTH, 10}, {"october", MONTH, 10}, {"on", IGNORE_DTF, 0}, /* "on" (throwaway) */ {"pm", AMPM, PM}, {"s", UNITS, DTK_SECOND}, /* "seconds" for ISO input */ {"sat", DOW, 6}, {"saturday", DOW, 6}, {"sep", MONTH, 9}, {"sept", MONTH, 9}, {"september", MONTH, 9}, {"sun", DOW, 0}, {"sunday", DOW, 0}, {"t", ISOTIME, DTK_TIME}, /* Filler for ISO time fields */ {"thu", DOW, 4}, {"thur", DOW, 4}, {"thurs", DOW, 4}, {"thursday", DOW, 4}, {TODAY, RESERV, DTK_TODAY}, /* midnight */ {TOMORROW, RESERV, DTK_TOMORROW}, /* tomorrow midnight */ {"tue", DOW, 2}, {"tues", DOW, 2}, {"tuesday", DOW, 2}, {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */ {"wed", DOW, 3}, {"wednesday", DOW, 3}, {"weds", DOW, 3}, {"y", UNITS, DTK_YEAR}, /* "year" for ISO input */ {YESTERDAY, RESERV, DTK_YESTERDAY} /* yesterday midnight */};static int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0];static datetkn deltatktbl[] = { /* text, token, lexval */ {"@", IGNORE_DTF, 0}, /* postgres relative prefix */ {DAGO, AGO, 0}, /* "ago" indicates negative time offset */ {"c", UNITS, DTK_CENTURY}, /* "century" relative */ {"cent", UNITS, DTK_CENTURY}, /* "century" relative */ {"centuries", UNITS, DTK_CENTURY}, /* "centuries" relative */ {DCENTURY, UNITS, DTK_CENTURY}, /* "century" relative */ {"d", UNITS, DTK_DAY}, /* "day" relative */ {DDAY, UNITS, DTK_DAY}, /* "day" relative */ {"days", UNITS, DTK_DAY}, /* "days" relative */ {"dec", UNITS, DTK_DECADE}, /* "decade" relative */ {DDECADE, UNITS, DTK_DECADE}, /* "decade" relative */ {"decades", UNITS, DTK_DECADE}, /* "decades" relative */ {"decs", UNITS, DTK_DECADE}, /* "decades" relative */ {"h", UNITS, DTK_HOUR}, /* "hour" relative */ {DHOUR, UNITS, DTK_HOUR}, /* "hour" relative */ {"hours", UNITS, DTK_HOUR}, /* "hours" relative */ {"hr", UNITS, DTK_HOUR}, /* "hour" relative */ {"hrs", UNITS, DTK_HOUR}, /* "hours" relative */ {INVALID, RESERV, DTK_INVALID}, /* reserved for invalid time */ {"m", UNITS, DTK_MINUTE}, /* "minute" relative */ {"microsecon", UNITS, DTK_MICROSEC}, /* "microsecond" relative */ {"mil", UNITS, DTK_MILLENNIUM}, /* "millennium" relative */ {"millennia", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */ {DMILLENNIUM, UNITS, DTK_MILLENNIUM}, /* "millennium" relative */ {"millisecon", UNITS, DTK_MILLISEC}, /* relative */ {"mils", UNITS, DTK_MILLENNIUM}, /* "millennia" relative */ {"min", UNITS, DTK_MINUTE}, /* "minute" relative */ {"mins", UNITS, DTK_MINUTE}, /* "minutes" relative */ {DMINUTE, UNITS, DTK_MINUTE}, /* "minute" relative */ {"minutes", UNITS, DTK_MINUTE}, /* "minutes" relative */ {"mon", UNITS, DTK_MONTH}, /* "months" relative */ {"mons", UNITS, DTK_MONTH}, /* "months" relative */ {DMONTH, UNITS, DTK_MONTH}, /* "month" relative */ {"months", UNITS, DTK_MONTH}, {"ms", UNITS, DTK_MILLISEC}, {"msec", UNITS, DTK_MILLISEC}, {DMILLISEC, UNITS, DTK_MILLISEC}, {"mseconds", UNITS, DTK_MILLISEC}, {"msecs", UNITS, DTK_MILLISEC}, {"qtr", UNITS, DTK_QUARTER}, /* "quarter" relative */ {DQUARTER, UNITS, DTK_QUARTER}, /* "quarter" relative */ {"reltime", IGNORE_DTF, 0}, /* pre-v6.1 "Undefined Reltime" */ {"s", UNITS, DTK_SECOND}, {"sec", UNITS, DTK_SECOND}, {DSECOND, UNITS, DTK_SECOND}, {"seconds", UNITS, DTK_SECOND}, {"secs", UNITS, DTK_SECOND}, {DTIMEZONE, UNITS, DTK_TZ}, /* "timezone" time offset */ {"timezone_h", UNITS, DTK_TZ_HOUR}, /* timezone hour units */ {"timezone_m", UNITS, DTK_TZ_MINUTE}, /* timezone minutes units */ {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */ {"us", UNITS, DTK_MICROSEC}, /* "microsecond" relative */ {"usec", UNITS, DTK_MICROSEC}, /* "microsecond" relative */ {DMICROSEC, UNITS, DTK_MICROSEC}, /* "microsecond" relative */ {"useconds", UNITS, DTK_MICROSEC}, /* "microseconds" relative */ {"usecs", UNITS, DTK_MICROSEC}, /* "microseconds" relative */ {"w", UNITS, DTK_WEEK}, /* "week" relative */ {DWEEK, UNITS, DTK_WEEK}, /* "week" relative */ {"weeks", UNITS, DTK_WEEK}, /* "weeks" relative */ {"y", UNITS, DTK_YEAR}, /* "year" relative */ {DYEAR, UNITS, DTK_YEAR}, /* "year" relative */ {"years", UNITS, DTK_YEAR}, /* "years" relative */ {"yr", UNITS, DTK_YEAR}, /* "year" relative */ {"yrs", UNITS, DTK_YEAR} /* "years" relative */};static int szdeltatktbl = sizeof deltatktbl / sizeof deltatktbl[0];static const datetkn *datecache[MAXDATEFIELDS] = {NULL};static const datetkn *deltacache[MAXDATEFIELDS] = {NULL};/* * strtoi --- just like strtol, but returns int not long */static intstrtoi(const char *nptr, char **endptr, int base){ long val; val = strtol(nptr, endptr, base);#ifdef HAVE_LONG_INT_64 if (val != (long) ((int32) val)) errno = ERANGE;#endif return (int) val;}/* * Calendar time to Julian date conversions. * Julian date is commonly used in astronomical applications, * since it is numerically accurate and computationally simple. * The algorithms here will accurately convert between Julian day * and calendar date for all non-negative Julian days * (i.e. from Nov 24, -4713 on). * * These routines will be used by other date/time packages * - thomas 97/02/25 * * Rewritten to eliminate overflow problems. This now allows the * routines to work correctly for all Julian day counts from * 0 to 2147483647 (Nov 24, -4713 to Jun 3, 5874898) assuming * a 32-bit integer. Longer types should also work to the limits * of their precision. */intdate2j(int y, int m, int d){ int julian; int century; if (m > 2) { m += 1; y += 4800; } else { m += 13; y += 4799; } century = y / 100; julian = y * 365 - 32167; julian += y / 4 - century + century / 4; julian += 7834 * m / 256 + d; return julian;} /* date2j() */voidj2date(int jd, int *year, int *month, int *day){ unsigned int julian; unsigned int quad; unsigned int extra; int y; julian = jd; julian += 32044; quad = julian / 146097; extra = (julian - quad * 146097) * 4 + 3; julian += 60 + quad * 3 + extra / 146097; quad = julian / 1461; julian -= quad * 1461; y = julian * 4 / 1461; julian = ((y != 0) ? ((julian + 305) % 365) : ((julian + 306) % 366)) + 123; y += quad * 4; *year = y - 4800; quad = julian * 2141 / 65536; *day = julian - 7834 * quad / 256; *month = (quad + 10) % 12 + 1; return;} /* j2date() *//* * j2day - convert Julian date to day-of-week (0..6 == Sun..Sat) * * Note: various places use the locution j2day(date - 1) to produce a * result according to the convention 0..6 = Mon..Sun. This is a bit of * a crock, but will work as long as the computation here is just a modulo. */intj2day(int date){ unsigned int day; day = date; day += 1; day %= 7; return (int) day;} /* j2day() *//* * GetCurrentDateTime() * * Get the transaction start time ("now()") broken down as a struct pg_tm. */voidGetCurrentDateTime(struct pg_tm * tm){ int tz; fsec_t fsec; timestamp2tm(GetCurrentTransactionStartTimestamp(), &tz, tm, &fsec, NULL, NULL); /* Note: don't pass NULL tzp to timestamp2tm; affects behavior */}/* * GetCurrentTimeUsec() * * Get the transaction start time ("now()") broken down as a struct pg_tm, * including fractional seconds and timezone offset. */voidGetCurrentTimeUsec(struct pg_tm * tm, fsec_t *fsec, int *tzp){ int tz; timestamp2tm(GetCurrentTransactionStartTimestamp(), &tz, tm, fsec, NULL, NULL); /* Note: don't pass NULL tzp to timestamp2tm; affects behavior */ if (tzp != NULL) *tzp = tz;}/* TrimTrailingZeros() * ... resulting from printing numbers with full precision. */static voidTrimTrailingZeros(char *str){ int len = strlen(str);#if 0 /* chop off trailing one to cope with interval rounding */ if (strcmp(str + len - 4, "0001") == 0) { len -= 4; *(str + len) = '\0'; }#endif /* chop off trailing zeros... but leave at least 2 fractional digits */ while (*(str + len - 1) == '0' && *(str + len - 3) != '.') { len--; *(str + len) = '\0'; }}/* ParseDateTime() * Break string into tokens based on a date/time context. * Returns 0 if successful, DTERR code if bogus input detected. * * timestr - the input string * workbuf - workspace for field string storage. This must be * larger than the largest legal input for this datetime type -- * some additional space will be needed to NUL terminate fields. * buflen - the size of workbuf * field[] - pointers to field strings are returned in this array * ftype[] - field type indicators are returned in this array * maxfields - dimensions of the above two arrays * *numfields - set to the actual number of fields detected * * The fields extracted from the input are stored as separate, * null-terminated strings in the workspace at workbuf. Any text is * converted to lower case. * * Several field types are assigned: * DTK_NUMBER - digits and (possibly) a decimal point * DTK_DATE - digits and two delimiters, or digits and text * DTK_TIME - digits, colon delimiters, and possibly a decimal point * DTK_STRING - text (no digits or punctuation) * DTK_SPECIAL - leading "+" or "-" followed by text * DTK_TZ - leading "+" or "-" followed by digits (also eats ':' or '.') * * Note that some field types can hold unexpected items: * DTK_NUMBER can hold date fields (yy.ddd) * DTK_STRING can hold months (January) and time zones (PST) * DTK_DATE can hold time zone names (America/New_York, GMT-8) */intParseDateTime(const char *timestr, char *workbuf, size_t buflen, char **field, int *ftype, int maxfields, int *numfields){ int nf = 0; const char *cp = timestr; char *bufp = workbuf; const char *bufend = workbuf + buflen; /* * Set the character pointed-to by "bufptr" to "newchar", and increment * "bufptr". "end" gives the end of the buffer -- we return an error if * there is no space left to append a character to the buffer. Note that * "bufptr" is evaluated twice. */#define APPEND_CHAR(bufptr, end, newchar) \ do \ { \ if (((bufptr) + 1) >= (end)) \ return DTERR_BAD_FORMAT; \ *(bufptr)++ = newchar; \ } while (0) /* outer loop through fields */ while (*cp != '\0') { /* Ignore spaces between fields */ if (isspace((unsigned char) *cp)) { cp++; continue; } /* Record start of current field */ if (nf >= maxfields) return DTERR_BAD_FORMAT; field[nf] = bufp; /* leading digit? then date or time */ if (isdigit((unsigned char) *cp)) { APPEND_CHAR(bufp, bufend, *cp++); while (isdigit((unsigned char) *cp)) APPEND_CHAR(bufp, bufend, *cp++); /* time field? */ if (*cp == ':') { ftype[nf] = DTK_TIME; APPEND_CHAR(bufp, bufend, *cp++); while (isdigit((unsigned char) *cp) || (*cp == ':') || (*cp == '.')) APPEND_CHAR(bufp, bufend, *cp++); } /* date field? allow embedded text month */ else if (*cp == '-' || *cp == '/' || *cp == '.') { /* save delimiting character to use later */ char delim = *cp; APPEND_CHAR(bufp, bufend, *cp++); /* second field is all digits? then no embedded text month */ if (isdigit((unsigned char) *cp)) { ftype[nf] = ((delim == '.') ? DTK_NUMBER : DTK_DATE); while (isdigit((unsigned char) *cp)) APPEND_CHAR(bufp, bufend, *cp++); /* * insist that the delimiters match to get a three-field * date. */ if (*cp == delim) { ftype[nf] = DTK_DATE; APPEND_CHAR(bufp, bufend, *cp++); while (isdigit((unsigned char) *cp) || *cp == delim) APPEND_CHAR(bufp, bufend, *cp++); }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?