📄 datetime.c
字号:
case DOW: tm->tm_wday = val; break; case UNITS: tmask = 0; ptype = val; break; case ISOTIME: /* * This is a filler field "t" indicating that the next * field is time. Try to verify that this is sensible. */ tmask = 0; /* No preceding date? Then quit... */ if ((fmask & DTK_DATE_M) != DTK_DATE_M) return DTERR_BAD_FORMAT; /*** * We will need one of the following fields: * DTK_NUMBER should be hhmmss.fff * DTK_TIME should be hh:mm:ss.fff * DTK_DATE should be hhmmss-zz ***/ if (i >= nf - 1 || (ftype[i + 1] != DTK_NUMBER && ftype[i + 1] != DTK_TIME && ftype[i + 1] != DTK_DATE)) return DTERR_BAD_FORMAT; ptype = val; break; default: return DTERR_BAD_FORMAT; } break; default: return DTERR_BAD_FORMAT; } if (tmask & fmask) return DTERR_BAD_FORMAT; fmask |= tmask; } 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 (mer != HR24 && tm->tm_hour > 12) return DTERR_FIELD_OVERFLOW; if (mer == AM && tm->tm_hour == 12) tm->tm_hour = 0; else if (mer == PM && tm->tm_hour != 12) tm->tm_hour += 12; /* do additional checking for full date specs... */ if (*dtype == DTK_DATE) { if ((fmask & DTK_DATE_M) != DTK_DATE_M) { if ((fmask & DTK_TIME_M) == DTK_TIME_M) return 1; return DTERR_BAD_FORMAT; } /* * 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; /* timezone not specified? then find local timezone if possible */ if (tzp != NULL && !(fmask & DTK_M(TZ))) { /* * daylight savings time modifier but no standard timezone? then * error */ if (fmask & DTK_M(DTZMOD)) return DTERR_BAD_FORMAT; *tzp = DetermineTimeZoneOffset(tm, global_timezone); } } return 0;}/* DetermineTimeZoneOffset() * * Given a struct pg_tm in which tm_year, tm_mon, tm_mday, tm_hour, tm_min, and * tm_sec fields are set, attempt to determine the applicable time zone * (ie, regular or daylight-savings time) at that time. Set the struct pg_tm's * tm_isdst field accordingly, and return the actual timezone offset. * * Note: it might seem that we should use mktime() for this, but bitter * experience teaches otherwise. This code is much faster than most versions * of mktime(), anyway. */intDetermineTimeZoneOffset(struct pg_tm * tm, pg_tz *tzp){ int date, sec; pg_time_t day, mytime, prevtime, boundary, beforetime, aftertime; long int before_gmtoff, after_gmtoff; int before_isdst, after_isdst; int res; if (tzp == global_timezone && HasCTZSet) { tm->tm_isdst = 0; /* for lack of a better idea */ return CTimeZone; } /* * First, generate the pg_time_t value corresponding to the given * y/m/d/h/m/s taken as GMT time. If this overflows, punt and decide the * timezone is GMT. (We only need to worry about overflow on machines * where pg_time_t is 32 bits.) */ 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; /* * Find the DST time boundary just before or following the target time. We * assume that all zones have GMT offsets less than 24 hours, and that DST * boundaries can't be closer together than 48 hours, so backing up 24 * hours and finding the "next" boundary will work. */ prevtime = mytime - SECS_PER_DAY; if (mytime < 0 && prevtime > 0) goto overflow; res = pg_next_dst_boundary(&prevtime, &before_gmtoff, &before_isdst, &boundary, &after_gmtoff, &after_isdst, tzp); if (res < 0) goto overflow; /* failure? */ if (res == 0) { /* Non-DST zone, life is simple */ tm->tm_isdst = before_isdst; return -(int) before_gmtoff; } /* * Form the candidate pg_time_t values with local-time adjustment */ beforetime = mytime - before_gmtoff; if ((before_gmtoff > 0 && mytime < 0 && beforetime > 0) || (before_gmtoff <= 0 && mytime > 0 && beforetime < 0)) goto overflow; aftertime = mytime - after_gmtoff; if ((after_gmtoff > 0 && mytime < 0 && aftertime > 0) || (after_gmtoff <= 0 && mytime > 0 && aftertime < 0)) goto overflow; /* * If both before or both after the boundary time, we know what to do */ if (beforetime <= boundary && aftertime < boundary) { tm->tm_isdst = before_isdst; return -(int) before_gmtoff; } if (beforetime > boundary && aftertime >= boundary) { tm->tm_isdst = after_isdst; return -(int) after_gmtoff; } /* * It's an invalid or ambiguous time due to timezone transition. Prefer * the standard-time interpretation. */ if (after_isdst == 0) { tm->tm_isdst = after_isdst; return -(int) after_gmtoff; } tm->tm_isdst = before_isdst; return -(int) before_gmtoff;overflow: /* Given date is out of range, so assume UTC */ tm->tm_isdst = 0; return 0;}/* DecodeTimeOnly() * Interpret parsed string as time fields only. * Returns 0 if successful, DTERR code if bogus input detected. * * Note that support for time zone is here for * SQL92 TIME WITH TIME ZONE, but it reveals * bogosity with SQL92 date/time standards, since * we must infer a time zone from current time. * - thomas 2000-03-10 * Allow specifying date to get a better time zone, * if time zones are allowed. - thomas 2001-12-26 */intDecodeTimeOnly(char **field, int *ftype, int nf, int *dtype, struct pg_tm * tm, fsec_t *fsec, int *tzp){ int fmask = 0, tmask, type; int ptype = 0; /* "prefix type" for ISO h04mm05s06 format */ int i; int val; int dterr; int is2digits = FALSE; int mer = HR24; *dtype = DTK_TIME; tm->tm_hour = 0; tm->tm_min = 0; tm->tm_sec = 0; *fsec = 0; /* don't know daylight savings time status apriori */ tm->tm_isdst = -1; if (tzp != NULL) *tzp = 0; for (i = 0; i < nf; i++) { switch (ftype[i]) { case DTK_DATE: /* * Time zone not allowed? Then should not accept dates or time * zones no matter what else! */ 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, tm); if (dterr) return dterr; } /* otherwise, this is a time and/or time zone */ else { if (isdigit((unsigned char) *field[i])) { char *cp; /* * Starts with a digit but we already have a time * field? Then we are in trouble with time already... */ if ((fmask & DTK_TIME_M) == DTK_TIME_M) return DTERR_BAD_FORMAT; /* * Should not get here and fail. Sanity check only... */ if ((cp = strchr(field[i], '-')) == NULL) return DTERR_BAD_FORMAT; /* Get the time zone from the end of the string */ dterr = DecodeTimezone(cp, tzp); if (dterr) return dterr; *cp = '\0'; /* * Then read the rest of the field as a concatenated * time */ dterr = DecodeNumberField(strlen(field[i]), field[i], (fmask | DTK_DATE_M), &tmask, tm, fsec, &is2digits); if (dterr < 0) return dterr; ftype[i] = dterr; tmask |= DTK_M(TZ); } else { dterr = DecodePosixTimezone(field[i], tzp); if (dterr) return dterr; ftype[i] = DTK_TZ; tmask = DTK_M(TZ); } } break; case DTK_TIME: dterr = DecodeTime(field[i], (fmask | DTK_DATE_M), &tmask, tm, fsec); if (dterr) return dterr; break; case DTK_TZ: { int tz; if (tzp == NULL) return DTERR_BAD_FORMAT; dterr = DecodeTimezone(field[i], &tz); if (dterr) return dterr; /* * Already have a time zone? Then maybe this is the second * field of a POSIX time: EST+3 (equivalent to PST) */ if (i > 0 && (fmask & DTK_M(TZ)) != 0 && ftype[i - 1] == DTK_TZ && isalpha((unsigned char) *field[i - 1])) { *tzp -= tz; tmask = 0; } else { *tzp = tz; tmask = DTK_M(TZ); } } break; case DTK_NUMBER: /* * Was this an "ISO time" with embedded field labels? An * example is "h04m05s06" - thomas 2001-02-04 */ if (ptype != 0) { char *cp; int val; /* Only accept a date under limited circumstances */ switch (ptype) { case DTK_JULIAN: case DTK_YEAR: case DTK_MONTH: case DTK_DAY: if (tzp == NULL) return DTERR_BAD_FORMAT; default: break; } errno = 0; val = strtol(field[i], &cp, 10); if (errno == ERANGE) return DTERR_FIELD_OVERFLOW; /* * only a few kinds are allowed to have an embedded * decimal */ if (*cp == '.') switch (ptype) { case DTK_JULIAN: case DTK_TIME: case DTK_SECOND: break; default: return DTERR_BAD_FORMAT; break; } else if (*cp != '\0') return DTERR_BAD_FORMAT; switch (ptype) { case DTK_YEAR: tm->tm_year = val; tmask = DTK_M(YEAR); break; case DTK_MONTH: /* * already have a month and hour? then assume * minutes */ if ((fmask & DTK_M(MONTH)) != 0 && (fmask & DTK_M(HOUR)) != 0) { tm->tm_min = val; tmask = DTK_M(MINUTE); } else { tm->tm_mon = val; tmask = DTK_M(MONTH); } break; case DTK_DAY: tm->tm_mday = val; tmask = DTK_M(DAY); break; case DTK_HOUR: tm->tm_hour = val; tmask = DTK_M(HOUR); break; case DTK_MINUTE: tm->tm_min = val;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -