/* *j2day-convertJuliandatetoday-of-week(0..6==Sun..Sat) * *Note:variousplacesusethelocutionj2day(date-1)toproducea *resultaccordingtotheconvention0..6=Mon..Sun.Thisisabitof *acrock,butwillworkaslongasthecomputationhereisjustamodulo.
*/ int
j2day(int date)
{
date += 1;
date %= 7; /* Cope if division truncates towards zero, as it probably does */ if (date < 0)
date += 7;
/* outer loop through fields */ while (*cp != '\0')
{ /* Ignore spaces between fields */ if (isspace((unsignedchar) *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((unsignedchar) *cp))
{
APPEND_CHAR(bufp, bufend, *cp++); while (isdigit((unsignedchar) *cp))
APPEND_CHAR(bufp, bufend, *cp++);
/* time field? */ if (*cp == ':')
{
ftype[nf] = DTK_TIME;
APPEND_CHAR(bufp, bufend, *cp++); while (isdigit((unsignedchar) *cp) ||
(*cp == ':') || (*cp == '.'))
APPEND_CHAR(bufp, bufend, *cp++);
} /* date field? allow embedded text month */ elseif (*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((unsignedchar) *cp))
{
ftype[nf] = ((delim == '.') ? DTK_NUMBER : DTK_DATE); while (isdigit((unsignedchar) *cp))
APPEND_CHAR(bufp, bufend, *cp++);
/* *Alreadyhaveadate?Thenthismightbeatimezonename *withembeddedpunctuation(e.g."America/New_York")ora *run-togethertimewithtrailingtimezone(e.g.hhmmss-zz). *-thomas2001-12-25 * *Weconsideritatimezoneifwealreadyhavemonth&day. *Thisistoallowtheform"mmmddhhmmsstzyear",which *we'vehistoricallyaccepted.
*/ elseif (ptype != 0 ||
((fmask & (DTK_M(MONTH) | DTK_M(DAY))) ==
(DTK_M(MONTH) | DTK_M(DAY))))
{ /* No time zone accepted? Then quit... */ if (tzp == NULL) return DTERR_BAD_FORMAT;
if (isdigit((unsignedchar) *field[i]) || ptype != 0)
{ char *cp;
/* *Allowapreceding"t"field,butnootherunits.
*/ if (ptype != 0)
{ /* Sanity check; should not fail this test */ if (ptype != DTK_TIME) return DTERR_BAD_FORMAT;
ptype = 0;
}
case DTK_TIME: /* previous field was "t" for ISO time */
dterr = DecodeNumberField(strlen(field[i]), field[i],
(fmask | DTK_DATE_M),
&tmask, tm,
fsec, &is2digits); if (dterr < 0) return dterr; if (tmask != DTK_TIME_M) return DTERR_BAD_FORMAT; break;
case DTK_STRING: case DTK_SPECIAL: /* timezone abbrevs take precedence over built-in tokens */
dterr = DecodeTimezoneAbbrev(i, field[i],
&type, &val, &valtz, extra); if (dterr) return dterr; if (type == UNKNOWN_FIELD)
type = DecodeSpecial(i, field[i], &val); if (type == IGNORE_DTF) continue;
/* No preceding date? Then quit... */ if ((fmask & DTK_DATE_M) != DTK_DATE_M) return DTERR_BAD_FORMAT;
/* reject consecutive unhandled units */ if (ptype != 0) return DTERR_BAD_FORMAT;
ptype = val; break;
case UNKNOWN_FIELD:
/* *Beforegivingupanddeclaringerror,checktosee *ifitisanall-alphatimezonename.
*/
namedTz = pg_tzset(field[i]); if (!namedTz) return DTERR_BAD_FORMAT; /* we'll apply the zone setting below */
tmask = DTK_M(TZ); break;
default: return DTERR_BAD_FORMAT;
} break;
default: return DTERR_BAD_FORMAT;
}
if (tmask & fmask) return DTERR_BAD_FORMAT;
fmask |= tmask;
} /* end loop over fields */
/* reject if prefix type appeared and was never handled */ if (ptype != 0) return DTERR_BAD_FORMAT;
/* do additional checking for normal date specs (but not "infinity" etc) */ if (*dtype == DTK_DATE)
{ /* do final checking/adjustment of Y/M/D fields */
dterr = ValidateDate(fmask, isjulian, is2digits, bc, tm); if (dterr) return dterr;
/* check for incomplete input */ if ((fmask & DTK_DATE_M) != DTK_DATE_M)
{ if ((fmask & DTK_TIME_M) == DTK_TIME_M) return1; return DTERR_BAD_FORMAT;
}
/* *Ifwehadafulltimezonespec,computetheoffset(wecouldnotdo *itbefore,becauseweneedthedatetoresolveDSTstatus).
*/ if (namedTz != NULL)
{ /* daylight savings time modifier disallowed with full TZ */ if (fmask & DTK_M(DTZMOD)) return DTERR_BAD_FORMAT;
*tzp = DetermineTimeZoneOffset(tm, namedTz);
}
/* *Likewise,ifwehadadynamictimezoneabbreviation,resolveit *now.
*/ if (abbrevTz != NULL)
{ /* daylight savings time modifier disallowed with dynamic TZ */ if (fmask & DTK_M(DTZMOD)) return DTERR_BAD_FORMAT;
/* *First,generatethepg_time_tvaluecorrespondingtothegiven *y/m/d/h/m/stakenasGMTtime.Ifthisoverflows,puntanddecidethe *timezoneisGMT.(ForavalidJuliandate,integeroverflowshouldbe *impossiblewith64-bitpg_time_t,butlet'scheckforsafety.)
*/ if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday)) goto overflow;
date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - UNIX_EPOCH_JDATE;
day = ((pg_time_t) date) * SECS_PER_DAY; if (day / SECS_PER_DAY != date) goto overflow;
sec = tm->tm_sec + (tm->tm_min + tm->tm_hour * MINS_PER_HOUR) * SECS_PER_MINUTE;
mytime = day + sec; /* since sec >= 0, overflow could only be from +day to -mytime */ if (mytime < 0 && day > 0) goto overflow;
/* We need to force the abbrev to upper case */
strlcpy(upabbr, abbr, sizeof(upabbr)); for (p = (unsignedchar *) upabbr; *p; p++)
*p = pg_toupper(*p);
/* Look up the abbrev's meaning at this time in this zone */ if (pg_interpret_timezone_abbrev(upabbr,
&t,
&gmtoff,
isdst,
tzp))
{ /* Change sign to agree with DetermineTimeZoneOffset() */
*offset = (int) -gmtoff; returntrue;
} returnfalse;
}
/* We need to force the abbrev to upper case */
strlcpy(upabbr, abbr, sizeof(upabbr)); for (p = (unsignedchar *) upabbr; *p; p++)
*p = pg_toupper(*p);
/* Look up the abbrev's meaning in this zone */ if (pg_timezone_abbrev_is_known(upabbr,
isfixed,
&gmtoff,
isdst,
tzp))
{ /* Change sign to agree with DetermineTimeZoneOffset() */
*offset = (int) -gmtoff; returntrue;
} returnfalse;
}
/* DecodeTimeOnly() *Interpretparsedstringastimefieldsonly. *Returns0ifsuccessful,DTERRcodeifbogusinputdetected. * *Inputsarefield[]andftype[]arrays,oflengthnf. *Otherargumentsareoutputs. * *Notethatsupportfortimezoneisherefor *SQLTIMEWITHTIMEZONE,butitreveals *bogositywithSQLdate/timestandards,since *wemustinferatimezonefromcurrenttime. *-thomas2000-03-10 *Allowspecifyingdatetogetabettertimezone, *iftimezonesareallowed.-thomas2001-12-26
*/ int
DecodeTimeOnly(char **field, int *ftype, int nf, int *dtype, struct pg_tm *tm, fsec_t *fsec, int *tzp,
DateTimeErrorExtra *extra)
{ int fmask = 0,
tmask,
type; int ptype = 0; /* "prefix type" for ISO and Julian formats */ int i; int val; int dterr; bool isjulian = false; bool is2digits = false; bool bc = false; int mer = HR24;
pg_tz *namedTz = NULL;
pg_tz *abbrevTz = NULL; char *abbrev = NULL;
pg_tz *valtz;
for (i = 0; i < nf; i++)
{ switch (ftype[i])
{ case DTK_DATE:
/* *Timezonenotallowed?Thenshouldnotacceptdatesortime *zonesnomatterwhatelse!
*/ if (tzp == NULL) return DTERR_BAD_FORMAT;
/* Under limited circumstances, we will accept a date... */ if (i == 0 && nf >= 2 &&
(ftype[nf - 1] == DTK_DATE || ftype[1] == DTK_TIME))
{
dterr = DecodeDate(field[i], fmask,
&tmask, &is2digits, tm); if (dterr) return dterr;
} /* otherwise, this is a time and/or time zone */ else
{ if (isdigit((unsignedchar) *field[i]))
{ char *cp;
case DTK_TIME: /* previous field was "t" for ISO time */
dterr = DecodeNumberField(strlen(field[i]), field[i],
(fmask | DTK_DATE_M),
&tmask, tm,
fsec, &is2digits); if (dterr < 0) return dterr;
ftype[i] = dterr;
if (tmask != DTK_TIME_M) return DTERR_BAD_FORMAT; break;
case DTK_STRING: case DTK_SPECIAL: /* timezone abbrevs take precedence over built-in tokens */
dterr = DecodeTimezoneAbbrev(i, field[i],
&type, &val, &valtz, extra); if (dterr) return dterr; if (type == UNKNOWN_FIELD)
type = DecodeSpecial(i, field[i], &val); if (type == IGNORE_DTF) continue;
/* daylight savings time modifier disallowed with full TZ */ if (fmask & DTK_M(DTZMOD)) return DTERR_BAD_FORMAT;
/* if non-DST zone, we do not need to know the date */ if (pg_get_timezone_offset(namedTz, &gmtoff))
{
*tzp = -(int) gmtoff;
} else
{ /* a date has to be specified */ if ((fmask & DTK_DATE_M) != DTK_DATE_M) return DTERR_BAD_FORMAT;
*tzp = DetermineTimeZoneOffset(tm, namedTz);
}
}
/* *daylightsavingstimemodifierbutnostandardtimezone?thenerror
*/ if (fmask & DTK_M(DTZMOD)) return DTERR_BAD_FORMAT;
if ((fmask & DTK_DATE_M) == 0)
GetCurrentDateTime(tmp); else
{ /* a date has to be specified */ if ((fmask & DTK_DATE_M) != DTK_DATE_M) return DTERR_BAD_FORMAT;
tmp->tm_year = tm->tm_year;
tmp->tm_mon = tm->tm_mon;
tmp->tm_mday = tm->tm_mday;
}
tmp->tm_hour = tm->tm_hour;
tmp->tm_min = tm->tm_min;
tmp->tm_sec = tm->tm_sec;
*tzp = DetermineTimeZoneAbbrevOffset(tmp, abbrev, abbrevTz);
tm->tm_isdst = tmp->tm_isdst;
}
/* timezone not specified? then use session timezone */ if (tzp != NULL && !(fmask & DTK_M(TZ)))
{ struct pg_tm tt,
*tmp = &tt;
/* *daylightsavingstimemodifierbutnostandardtimezone?thenerror
*/ if (fmask & DTK_M(DTZMOD)) return DTERR_BAD_FORMAT;
if ((fmask & DTK_DATE_M) == 0)
GetCurrentDateTime(tmp); else
{ /* a date has to be specified */ if ((fmask & DTK_DATE_M) != DTK_DATE_M) return DTERR_BAD_FORMAT;
tmp->tm_year = tm->tm_year;
tmp->tm_mon = tm->tm_mon;
tmp->tm_mday = tm->tm_mday;
}
tmp->tm_hour = tm->tm_hour;
tmp->tm_min = tm->tm_min;
tmp->tm_sec = tm->tm_sec;
*tzp = DetermineTimeZoneOffset(tmp, session_timezone);
tm->tm_isdst = tmp->tm_isdst;
}
return0;
}
/* DecodeDate() *Decodedatestringwhichincludesdelimiters. *Return0ifokay,aDTERRcodeifnot. * *str:fieldtobeparsed *fmask:bitmaskforfieldtypesalreadyseen **tmask:receivesbitmaskforfieldsfoundhere **is2digits:settotrueifwefind2-digityear **tm:fieldvaluesarestoredintoappropriatemembersofthisstruct
*/ staticint
DecodeDate(char *str, int fmask, int *tmask, bool *is2digits, struct pg_tm *tm)
{
fsec_t fsec; int nf = 0; int i,
len; int dterr; bool haveTextMonth = false; int type,
val,
dmask = 0; char *field[MAXDATEFIELDS];
*tmask = 0;
/* parse this string... */ while (*str != '\0' && nf < MAXDATEFIELDS)
{ /* skip field separators */ while (*str != '\0' && !isalnum((unsignedchar) *str))
str++;
if (*str == '\0') return DTERR_BAD_FORMAT; /* end of string after separator */
field[nf] = str; if (isdigit((unsignedchar) *str))
{ while (isdigit((unsignedchar) *str))
str++;
} elseif (isalpha((unsignedchar) *str))
{ while (isalpha((unsignedchar) *str))
str++;
}
/* Just get rid of any non-digit, non-alpha characters... */ if (*str != '\0')
*str++ = '\0';
nf++;
}
/* look first for text fields, since that will be unambiguous month */ for (i = 0; i < nf; i++)
{ if (isalpha((unsignedchar) *field[i]))
{
type = DecodeSpecial(i, field[i], &val); if (type == IGNORE_DTF) continue;
if ((fmask & ~(DTK_M(DOY) | DTK_M(TZ))) != DTK_DATE_M) return DTERR_BAD_FORMAT;
/* validation of the field values must wait until ValidateDate() */
return0;
}
/* ValidateDate() *Checkvalidyear/month/dayvalues,handleBCandDOYcases *Return0ifokay,aDTERRcodeifnot.
*/ int
ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc, struct pg_tm *tm)
{ if (fmask & DTK_M(YEAR))
{ if (isjulian)
{ /* tm_year is correct and should not be touched */
} elseif (bc)
{ /* there is no year zero in AD/BC notation */ if (tm->tm_year <= 0) return DTERR_FIELD_OVERFLOW; /* internally, we represent 1 BC as year zero, 2 BC as -1, etc */
tm->tm_year = -(tm->tm_year - 1);
} elseif (is2digits)
{ /* process 1 or 2-digit input as 1970-2069 AD, allow '0' and '00' */ if (tm->tm_year < 0) /* just paranoia */ return DTERR_FIELD_OVERFLOW; if (tm->tm_year < 70)
tm->tm_year += 2000; elseif (tm->tm_year < 100)
tm->tm_year += 1900;
} else
{ /* there is no year zero in AD/BC notation */ if (tm->tm_year <= 0) return DTERR_FIELD_OVERFLOW;
}
}
/* 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;
}
/* 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 ... */ return0;
}
/* Switch based on what we have so far */ switch (fmask & DTK_DATE_M)
{ case0:
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)
{ /* *Weareatthefirstnumericfieldofadatethatincludeda *textualmonthname.Wewanttosupportthevariants *MON-DD-YYYY,DD-MON-YYYY,andYYYY-MON-DDasunambiguous *inputs.WewillalsoacceptMON-DD-YYorDD-MON-YYin *eitherDMYorMDYmodes,aswellasYY-MON-DDinYMDmode.
*/ 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; return0;
/* *Haveadecimalpoint?Thenthisisadateorsomethingwithaseconds *field...
*/ if ((cp = strchr(str, '.')) != NULL)
{ int dterr;
/* Convert the fraction and store at *fsec */
dterr = ParseFractionalSecond(cp, fsec); if (dterr) return dterr; /* Now truncate off the fraction for further processing */
*cp = '\0';
len = strlen(str);
} /* No decimal point and no complete date yet? */ elseif ((fmask & DTK_DATE_M) != DTK_DATE_M)
{ if (len >= 6)
{
*tmask = DTK_DATE_M;
if (type == TZ || type == DTZ)
{ /* fixed-offset abbreviation, return the offset */ return TZNAME_FIXED_OFFSET;
} elseif (type == DYNTZ)
{ /* dynamic-offset abbreviation, return its referenced timezone */ return TZNAME_DYNTZ;
} else
{ /* try it as a full zone name */
*tz = pg_tzset(tzname); if (*tz == NULL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("time zone \"%s\" not recognized", tzname))); return TZNAME_ZONE;
}
}
if (DecodeTimezoneName(tzname, &offset, &result) == TZNAME_FIXED_OFFSET)
{ /* fixed-offset abbreviation, get a pg_tz descriptor for that */
result = pg_tzset_offset(-offset); /* flip to POSIX sign convention */
} return result;
}
/* DecodeTimezoneAbbrevPrefix() *Interpretprefixofstringasatimezoneabbreviation,ifpossible. * *ThishasroughlythesamefunctionalityasDecodeTimezoneAbbrev(), *buttheAPIisadaptedtotheneedsofformatting.c.Notably, *wewillmatchthelongestpossibleprefixofthegivenstring *ratherthaninsistingonacompletematch,anddowncasingisapplied *hereratherthaninthecaller. * *Returnsthelengthofthetimezoneabbreviation,or-1ifnotrecognized. *Onsuccess,sets*offsettotheGMToffsetfortheabbreviationifit *isafixed-offsetabbreviation,orsets*tztothepg_tzstructfor *adynamicabbreviation.
*/ int
DecodeTimezoneAbbrevPrefix(constchar *str, int *offset, pg_tz **tz)
{ char lowtoken[TOKMAXLEN + 1]; int len;
*offset = 0; /* avoid uninitialized vars on failure */
*tz = NULL;
/* Downcase as much of the string as we could need */ for (len = 0; len < TOKMAXLEN; len++)
{ if (*str == '\0' || !isalpha((unsignedchar) *str)) break;
lowtoken[len] = pg_tolower((unsignedchar) *str++);
}
lowtoken[len] = '\0';
/* *Wecouldavoiddoingrepeatedbinarysearchesifwecaredtoduplicate *datebsearchhere,butit'snotclearthatsuchanoptimizationwouldbe *worththetrouble.Incommoncasesthere'sprobablynotanythingafter *thezoneabbrevanyway.Sojustsearchwithsuccessivelytruncated *strings.
*/ while (len > 0)
{ bool isfixed; int isdst; const datetkn *tp;
/* See if the current session_timezone recognizes it. */ if (session_timezone &&
TimeZoneAbbrevIsKnown(lowtoken, session_timezone,
&isfixed, offset, &isdst))
{ if (isfixed)
{ /* flip sign to agree with the convention in zoneabbrevtbl */
*offset = -(*offset);
} else
{ /* Caller must resolve the abbrev's current meaning */
*tz = session_timezone;
} return len;
}
/* Known in zoneabbrevtbl? */ if (zoneabbrevtbl)
tp = datebsearch(lowtoken, zoneabbrevtbl->abbrevs,
zoneabbrevtbl->numabbrevs); else
tp = NULL; if (tp != NULL)
{ if (tp->type == DYNTZ)
{
DateTimeErrorExtra extra;
pg_tz *tzp = FetchDynamicTimeZone(zoneabbrevtbl, tp,
&extra);
if (tzp != NULL)
{ /* Caller must resolve the abbrev's current meaning */
*tz = tzp; return len;
}
} else
{ /* Fixed-offset zone abbrev, so it's easy */
*offset = tp->value; return len;
}
}
/* Nope, try the next shorter string. */
lowtoken[--len] = '\0';
}
/* read through list backwards to pick up units before values */ for (i = nf - 1; i >= 0; i--)
{ switch (ftype[i])
{ case DTK_TIME:
dterr = DecodeTimeForInterval(field[i], fmask, range,
&tmask, itm_in); if (dterr) return dterr; if (force_negative &&
itm_in->tm_usec > 0)
itm_in->tm_usec = -itm_in->tm_usec;
type = DTK_DAY;
parsing_unit_val = false; break;
case DTK_DATE: case DTK_NUMBER: if (type == IGNORE_DTF)
{ /* use typmod to decide what rightmost field is */ switch (range)
{ case INTERVAL_MASK(YEAR):
type = DTK_YEAR; break; case INTERVAL_MASK(MONTH): case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
type = DTK_MONTH; break; case INTERVAL_MASK(DAY):
type = DTK_DAY; break; case INTERVAL_MASK(HOUR): case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
type = DTK_HOUR; break; case INTERVAL_MASK(MINUTE): case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
type = DTK_MINUTE; break; case INTERVAL_MASK(SECOND): case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
type = DTK_SECOND; break; default:
type = DTK_SECOND; break;
}
}
errno = 0;
val = strtoi64(field[i], &cp, 10); if (errno == ERANGE) return DTERR_FIELD_OVERFLOW;
if (*cp == '-')
{ /* SQL "years-months" syntax */ int val2;
case DTK_MINUTE: if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in)) return DTERR_FIELD_OVERFLOW;
tmask = DTK_M(MINUTE); break;
case DTK_HOUR: if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in)) return DTERR_FIELD_OVERFLOW;
tmask = DTK_M(HOUR);
type = DTK_DAY; /* set for next field */ break;
case DTK_DAY: if (!AdjustDays(val, 1, itm_in) ||
!AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in)) return DTERR_FIELD_OVERFLOW;
tmask = DTK_M(DAY); break;
case DTK_WEEK: if (!AdjustDays(val, 7, itm_in) ||
!AdjustFractDays(fval, 7, itm_in)) return DTERR_FIELD_OVERFLOW;
tmask = DTK_M(WEEK); break;
case DTK_MONTH: if (!AdjustMonths(val, itm_in) ||
!AdjustFractDays(fval, DAYS_PER_MONTH, itm_in)) return DTERR_FIELD_OVERFLOW;
tmask = DTK_M(MONTH); break;
case DTK_YEAR: if (!AdjustYears(val, 1, itm_in) ||
!AdjustFractYears(fval, 1, itm_in)) return DTERR_FIELD_OVERFLOW;
tmask = DTK_M(YEAR); break;
case DTK_DECADE: if (!AdjustYears(val, 10, itm_in) ||
!AdjustFractYears(fval, 10, itm_in)) return DTERR_FIELD_OVERFLOW;
tmask = DTK_M(DECADE); break;
case DTK_CENTURY: if (!AdjustYears(val, 100, itm_in) ||
!AdjustFractYears(fval, 100, itm_in)) return DTERR_FIELD_OVERFLOW;
tmask = DTK_M(CENTURY); break;
case DTK_MILLENNIUM: if (!AdjustYears(val, 1000, itm_in) ||
!AdjustFractYears(fval, 1000, itm_in)) return DTERR_FIELD_OVERFLOW;
tmask = DTK_M(MILLENNIUM); break;
case DTK_STRING: case DTK_SPECIAL: /* reject consecutive unhandled units */ if (parsing_unit_val) return DTERR_BAD_FORMAT;
type = DecodeUnits(i, field[i], &uval); if (type == UNKNOWN_FIELD)
type = DecodeSpecial(i, field[i], &uval); if (type == IGNORE_DTF) continue;
tmask = 0; /* DTK_M(type); */ switch (type)
{ case UNITS:
type = uval;
parsing_unit_val = true; break;
case AGO:
/* *"ago"isonlyallowedtoappearattheendofthe *interval.
*/ if (i != nf - 1) return DTERR_BAD_FORMAT;
is_before = true;
type = uval; break;
/* *Historicallythishasacceptedanythingthatstrtod()wouldtake, *notablyincluding"e"notation,socontinuedoingthat.Thisis *slightlyannoyingbecausetheprecisionofdoubleislessthanthatof *int64,sowewouldloseaccuracyforinputslargerthan2^53orso. *However,historicallywerejectedinputsoutsidetheint32range, *makingthatconcernmoot.Whatwedonowisrejectabs(val)above *1.0e15(aroundnumberabitlessthan2^50),sothatanyaccepted *valuewillhaveanexactintegerpart,andtherebyafractionpartwith *abs(*fpart)lessthan1.Intheabsenceoffieldcomplaintsitdoesn't *seemworthworkingharder.
*/ if (!(isdigit((unsignedchar) *str) || *str == '-' || *str == '.')) return DTERR_BAD_FORMAT;
errno = 0;
val = strtod(str, endptr); /* did we not see anything that looks like a double? */ if (*endptr == str || errno != 0) return DTERR_BAD_FORMAT; /* watch out for overflow, including infinities; reject NaN too */ if (isnan(val) || val < -1.0e15 || val > 1.0e15) return DTERR_FIELD_OVERFLOW; /* be very sure we truncate towards zero (cf dtrunc()) */ if (val >= 0)
*ipart = (int64) floor(val); else
*ipart = (int64) -floor(-val);
*fpart = val - *ipart; /* Callers expect this to hold */
Assert(*fpart > -1.0 && *fpart < 1.0); return0;
}
/* *DeterminenumberofintegraldigitsinavalidISO8601numberfield *(weshouldignoresignandanyfractionpart)
*/ staticint
ISO8601IntegerWidth(char *fieldstart)
{ /* We might have had a leading '-' */ if (*fieldstart == '-')
fieldstart++; return strspn(fieldstart, "0123456789");
}
/* *Note:wecouldstepofftheendofthestringhere.Codebelow **must*exittheloopifunit=='\0'.
*/
unit = *str++;
if (datepart)
{ switch (unit) /* before T: Y M W D */
{ case'Y': if (!AdjustYears(val, 1, itm_in) ||
!AdjustFractYears(fval, 1, itm_in)) return DTERR_FIELD_OVERFLOW; break; case'M': if (!AdjustMonths(val, itm_in) ||
!AdjustFractDays(fval, DAYS_PER_MONTH, itm_in)) return DTERR_FIELD_OVERFLOW; break; case'W': if (!AdjustDays(val, 7, itm_in) ||
!AdjustFractDays(fval, 7, itm_in)) return DTERR_FIELD_OVERFLOW; break; case'D': if (!AdjustDays(val, 1, itm_in) ||
!AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in)) return DTERR_FIELD_OVERFLOW; break; case'T': /* ISO 8601 4.4.3.3 Alternative Format / Basic */ case'\0': if (ISO8601IntegerWidth(fieldstart) == 8 && !havefield)
{ if (!AdjustYears(val / 10000, 1, itm_in) ||
!AdjustMonths((val / 100) % 100, itm_in) ||
!AdjustDays(val % 100, 1, itm_in) ||
!AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in)) return DTERR_FIELD_OVERFLOW; if (unit == '\0') return0;
datepart = false;
havefield = false; continue;
} /* Else fall through to extended alternative format */ /* FALLTHROUGH */ case'-': /* ISO 8601 4.4.3.3 Alternative Format,
* Extended */ if (havefield) return DTERR_BAD_FORMAT;
if (!AdjustYears(val, 1, itm_in) ||
!AdjustFractYears(fval, 1, itm_in)) return DTERR_FIELD_OVERFLOW; if (unit == '\0') return0; if (unit == 'T')
{
datepart = false;
havefield = false; continue;
}
dterr = ParseISO8601Number(str, &str, &val, &fval); if (dterr) return dterr; if (!AdjustMonths(val, itm_in) ||
!AdjustFractDays(fval, DAYS_PER_MONTH, itm_in)) return DTERR_FIELD_OVERFLOW; if (*str == '\0') return0; if (*str == 'T')
{
datepart = false;
havefield = false; continue;
} if (*str != '-') return DTERR_BAD_FORMAT;
str++;
dterr = ParseISO8601Number(str, &str, &val, &fval); if (dterr) return dterr; if (!AdjustDays(val, 1, itm_in) ||
!AdjustFractMicroseconds(fval, USECS_PER_DAY, itm_in)) return DTERR_FIELD_OVERFLOW; if (*str == '\0') return0; if (*str == 'T')
{
datepart = false;
havefield = false; continue;
} return DTERR_BAD_FORMAT; default: /* not a valid date unit suffix */ return DTERR_BAD_FORMAT;
}
} else
{ switch (unit) /* after T: H M S */
{ case'H': if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in)) return DTERR_FIELD_OVERFLOW; break; case'M': if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in)) return DTERR_FIELD_OVERFLOW; break; case'S': if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in)) return DTERR_FIELD_OVERFLOW; break; case'\0': /* ISO 8601 4.4.3.3 Alternative Format */ if (ISO8601IntegerWidth(fieldstart) == 6 && !havefield)
{ if (!AdjustMicroseconds(val / 10000, 0, USECS_PER_HOUR, itm_in) ||
!AdjustMicroseconds((val / 100) % 100, 0, USECS_PER_MINUTE, itm_in) ||
!AdjustMicroseconds(val % 100, 0, USECS_PER_SEC, itm_in) ||
!AdjustFractMicroseconds(fval, 1, itm_in)) return DTERR_FIELD_OVERFLOW; return0;
} /* Else fall through to extended alternative format */ /* FALLTHROUGH */ case':': /* ISO 8601 4.4.3.3 Alternative Format,
* Extended */ if (havefield) return DTERR_BAD_FORMAT;
if (!AdjustMicroseconds(val, fval, USECS_PER_HOUR, itm_in)) return DTERR_FIELD_OVERFLOW; if (unit == '\0') return0;
dterr = ParseISO8601Number(str, &str, &val, &fval); if (dterr) return dterr; if (!AdjustMicroseconds(val, fval, USECS_PER_MINUTE, itm_in)) return DTERR_FIELD_OVERFLOW; if (*str == '\0') return0; if (*str != ':') return DTERR_BAD_FORMAT;
str++;
dterr = ParseISO8601Number(str, &str, &val, &fval); if (dterr) return dterr; if (!AdjustMicroseconds(val, fval, USECS_PER_SEC, itm_in)) return DTERR_FIELD_OVERFLOW; if (*str == '\0') return0; return DTERR_BAD_FORMAT;
default: /* not a valid time unit suffix */ return DTERR_BAD_FORMAT;
}
}
havefield = true;
}
return0;
}
/* DecodeUnits() *Decodetextstringusinglookuptable. * *Thisroutinerecognizeskeywordsassociatedwithtimeintervalunits. * *Givenstringmustbelowercasedalready. * *Implementacachelookupsinceitislikelythatdates *willberelatedinformat.
*/ int
DecodeUnits(int field, constchar *lowtoken, int *val)
{ int type; const datetkn *tp;
tp = deltacache[field]; /* use strncmp so that we match truncated tokens */ if (tp == NULL || strncmp(lowtoken, tp->token, TOKMAXLEN) != 0)
{
tp = datebsearch(lowtoken, deltatktbl, szdeltatktbl);
} if (tp == NULL)
{
type = UNKNOWN_FIELD;
*val = 0;
} else
{
deltacache[field] = tp;
type = tp->type;
*val = tp->value;
}
return type;
} /* DecodeUnits() */
/* *Reportanerrordetectedbyoneofthedatetimeinputprocessingroutines. * *dterristheerrorcode,and*extracontainsanyauxiliaryinfoweneed *fortheerrorreport.extracanbeNULLifnotneededfortheparticular *dterrvalue. * *stristheoriginalinputstring,anddatatypeisthenameofthedatatype *weweretryingtoaccept.(ForsomeDTERRcodes,thesearenotusedand *canbeNULL.) * *IfescontextpointstoanErrorSaveContextnode,thatisfilledinstead *ofthrowinganerror. * *Note:itmightseemuselesstodistinguishDTERR_INTERVAL_OVERFLOWand *DTERR_TZDISP_OVERFLOWfromDTERR_FIELD_OVERFLOW,butSQL99mandatesthree *separateSQLSTATEcodes,so...
*/ void
DateTimeParseError(int dterr, DateTimeErrorExtra *extra, constchar *str, constchar *datatype,
Node *escontext)
{ switch (dterr)
{ case DTERR_FIELD_OVERFLOW:
errsave(escontext,
(errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
errmsg("date/time field value out of range: \"%s\"",
str))); break; case DTERR_MD_FIELD_OVERFLOW: /* <nanny>same as above, but add hint about DateStyle</nanny> */
errsave(escontext,
(errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
errmsg("date/time field value out of range: \"%s\"",
str),
errhint("Perhaps you need a different \"DateStyle\" setting."))); break; case DTERR_INTERVAL_OVERFLOW:
errsave(escontext,
(errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW),
errmsg("interval field value out of range: \"%s\"",
str))); break; case DTERR_TZDISP_OVERFLOW:
errsave(escontext,
(errcode(ERRCODE_INVALID_TIME_ZONE_DISPLACEMENT_VALUE),
errmsg("time zone displacement out of range: \"%s\"",
str))); break; case DTERR_BAD_TIMEZONE:
errsave(escontext,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("time zone \"%s\" not recognized",
extra->dtee_timezone))); break; case DTERR_BAD_ZONE_ABBREV:
errsave(escontext,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("time zone \"%s\" not recognized",
extra->dtee_timezone),
errdetail("This time zone name appears in the configuration file for time zone abbreviation \"%s\".",
extra->dtee_abbrev))); break; case DTERR_BAD_FORMAT: default:
errsave(escontext,
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
errmsg("invalid input syntax for type %s: \"%s\"",
datatype, str))); break;
}
}
/* datebsearch() *Binarysearch--fromKnuth(6.2.1)AlgorithmB.Specialcaselikethis *isWAYfasterthanthegenericbsearch().
*/ staticconst datetkn *
datebsearch(constchar *key, const datetkn *base, int nel)
{ if (nel > 0)
{ const datetkn *last = base + nel - 1,
*position; int result;
while (last >= base)
{
position = base + ((last - base) >> 1); /* precheck the first character for a bit of extra speed */
result = (int) key[0] - (int) position->token[0]; if (result == 0)
{ /* use strncmp so that we match truncated tokens */
result = strncmp(key, position->token, TOKMAXLEN); if (result == 0) return position;
} if (result < 0)
last = position - 1; else
base = position + 1;
}
} return NULL;
}
/* EncodeTimezone() *Copiesrepresentationofanumerictimezoneoffsettostr. * *Returnsapointertothenewendofstring.NoNULterminatorisput *there;callersareresponsibleforNULterminatingstrthemselves.
*/ staticchar *
EncodeTimezone(char *str, int tz, int style)
{ int hour,
min,
sec;
sec = abs(tz);
min = sec / SECS_PER_MINUTE;
sec -= min * SECS_PER_MINUTE;
hour = min / MINS_PER_HOUR;
min -= hour * MINS_PER_HOUR;
/* TZ is negated compared to sign we wish to display ... */
*str++ = (tz <= 0 ? '+' : '-');
/* Compatible with postgresql < 8.4 when DateStyle != 'iso' */ case INTSTYLE_POSTGRES_VERBOSE: default:
strcpy(cp, "@");
cp++;
cp = AddVerboseIntPart(cp, year, "year", &is_zero, &is_before);
cp = AddVerboseIntPart(cp, mon, "mon", &is_zero, &is_before);
cp = AddVerboseIntPart(cp, mday, "day", &is_zero, &is_before);
cp = AddVerboseIntPart(cp, hour, "hour", &is_zero, &is_before);
cp = AddVerboseIntPart(cp, min, "min", &is_zero, &is_before); if (sec != 0 || fsec != 0)
{
*cp++ = ' '; if (sec < 0 || (sec == 0 && fsec < 0))
{ if (is_zero)
is_before = true; elseif (!is_before)
*cp++ = '-';
} elseif (is_before)
*cp++ = '-';
cp = AppendSeconds(cp, sec, fsec, MAX_INTERVAL_PRECISION, false); /* We output "ago", not negatives, so use abs(). */
sprintf(cp, " sec%s",
(abs(sec) != 1 || fsec != 0) ? "s" : "");
is_zero = false;
} /* identically zero? then put in a unitless zero... */ if (is_zero)
strcat(cp, " 0"); if (is_before)
strcat(cp, " ago"); break;
}
}
/* *We'vebeenburntbystupiderrorsintheorderingofthedatetkntables *oncetoooften.Arrangetocheckthemduringpostmasterstart.
*/ staticbool
CheckDateTokenTable(constchar *tablename, const datetkn *base, int nel)
{ bool ok = true; int i;
for (i = 0; i < nel; i++)
{ /* check for token strings that don't fit */ if (strlen(base[i].token) > TOKMAXLEN)
{ /* %.*s is safe since all our tokens are ASCII */
elog(LOG, "token too long in %s table: \"%.*s\"",
tablename,
TOKMAXLEN + 1, base[i].token);
ok = false; break; /* don't risk applying strcmp */
} /* check for out of order */ if (i > 0 &&
strcmp(base[i - 1].token, base[i].token) >= 0)
{
elog(LOG, "ordering error in %s table: \"%s\" >= \"%s\"",
tablename,
base[i - 1].token,
base[i].token);
ok = false;
}
} return ok;
}
/* Space for fixed fields and datetkn array */
tbl_size = offsetof(TimeZoneAbbrevTable, abbrevs) +
n * sizeof(datetkn);
tbl_size = MAXALIGN(tbl_size); /* Count up space for dynamic abbreviations */ for (i = 0; i < n; i++)
{ struct tzEntry *abbr = abbrevs + i;
/* Alloc the result ... */
tbl = guc_malloc(LOG, tbl_size); if (!tbl) return NULL;
/* ... and fill it in */
tbl->tblsize = tbl_size;
tbl->numabbrevs = n; /* in this loop, tbl_size reprises the space calculation above */
tbl_size = offsetof(TimeZoneAbbrevTable, abbrevs) +
n * sizeof(datetkn);
tbl_size = MAXALIGN(tbl_size); for (i = 0; i < n; i++)
{ struct tzEntry *abbr = abbrevs + i;
datetkn *dtoken = tbl->abbrevs + i;
/* use strlcpy to truncate name if necessary */
strlcpy(dtoken->token, abbr->abbrev, TOKMAXLEN + 1); if (abbr->zone != NULL)
{ /* Allocate a DynamicZoneAbbrev for this abbreviation */
DynamicZoneAbbrev *dtza;
Size dsize;
/* Look up the underlying zone if we haven't already */ if (dtza->tz == NULL)
{
dtza->tz = pg_tzset(dtza->zone); if (dtza->tz == NULL)
{ /* Ooops, bogus zone name in config file entry */
extra->dtee_timezone = dtza->zone;
extra->dtee_abbrev = tp->token;
}
} return dtza->tz;
}
/* *Thisset-returningfunctionreadsallthetimezoneabbreviations *definedbytheIANAdataforthecurrenttimezonesetting, *andreturnsasetof(abbrev,utc_offset,is_dst).
*/
Datum
pg_timezone_abbrevs_zone(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx; int *pindex;
Datum result;
HeapTuple tuple;
Datum values[3]; bool nulls[3] = {0};
TimestampTz now = GetCurrentTransactionStartTimestamp();
pg_time_t t = timestamptz_to_time_t(now); constchar *abbrev; longint gmtoff; int isdst; struct pg_itm_in itm_in;
Interval *resInterval;
/* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL())
{
TupleDesc tupdesc;
MemoryContext oldcontext;
/* create a function context for cross-call persistence */
funcctx = SRF_FIRSTCALL_INIT();
/* allocate memory for user context */
pindex = (int *) palloc(sizeof(int));
*pindex = 0;
funcctx->user_fctx = pindex;
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
elog(ERROR, "return type must be a row type");
funcctx->tuple_desc = tupdesc;
MemoryContextSwitchTo(oldcontext);
}
/* stuff done on every call of the function */
funcctx = SRF_PERCALL_SETUP();
pindex = (int *) funcctx->user_fctx;
while ((abbrev = pg_get_next_timezone_abbrev(pindex,
session_timezone)) != NULL)
{ /* Ignore abbreviations that aren't all-alphabetic */ if (strspn(abbrev, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") != strlen(abbrev)) continue;
/* Determine the current meaning of the abbrev */ if (!pg_interpret_timezone_abbrev(abbrev,
&t,
&gmtoff,
&isdst,
session_timezone)) continue; /* hm, not actually used in this zone? */
/* allocate memory for user context */
pindex = (int *) palloc(sizeof(int));
*pindex = 0;
funcctx->user_fctx = pindex;
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
elog(ERROR, "return type must be a row type");
funcctx->tuple_desc = tupdesc;
MemoryContextSwitchTo(oldcontext);
}
/* stuff done on every call of the function */
funcctx = SRF_PERCALL_SETUP();
pindex = (int *) funcctx->user_fctx;
if (zoneabbrevtbl == NULL ||
*pindex >= zoneabbrevtbl->numabbrevs)
SRF_RETURN_DONE(funcctx);
tp = zoneabbrevtbl->abbrevs + *pindex;
switch (tp->type)
{ case TZ:
gmtoffset = tp->value;
is_dst = false; break; case DTZ:
gmtoffset = tp->value;
is_dst = true; break; case DYNTZ:
{ /* Determine the current meaning of the abbrev */
pg_tz *tzp;
DateTimeErrorExtra extra;
TimestampTz now; int isdst;
/* search for another zone to display */ for (;;)
{
tz = pg_tzenumerate_next(tzenum); if (!tz) break;
/* Convert now() to local time in this zone */ if (timestamp2tm(GetCurrentTransactionStartTimestamp(),
&tzoff, &tm, &fsec, &tzn, tz) != 0) continue; /* ignore if conversion fails */
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.