/* copy&pasted from .../src/backend/utils/adt/datetime.c * with 3 exceptions * * * changesd struct pg_tm to struct tm * * * ECPG code called this without a 'range' parameter * removed 'int range' from the argument list and * places where DecodeTime is called; and added * int range = INTERVAL_FULL_RANGE; * * * ECPG semes not to have a global IntervalStyle * so added * int IntervalStyle = INTSTYLE_POSTGRES; * * * Assert wasn't available so removed it. */ int DecodeInterval(char **field, int *ftype, int nf, /* int range, */ int *dtype, struct /* pg_ */ tm * tm, fsec_t *fsec) { int IntervalStyle = INTSTYLE_POSTGRES_VERBOSE; int range = INTERVAL_FULL_RANGE; bool is_before = FALSE; char *cp; int fmask = 0, tmask, type; int i; int dterr; int val; double fval; *dtype = DTK_DELTA; type = IGNORE_DTF; ClearPgTm(tm, fsec); /* read through list backwards to pick up units before values */ for (i = nf - 1; i >= 0; i--) { switch (ftype[i]) { case DTK_TIME: dterr = DecodeTime(field[i], /* range, */ &tmask, tm, fsec); if (dterr) return dterr; type = DTK_DAY; break; case DTK_TZ: /* * Timezone is a token with a leading sign character and at * least one digit; there could be ':', '.', '-' embedded in * it as well. */ /* Assert(*field[i] == '-' || *field[i] == '+'); */ /* * Try for hh:mm or hh:mm:ss. If not, fall through to * DTK_NUMBER case, which can handle signed float numbers and * signed year-month values. */ if (strchr(field[i] + 1, ':') != NULL && DecodeTime(field[i] + 1, /* INTERVAL_FULL_RANGE, */ &tmask, tm, fsec) == 0) { if (*field[i] == '-') { /* flip the sign on all fields */ tm->tm_hour = -tm->tm_hour; tm->tm_min = -tm->tm_min; tm->tm_sec = -tm->tm_sec; *fsec = -(*fsec); } /* * Set the next type to be a day, if units are not * specified. This handles the case of '1 +02:03' since we * are reading right to left. */ type = DTK_DAY; tmask = DTK_M(TZ); break; } /* FALL THROUGH */ 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): case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): type = DTK_HOUR; break; case INTERVAL_MASK(MINUTE): case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): type = DTK_MINUTE; break; case INTERVAL_MASK(SECOND): case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): type = DTK_SECOND; break; default: type = DTK_SECOND; break; } } errno = 0; val = strtoint(field[i], &cp, 10); if (errno == ERANGE) return DTERR_FIELD_OVERFLOW; if (*cp == '-') { /* SQL "years-months" syntax */ int val2; val2 = strtoint(cp + 1, &cp, 10); if (errno == ERANGE || val2 < 0 || val2 >= MONTHS_PER_YEAR) return DTERR_FIELD_OVERFLOW; if (*cp != '\0') return DTERR_BAD_FORMAT; type = DTK_MONTH; if (*field[i] == '-') val2 = -val2; val = val * MONTHS_PER_YEAR + val2; fval = 0; } else if (*cp == '.') { errno = 0; fval = strtod(cp, &cp); if (*cp != '\0' || errno != 0) return DTERR_BAD_FORMAT; if (*field[i] == '-') fval = -fval; } else if (*cp == '\0') fval = 0; else return DTERR_BAD_FORMAT; tmask = 0; /* DTK_M(type); */ switch (type) { case DTK_MICROSEC: #ifdef HAVE_INT64_TIMESTAMP *fsec += rint(val + fval); #else *fsec += (val + fval) * 1e-6; #endif tmask = DTK_M(MICROSECOND); break; case DTK_MILLISEC: #ifdef HAVE_INT64_TIMESTAMP *fsec += rint((val + fval) * 1000); #else *fsec += (val + fval) * 1e-3; #endif tmask = DTK_M(MILLISECOND); break; case DTK_SECOND: tm->tm_sec += val; #ifdef HAVE_INT64_TIMESTAMP *fsec += rint(fval * 1000000); #else *fsec += fval; #endif /* * If any subseconds were specified, consider this * microsecond and millisecond input as well. */ if (fval == 0) tmask = DTK_M(SECOND); else tmask = DTK_ALL_SECS_M; break; case DTK_MINUTE: tm->tm_min += val; AdjustFractSeconds(fval, tm, fsec, SECS_PER_MINUTE); tmask = DTK_M(MINUTE); break; case DTK_HOUR: tm->tm_hour += val; AdjustFractSeconds(fval, tm, fsec, SECS_PER_HOUR); tmask = DTK_M(HOUR); type = DTK_DAY; break; case DTK_DAY: tm->tm_mday += val; AdjustFractSeconds(fval, tm, fsec, SECS_PER_DAY); tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY); break; case DTK_WEEK: tm->tm_mday += val * 7; AdjustFractDays(fval, tm, fsec, 7); tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY); break; case DTK_MONTH: tm->tm_mon += val; AdjustFractDays(fval, tm, fsec, DAYS_PER_MONTH); tmask = DTK_M(MONTH); break; case DTK_YEAR: tm->tm_year += val; if (fval != 0) tm->tm_mon += fval * MONTHS_PER_YEAR; tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR); break; case DTK_DECADE: tm->tm_year += val * 10; if (fval != 0) tm->tm_mon += fval * MONTHS_PER_YEAR * 10; tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR); break; case DTK_CENTURY: tm->tm_year += val * 100; if (fval != 0) tm->tm_mon += fval * MONTHS_PER_YEAR * 100; tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR); break; case DTK_MILLENNIUM: tm->tm_year += val * 1000; if (fval != 0) tm->tm_mon += fval * MONTHS_PER_YEAR * 1000; tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR); break; default: return DTERR_BAD_FORMAT; } break; case DTK_STRING: case DTK_SPECIAL: type = DecodeUnits(i, field[i], &val); if (type == IGNORE_DTF) continue; tmask = 0; /* DTK_M(type); */ switch (type) { case UNITS: type = val; break; case AGO: is_before = TRUE; type = val; break; case RESERV: tmask = (DTK_DATE_M | DTK_TIME_M); *dtype = val; break; default: return DTERR_BAD_FORMAT; } break; default: return DTERR_BAD_FORMAT; } if (tmask & fmask) return DTERR_BAD_FORMAT; fmask |= tmask; } /* ensure that at least one time field has been found */ if (fmask == 0) return DTERR_BAD_FORMAT; /* ensure fractional seconds are fractional */ if (*fsec != 0) { int sec; #ifdef HAVE_INT64_TIMESTAMP sec = *fsec / USECS_PER_SEC; *fsec -= sec * USECS_PER_SEC; #else TMODULO(*fsec, sec, 1.0); #endif tm->tm_sec += sec; } /*---------- * The SQL standard defines the interval literal * '-1 1:00:00' * to mean "negative 1 days and negative 1 hours", while Postgres * traditionally treats this as meaning "negative 1 days and positive * 1 hours". In SQL_STANDARD intervalstyle, we apply the leading sign * to all fields if there are no other explicit signs. * * We leave the signs alone if there are additional explicit signs. * This protects us against misinterpreting postgres-style dump output, * since the postgres-style output code has always put an explicit sign on * all fields following a negative field. But note that SQL-spec output * is ambiguous and can be misinterpreted on load! (So it's best practice * to dump in postgres style, not SQL style.) *---------- */ if (IntervalStyle == INTSTYLE_SQL_STANDARD && *field[0] == '-') { /* Check for additional explicit signs */ bool more_signs = false; for (i = 1; i < nf; i++) { if (*field[i] == '-' || *field[i] == '+') { more_signs = true; break; } } if (!more_signs) { /* * Rather than re-determining which field was field[0], just force * 'em all negative. */ if (*fsec > 0) *fsec = -(*fsec); if (tm->tm_sec > 0) tm->tm_sec = -tm->tm_sec; if (tm->tm_min > 0) tm->tm_min = -tm->tm_min; if (tm->tm_hour > 0) tm->tm_hour = -tm->tm_hour; if (tm->tm_mday > 0) tm->tm_mday = -tm->tm_mday; if (tm->tm_mon > 0) tm->tm_mon = -tm->tm_mon; if (tm->tm_year > 0) tm->tm_year = -tm->tm_year; } } /* finally, AGO negates everything */ if (is_before) { *fsec = -(*fsec); tm->tm_sec = -tm->tm_sec; tm->tm_min = -tm->tm_min; tm->tm_hour = -tm->tm_hour; tm->tm_mday = -tm->tm_mday; tm->tm_mon = -tm->tm_mon; tm->tm_year = -tm->tm_year; } return 0; }
/* DecodeInterval() * Interpret previously parsed fields for general time interval. * Return 0 if decoded and -1 if problems. * * Allow "date" field DTK_DATE since this could be just * an unsigned floating point number. - thomas 1997-11-16 * * Allow ISO-style time span, with implicit units on number of days * preceding an hh:mm:ss field. - thomas 1998-04-30 */ int DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fsec_t *fsec) { int is_before = FALSE; char *cp; int fmask = 0, tmask, type; int i; int val; double fval; *dtype = DTK_DELTA; type = IGNORE_DTF; tm->tm_year = 0; tm->tm_mon = 0; tm->tm_mday = 0; tm->tm_hour = 0; tm->tm_min = 0; tm->tm_sec = 0; *fsec = 0; /* read through list backwards to pick up units before values */ for (i = nf - 1; i >= 0; i--) { switch (ftype[i]) { case DTK_TIME: if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0) return -1; type = DTK_DAY; break; case DTK_TZ: /* * Timezone is a token with a leading sign character and * otherwise the same as a non-signed time field */ /* * A single signed number ends up here, but will be rejected * by DecodeTime(). So, work this out to drop through to * DTK_NUMBER, which *can* tolerate this. */ cp = field[i] + 1; while (*cp != '\0' && *cp != ':' && *cp != '.') cp++; if (*cp == ':' && DecodeTime((field[i] + 1), fmask, &tmask, tm, fsec) == 0) { if (*field[i] == '-') { /* flip the sign on all fields */ tm->tm_hour = -tm->tm_hour; tm->tm_min = -tm->tm_min; tm->tm_sec = -tm->tm_sec; *fsec = -(*fsec); } /* * Set the next type to be a day, if units are not * specified. This handles the case of '1 +02:03' since we * are reading right to left. */ type = DTK_DAY; tmask = DTK_M(TZ); break; } else if (type == IGNORE_DTF) { if (*cp == '.') { /* * Got a decimal point? Then assume some sort of * seconds specification */ type = DTK_SECOND; } else if (*cp == '\0') { /* * Only a signed integer? Then must assume a * timezone-like usage */ type = DTK_HOUR; } } /* DROP THROUGH */ case DTK_DATE: case DTK_NUMBER: val = strtol(field[i], &cp, 10); if (type == IGNORE_DTF) type = DTK_SECOND; if (*cp == '.') { fval = strtod(cp, &cp); if (*cp != '\0') return -1; if (val < 0) fval = -fval; } else if (*cp == '\0') fval = 0; else return -1; tmask = 0; /* DTK_M(type); */ switch (type) { case DTK_MICROSEC: #ifdef HAVE_INT64_TIMESTAMP *fsec += val + fval; #else *fsec += (val + fval) * 1e-6; #endif break; case DTK_MILLISEC: #ifdef HAVE_INT64_TIMESTAMP *fsec += (val + fval) * 1000; #else *fsec += (val + fval) * 1e-3; #endif break; case DTK_SECOND: tm->tm_sec += val; #ifdef HAVE_INT64_TIMESTAMP *fsec += fval * 1000000; #else *fsec += fval; #endif tmask = DTK_M(SECOND); break; case DTK_MINUTE: tm->tm_min += val; if (fval != 0) { int sec; fval *= SECS_PER_MINUTE; sec = fval; tm->tm_sec += sec; #ifdef HAVE_INT64_TIMESTAMP *fsec += ((fval - sec) * 1000000); #else *fsec += fval - sec; #endif } tmask = DTK_M(MINUTE); break; case DTK_HOUR: tm->tm_hour += val; if (fval != 0) { int sec; fval *= SECS_PER_HOUR; sec = fval; tm->tm_sec += sec; #ifdef HAVE_INT64_TIMESTAMP *fsec += (fval - sec) * 1000000; #else *fsec += fval - sec; #endif } tmask = DTK_M(HOUR); break; case DTK_DAY: tm->tm_mday += val; if (fval != 0) { int sec; fval *= SECS_PER_DAY; sec = fval; tm->tm_sec += sec; #ifdef HAVE_INT64_TIMESTAMP *fsec += (fval - sec) * 1000000; #else *fsec += fval - sec; #endif } tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY); break; case DTK_WEEK: tm->tm_mday += val * 7; if (fval != 0) { int extra_days; fval *= 7; extra_days = (int32) fval; tm->tm_mday += extra_days; fval -= extra_days; if (fval != 0) { int sec; fval *= SECS_PER_DAY; sec = fval; tm->tm_sec += sec; #ifdef HAVE_INT64_TIMESTAMP *fsec += (fval - sec) * 1000000; #else *fsec += fval - sec; #endif } } tmask = (fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY); break; case DTK_MONTH: tm->tm_mon += val; if (fval != 0) { int day; fval *= DAYS_PER_MONTH; day = fval; tm->tm_mday += day; fval -= day; if (fval != 0) { int sec; fval *= SECS_PER_DAY; sec = fval; tm->tm_sec += sec; #ifdef HAVE_INT64_TIMESTAMP *fsec += (fval - sec) * 1000000; #else *fsec += fval - sec; #endif } } tmask = DTK_M(MONTH); break; case DTK_YEAR: tm->tm_year += val; if (fval != 0) tm->tm_mon += fval * MONTHS_PER_YEAR; tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR); break; case DTK_DECADE: tm->tm_year += val * 10; if (fval != 0) tm->tm_mon += fval * MONTHS_PER_YEAR * 10; tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR); break; case DTK_CENTURY: tm->tm_year += val * 100; if (fval != 0) tm->tm_mon += fval * MONTHS_PER_YEAR * 100; tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR); break; case DTK_MILLENNIUM: tm->tm_year += val * 1000; if (fval != 0) tm->tm_mon += fval * MONTHS_PER_YEAR * 1000; tmask = (fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR); break; default: return -1; } break; case DTK_STRING: case DTK_SPECIAL: type = DecodeUnits(i, field[i], &val); if (type == IGNORE_DTF) continue; tmask = 0; /* DTK_M(type); */ switch (type) { case UNITS: type = val; break; case AGO: is_before = TRUE; type = val; break; case RESERV: tmask = (DTK_DATE_M || DTK_TIME_M); *dtype = val; break; default: return -1; } break; default: return -1; } if (tmask & fmask) return -1; fmask |= tmask; } if (*fsec != 0) { int sec; #ifdef HAVE_INT64_TIMESTAMP sec = *fsec / USECS_PER_SEC; *fsec -= sec * USECS_PER_SEC; #else TMODULO(*fsec, sec, 1.0); #endif tm->tm_sec += sec; } if (is_before) { *fsec = -(*fsec); tm->tm_sec = -(tm->tm_sec); tm->tm_min = -(tm->tm_min); tm->tm_hour = -(tm->tm_hour); tm->tm_mday = -(tm->tm_mday); tm->tm_mon = -(tm->tm_mon); tm->tm_year = -(tm->tm_year); } /* ensure that at least one time field has been found */ return (fmask != 0) ? 0 : -1; } /* DecodeInterval() */