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 + -
显示快捷键?