datetime.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,681 行 · 第 1/5 页

C
2,681
字号
 * Return 0 if okay, a DTERR code if not. */static intValidateDate(int fmask, bool is2digits, bool bc, struct pg_tm * tm){	if (fmask & DTK_M(YEAR))	{		/* there is no year zero in AD/BC notation; i.e. "1 BC" == year 0 */		if (bc)		{			if (tm->tm_year > 0)				tm->tm_year = -(tm->tm_year - 1);			else				ereport(ERROR,						(errcode(ERRCODE_INVALID_DATETIME_FORMAT),						 errmsg("inconsistent use of year %04d and \"BC\"",								tm->tm_year)));		}		else if (is2digits)		{			if (tm->tm_year < 70)				tm->tm_year += 2000;			else if (tm->tm_year < 100)				tm->tm_year += 1900;		}	}	/* now that we have correct year, decode DOY */	if (fmask & DTK_M(DOY))	{		j2date(date2j(tm->tm_year, 1, 1) + tm->tm_yday - 1,			   &tm->tm_year, &tm->tm_mon, &tm->tm_mday);	}	/* check for valid month */	if (fmask & DTK_M(MONTH))	{		if (tm->tm_mon < 1 || tm->tm_mon > MONTHS_PER_YEAR)			return DTERR_MD_FIELD_OVERFLOW;	}	/* minimal check for valid day */	if (fmask & DTK_M(DAY))	{		if (tm->tm_mday < 1 || tm->tm_mday > 31)			return DTERR_MD_FIELD_OVERFLOW;	}	if ((fmask & DTK_DATE_M) == DTK_DATE_M)	{		/*		 * Check for valid day of month, now that we know for sure the month		 * and year.  Note we don't use MD_FIELD_OVERFLOW here, since it seems		 * unlikely that "Feb 29" is a YMD-order error.		 */		if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])			return DTERR_FIELD_OVERFLOW;	}	return 0;}/* DecodeTime() * Decode time string which includes delimiters. * Return 0 if okay, a DTERR code if not. * * Only check the lower limit on hours, since this same code can be * used to represent time spans. */static intDecodeTime(char *str, int fmask, int *tmask, struct pg_tm * tm, fsec_t *fsec){	char	   *cp;	*tmask = DTK_TIME_M;	errno = 0;	tm->tm_hour = strtoi(str, &cp, 10);	if (errno == ERANGE)		return DTERR_FIELD_OVERFLOW;	if (*cp != ':')		return DTERR_BAD_FORMAT;	str = cp + 1;	errno = 0;	tm->tm_min = strtoi(str, &cp, 10);	if (errno == ERANGE)		return DTERR_FIELD_OVERFLOW;	if (*cp == '\0')	{		tm->tm_sec = 0;		*fsec = 0;	}	else if (*cp != ':')		return DTERR_BAD_FORMAT;	else	{		str = cp + 1;		errno = 0;		tm->tm_sec = strtoi(str, &cp, 10);		if (errno == ERANGE)			return DTERR_FIELD_OVERFLOW;		if (*cp == '\0')			*fsec = 0;		else if (*cp == '.')		{			double		frac;			str = cp;			frac = strtod(str, &cp);			if (*cp != '\0')				return DTERR_BAD_FORMAT;#ifdef HAVE_INT64_TIMESTAMP			*fsec = rint(frac * 1000000);#else			*fsec = frac;#endif		}		else			return DTERR_BAD_FORMAT;	}	/* do a sanity check */#ifdef HAVE_INT64_TIMESTAMP	if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 ||		tm->tm_sec < 0 || tm->tm_sec > 60 || *fsec < INT64CONST(0) ||		*fsec >= USECS_PER_SEC)		return DTERR_FIELD_OVERFLOW;#else	if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_min > 59 ||		tm->tm_sec < 0 || tm->tm_sec > 60 || *fsec < 0 || *fsec >= 1)		return DTERR_FIELD_OVERFLOW;#endif	return 0;}/* DecodeNumber() * Interpret plain numeric field as a date value in context. * Return 0 if okay, a DTERR code if not. */static intDecodeNumber(int flen, char *str, bool haveTextMonth, int fmask,			 int *tmask, struct pg_tm * tm, fsec_t *fsec, bool *is2digits){	int			val;	char	   *cp;	int			dterr;	*tmask = 0;	errno = 0;	val = strtoi(str, &cp, 10);	if (errno == ERANGE)		return DTERR_FIELD_OVERFLOW;	if (cp == str)		return DTERR_BAD_FORMAT;	if (*cp == '.')	{		double		frac;		/*		 * More than two digits before decimal point? Then could be a date or		 * a run-together time: 2001.360 20011225 040506.789		 */		if (cp - str > 2)		{			dterr = DecodeNumberField(flen, str,									  (fmask | DTK_DATE_M),									  tmask, tm,									  fsec, is2digits);			if (dterr < 0)				return dterr;			return 0;		}		frac = strtod(cp, &cp);		if (*cp != '\0')			return DTERR_BAD_FORMAT;#ifdef HAVE_INT64_TIMESTAMP		*fsec = rint(frac * 1000000);#else		*fsec = frac;#endif	}	else if (*cp != '\0')		return DTERR_BAD_FORMAT;	/* Special case for day of year */	if (flen == 3 && (fmask & DTK_DATE_M) == DTK_M(YEAR) && val >= 1 &&		val <= 366)	{		*tmask = (DTK_M(DOY) | DTK_M(MONTH) | DTK_M(DAY));		tm->tm_yday = val;		/* tm_mon and tm_mday can't actually be set yet ... */		return 0;	}	/* Switch based on what we have so far */	switch (fmask & DTK_DATE_M)	{		case 0:			/*			 * Nothing so far; make a decision about what we think the input			 * is.	There used to be lots of heuristics here, but the			 * consensus now is to be paranoid.  It *must* be either			 * YYYY-MM-DD (with a more-than-two-digit year field), or the			 * field order defined by DateOrder.			 */			if (flen >= 3 || DateOrder == DATEORDER_YMD)			{				*tmask = DTK_M(YEAR);				tm->tm_year = val;			}			else if (DateOrder == DATEORDER_DMY)			{				*tmask = DTK_M(DAY);				tm->tm_mday = val;			}			else			{				*tmask = DTK_M(MONTH);				tm->tm_mon = val;			}			break;		case (DTK_M(YEAR)):			/* Must be at second field of YY-MM-DD */			*tmask = DTK_M(MONTH);			tm->tm_mon = val;			break;		case (DTK_M(MONTH)):			if (haveTextMonth)			{				/*				 * We are at the first numeric field of a date that included a				 * textual month name.	We want to support the variants				 * MON-DD-YYYY, DD-MON-YYYY, and YYYY-MON-DD as unambiguous				 * inputs.	We will also accept MON-DD-YY or DD-MON-YY in				 * either DMY or MDY modes, as well as YY-MON-DD in YMD mode.				 */				if (flen >= 3 || DateOrder == DATEORDER_YMD)				{					*tmask = DTK_M(YEAR);					tm->tm_year = val;				}				else				{					*tmask = DTK_M(DAY);					tm->tm_mday = val;				}			}			else			{				/* Must be at second field of MM-DD-YY */				*tmask = DTK_M(DAY);				tm->tm_mday = val;			}			break;		case (DTK_M(YEAR) | DTK_M(MONTH)):			if (haveTextMonth)			{				/* Need to accept DD-MON-YYYY even in YMD mode */				if (flen >= 3 && *is2digits)				{					/* Guess that first numeric field is day was wrong */					*tmask = DTK_M(DAY);		/* YEAR is already set */					tm->tm_mday = tm->tm_year;					tm->tm_year = val;					*is2digits = FALSE;				}				else				{					*tmask = DTK_M(DAY);					tm->tm_mday = val;				}			}			else			{				/* Must be at third field of YY-MM-DD */				*tmask = DTK_M(DAY);				tm->tm_mday = val;			}			break;		case (DTK_M(DAY)):			/* Must be at second field of DD-MM-YY */			*tmask = DTK_M(MONTH);			tm->tm_mon = val;			break;		case (DTK_M(MONTH) | DTK_M(DAY)):			/* Must be at third field of DD-MM-YY or MM-DD-YY */			*tmask = DTK_M(YEAR);			tm->tm_year = val;			break;		case (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY)):			/* we have all the date, so it must be a time field */			dterr = DecodeNumberField(flen, str, fmask,									  tmask, tm,									  fsec, is2digits);			if (dterr < 0)				return dterr;			return 0;		default:			/* Anything else is bogus input */			return DTERR_BAD_FORMAT;	}	/*	 * When processing a year field, mark it for adjustment if it's only one	 * or two digits.	 */	if (*tmask == DTK_M(YEAR))		*is2digits = (flen <= 2);	return 0;}/* DecodeNumberField() * Interpret numeric string as a concatenated date or time field. * Return a DTK token (>= 0) if successful, a DTERR code (< 0) if not. * * Use the context of previously decoded fields to help with * the interpretation. */static intDecodeNumberField(int len, char *str, int fmask,				int *tmask, struct pg_tm * tm, fsec_t *fsec, bool *is2digits){	char	   *cp;	/*	 * Have a decimal point? Then this is a date or something with a seconds	 * field...	 */	if ((cp = strchr(str, '.')) != NULL)	{		double		frac;		frac = strtod(cp, NULL);#ifdef HAVE_INT64_TIMESTAMP		*fsec = rint(frac * 1000000);#else		*fsec = frac;#endif		*cp = '\0';		len = strlen(str);	}	/* No decimal point and no complete date yet? */	else if ((fmask & DTK_DATE_M) != DTK_DATE_M)	{		/* yyyymmdd? */		if (len == 8)		{			*tmask = DTK_DATE_M;			tm->tm_mday = atoi(str + 6);			*(str + 6) = '\0';			tm->tm_mon = atoi(str + 4);			*(str + 4) = '\0';			tm->tm_year = atoi(str + 0);			return DTK_DATE;		}		/* yymmdd? */		else if (len == 6)		{			*tmask = DTK_DATE_M;			tm->tm_mday = atoi(str + 4);			*(str + 4) = '\0';			tm->tm_mon = atoi(str + 2);			*(str + 2) = '\0';			tm->tm_year = atoi(str + 0);			*is2digits = TRUE;			return DTK_DATE;		}	}	/* not all time fields are specified? */	if ((fmask & DTK_TIME_M) != DTK_TIME_M)	{		/* hhmmss */		if (len == 6)		{			*tmask = DTK_TIME_M;			tm->tm_sec = atoi(str + 4);			*(str + 4) = '\0';			tm->tm_min = atoi(str + 2);			*(str + 2) = '\0';			tm->tm_hour = atoi(str + 0);			return DTK_TIME;		}		/* hhmm? */		else if (len == 4)		{			*tmask = DTK_TIME_M;			tm->tm_sec = 0;			tm->tm_min = atoi(str + 2);			*(str + 2) = '\0';			tm->tm_hour = atoi(str + 0);			return DTK_TIME;		}	}	return DTERR_BAD_FORMAT;}/* DecodeTimezone() * Interpret string as a numeric timezone. * * Return 0 if okay (and set *tzp), a DTERR code if not okay. * * NB: this must *not* ereport on failure; see commands/variable.c. * * Note: we allow timezone offsets up to 13:59.  There are places that * use +1300 summer time. */static intDecodeTimezone(char *str, int *tzp){	int			tz;	int			hr,				min,				sec = 0;	char	   *cp;	/* leading character must be "+" or "-" */	if (*str != '+' && *str != '-')		return DTERR_BAD_FORMAT;	errno = 0;	hr = strtoi(str + 1, &cp, 10);	if (errno == ERANGE)		return DTERR_TZDISP_OVERFLOW;	/* explicit delimiter? */	if (*cp == ':')	{		errno = 0;		min = strtoi(cp + 1, &cp, 10);		if (errno == ERANGE)			return DTERR_TZDISP_OVERFLOW;		if (*cp == ':')		{			errno = 0;			sec = strtoi(cp + 1, &cp, 10);			if (errno == ERANGE)				return DTERR_TZDISP_OVERFLOW;		}	}	/* otherwise, might have run things together... */	else if (*cp == '\0' && strlen(str) > 3)	{		min = hr % 100;		hr = hr / 100;		/* we could, but don't, support a run-together hhmmss format */	}	else		min = 0;	if (hr < 0 || hr > 14)		return DTERR_TZDISP_OVERFLOW;	if (min < 0 || min >= 60)		return DTERR_TZDISP_OVERFLOW;	if (sec < 0 || sec >= 60)		return DTERR_TZDISP_OVERFLOW;	tz = (hr * MINS_PER_HOUR + min) * SECS_PER_MINUTE + sec;	if (*str == '-')		tz = -tz;	*tzp = -tz;	if (*cp != '\0')		return DTERR_BAD_FORMAT;	return 0;}/* DecodeSpecial() * Decode text string using lookup table. * * Implement a cache lookup since it is likely that dates *	will be related in format. * * NB: this must *not* ereport on failure; * see commands/variable.c. */intDecodeSpecial(int field, char *lowtoken, int *val){	int			type;	const datetkn *tp;	tp = datecache[field];	if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)	{		tp = datebsearch(lowtoken, timezonetktbl, sztimezonetktbl);		if (tp == NULL)			tp = datebsearch(lowtoken, datetktbl, szdatetktbl);	}	if (tp == NULL)	{		type = UNKNOWN_FIELD;		*val = 0;	}	else	{		datecache[field] = tp;		type = tp->type;		switch (type)		{			case TZ:			case DTZ:			case DTZMOD:				*val = FROMVAL(tp);				break;			default:				*val 

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?