relative-timestamps.patch
application/octet-stream
Filename: relative-timestamps.patch
Type: application/octet-stream
Part: 0
Message:
Re: Inputting relative datetimes
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
new file mode 100644
index e737e72..ba923d0
*** a/src/backend/utils/adt/date.c
--- b/src/backend/utils/adt/date.c
*************** date_in(PG_FUNCTION_ARGS)
*** 123,133 ****
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char workbuf[MAXDATELEN + 1];
dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
! dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp);
if (dterr != 0)
DateTimeParseError(dterr, str, "date");
--- 123,135 ----
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char workbuf[MAXDATELEN + 1];
+ Interval interval,
+ *offset = &interval;
dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
! dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tzp, offset);
if (dterr != 0)
DateTimeParseError(dterr, str, "date");
*************** date_in(PG_FUNCTION_ARGS)
*** 166,171 ****
--- 168,193 ----
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("date out of range: \"%s\"", str)));
+ if (offset->month != 0 || offset->day != 0 || offset->time != 0)
+ {
+ Timestamp timestamp;
+
+ if (tm2timestamp(tm, fsec, NULL, ×tamp) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range: \"%s\"", str)));
+
+ timestamp = DatumGetTimestamp(
+ DirectFunctionCall2(timestamp_pl_interval,
+ TimestampGetDatum(timestamp),
+ PointerGetDatum(offset)));
+
+ if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range")));
+ }
+
date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
PG_RETURN_DATEADT(date);
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
new file mode 100644
index 3d320cc..a4808a0
*** a/src/backend/utils/adt/datetime.c
--- b/src/backend/utils/adt/datetime.c
*************** static const datetkn datetktbl[] = {
*** 148,153 ****
--- 148,154 ----
{"mar", MONTH, 3},
{"march", MONTH, 3},
{"may", MONTH, 5},
+ {MINUS, RESERV, DTK_MINUS}, /* "minus" an interval */
{"mm", UNITS, DTK_MINUTE}, /* "minute" for ISO input */
{"mon", DOW, 1},
{"monday", DOW, 1},
*************** static const datetkn datetktbl[] = {
*** 157,162 ****
--- 158,164 ----
{"oct", MONTH, 10},
{"october", MONTH, 10},
{"on", IGNORE_DTF, 0}, /* "on" (throwaway) */
+ {PLUS, RESERV, DTK_PLUS}, /* "plus" an interval */
{"pm", AMPM, PM},
{"s", UNITS, DTK_SECOND}, /* "seconds" for ISO input */
{"sat", DOW, 6},
*************** ParseDateTime(const char *timestr, char
*** 784,790 ****
*/
int
DecodeDateTime(char **field, int *ftype, int nf,
! int *dtype, struct pg_tm * tm, fsec_t *fsec, int *tzp)
{
int fmask = 0,
tmask,
--- 786,793 ----
*/
int
DecodeDateTime(char **field, int *ftype, int nf,
! int *dtype, struct pg_tm * tm, fsec_t *fsec, int *tzp,
! Interval *offset)
{
int fmask = 0,
tmask,
*************** DecodeDateTime(char **field, int *ftype,
*** 799,804 ****
--- 802,809 ----
bool is2digits = FALSE;
bool bc = FALSE;
pg_tz *namedTz = NULL;
+ int ifield = 0; /* interval field index after "plus" or "minus"
+ * to add or subtract an interval */
/*
* We'll insist on at least all of the date fields, but initialize the
*************** DecodeDateTime(char **field, int *ftype,
*** 814,819 ****
--- 819,832 ----
if (tzp != NULL)
*tzp = 0;
+ /* Zero any interval passed in too */
+ if (offset != NULL)
+ {
+ offset->month = 0;
+ offset->day = 0;
+ offset->time = 0;
+ }
+
for (i = 0; i < nf; i++)
{
switch (ftype[i])
*************** DecodeDateTime(char **field, int *ftype,
*** 1235,1240 ****
--- 1248,1265 ----
*tzp = 0;
break;
+ case DTK_PLUS:
+ case DTK_MINUS:
+ /*
+ * All the remaining fields should form an
+ * interval to be added or subtracted from the
+ * datetime so far. We exit the main loop now
+ * and process this at the end.
+ */
+ ifield = i + 1;
+ i = nf;
+ break;
+
default:
*dtype = val;
}
*************** DecodeDateTime(char **field, int *ftype,
*** 1381,1386 ****
--- 1406,1421 ----
/* do additional checking for full date specs... */
if (*dtype == DTK_DATE)
{
+ /*
+ * If the datetime starts with a "plus" or a "minus" and followed by
+ * an interval, treat it as relative to now.
+ */
+ if (ifield == 1)
+ {
+ fmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
+ GetCurrentTimeUsec(tm, fsec, tzp);
+ }
+
if ((fmask & DTK_DATE_M) != DTK_DATE_M)
{
if ((fmask & DTK_TIME_M) == DTK_TIME_M)
*************** DecodeDateTime(char **field, int *ftype,
*** 1415,1420 ****
--- 1450,1506 ----
}
}
+ if (*dtype == DTK_DATE || *dtype == DTK_EPOCH)
+ {
+ /* Return any interval to be added or subtracted */
+ if (ifield > 0)
+ {
+ int idtype;
+ struct pg_tm tt,
+ *itm = &tt;
+ fsec_t ifsec;
+
+ dterr = DecodeInterval(field + ifield, ftype + ifield,
+ nf - ifield, INTERVAL_FULL_RANGE,
+ &idtype, itm, &ifsec);
+
+ /* if that thinks it's a bad format, try ISO8601 style */
+ if (dterr == DTERR_BAD_FORMAT)
+ {
+ /*
+ * join the interval fields back into a single string by
+ * replacing the '\0' delimiters with spaces. This works
+ * because ParseDateTime uses a single buffer to decode the
+ * fields.
+ */
+ for (i = ifield; i < nf - 1; i++)
+ field[i][strlen(field[i])] = ' ';
+
+ dterr = DecodeISO8601Interval(field[ifield], &idtype,
+ itm, &ifsec);
+ }
+ if (dterr)
+ return dterr;
+ if (idtype != DTK_DELTA)
+ return DTERR_BAD_FORMAT;
+
+ /* if "minus", negate the interval */
+ if (val == DTK_MINUS)
+ {
+ ifsec = -ifsec;
+ itm->tm_sec = -itm->tm_sec;
+ itm->tm_min = -itm->tm_min;
+ itm->tm_hour = -itm->tm_hour;
+ itm->tm_mday = -itm->tm_mday;
+ itm->tm_mon = -itm->tm_mon;
+ itm->tm_year = -itm->tm_year;
+ }
+
+ if (offset == NULL || tm2interval(itm, ifsec, offset) != 0)
+ return DTERR_BAD_FORMAT;
+ }
+ }
+
return 0;
}
diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c
new file mode 100644
index 6771e78..f027edd
*** a/src/backend/utils/adt/nabstime.c
--- b/src/backend/utils/adt/nabstime.c
*************** abstimein(PG_FUNCTION_ARGS)
*** 235,241 ****
dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
! dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
if (dterr != 0)
DateTimeParseError(dterr, str, "abstime");
--- 235,241 ----
dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
! dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz, NULL);
if (dterr != 0)
DateTimeParseError(dterr, str, "abstime");
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
new file mode 100644
index 45e7002..6bddfa5
*** a/src/backend/utils/adt/timestamp.c
--- b/src/backend/utils/adt/timestamp.c
*************** timestamp_in(PG_FUNCTION_ARGS)
*** 154,164 ****
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char workbuf[MAXDATELEN + MAXDATEFIELDS];
dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
! dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
if (dterr != 0)
DateTimeParseError(dterr, str, "timestamp");
--- 154,166 ----
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char workbuf[MAXDATELEN + MAXDATEFIELDS];
+ Interval interval,
+ *offset = &interval;
dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
! dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz, offset);
if (dterr != 0)
DateTimeParseError(dterr, str, "timestamp");
*************** timestamp_in(PG_FUNCTION_ARGS)
*** 169,178 ****
--- 171,190 ----
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range: \"%s\"", str)));
+ if (offset->month != 0 || offset->day != 0 || offset->time != 0)
+ result = DatumGetTimestamp(
+ DirectFunctionCall2(timestamp_pl_interval,
+ TimestampGetDatum(result),
+ PointerGetDatum(offset)));
break;
case DTK_EPOCH:
result = SetEpochTimestamp();
+ if (offset->month != 0 || offset->day != 0 || offset->time != 0)
+ result = DatumGetTimestamp(
+ DirectFunctionCall2(timestamp_pl_interval,
+ TimestampGetDatum(result),
+ PointerGetDatum(offset)));
break;
case DTK_LATE:
*************** timestamptz_in(PG_FUNCTION_ARGS)
*** 418,428 ****
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char workbuf[MAXDATELEN + MAXDATEFIELDS];
dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
! dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz);
if (dterr != 0)
DateTimeParseError(dterr, str, "timestamp with time zone");
--- 430,442 ----
char *field[MAXDATEFIELDS];
int ftype[MAXDATEFIELDS];
char workbuf[MAXDATELEN + MAXDATEFIELDS];
+ Interval interval,
+ *offset = &interval;
dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
field, ftype, MAXDATEFIELDS, &nf);
if (dterr == 0)
! dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz, offset);
if (dterr != 0)
DateTimeParseError(dterr, str, "timestamp with time zone");
*************** timestamptz_in(PG_FUNCTION_ARGS)
*** 433,442 ****
--- 447,466 ----
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range: \"%s\"", str)));
+ if (offset->month != 0 || offset->day != 0 || offset->time != 0)
+ result = DatumGetTimestampTz(
+ DirectFunctionCall2(timestamptz_pl_interval,
+ TimestampTzGetDatum(result),
+ PointerGetDatum(offset)));
break;
case DTK_EPOCH:
result = SetEpochTimestamp();
+ if (offset->month != 0 || offset->day != 0 || offset->time != 0)
+ result = DatumGetTimestampTz(
+ DirectFunctionCall2(timestamptz_pl_interval,
+ TimestampTzGetDatum(result),
+ PointerGetDatum(offset)));
break;
case DTK_LATE:
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
new file mode 100644
index 2880304..c19f872
*** a/src/include/utils/datetime.h
--- b/src/include/utils/datetime.h
*************** struct tzEntry;
*** 45,50 ****
--- 45,52 ----
#define TODAY "today"
#define TOMORROW "tomorrow"
#define YESTERDAY "yesterday"
+ #define PLUS "plus"
+ #define MINUS "minus"
#define ZULU "zulu"
#define DMICROSEC "usecond"
*************** struct tzEntry;
*** 178,183 ****
--- 180,188 ----
#define DTK_ISOYEAR 36
#define DTK_ISODOW 37
+ #define DTK_PLUS 38
+ #define DTK_MINUS 39
+
/*
* Bit mask definitions for time parsing.
*************** struct tzEntry;
*** 190,198 ****
#define DTK_DATE_M (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY))
#define DTK_TIME_M (DTK_M(HOUR) | DTK_M(MINUTE) | DTK_ALL_SECS_M)
! #define MAXDATELEN 63 /* maximum possible length of an input date
* string (not counting tr. null) */
! #define MAXDATEFIELDS 25 /* maximum possible number of fields in a date
* string */
#define TOKMAXLEN 10 /* only this many chars are stored in
* datetktbl */
--- 195,203 ----
#define DTK_DATE_M (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY))
#define DTK_TIME_M (DTK_M(HOUR) | DTK_M(MINUTE) | DTK_ALL_SECS_M)
! #define MAXDATELEN 512 /* maximum possible length of an input date
* string (not counting tr. null) */
! #define MAXDATEFIELDS 50 /* maximum possible number of fields in a date
* string */
#define TOKMAXLEN 10 /* only this many chars are stored in
* datetktbl */
*************** extern int date2j(int year, int month, i
*** 299,307 ****
extern int ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
char **field, int *ftype,
int maxfields, int *numfields);
! extern int DecodeDateTime(char **field, int *ftype,
! int nf, int *dtype,
! struct pg_tm * tm, fsec_t *fsec, int *tzp);
extern int DecodeTimeOnly(char **field, int *ftype,
int nf, int *dtype,
struct pg_tm * tm, fsec_t *fsec, int *tzp);
--- 304,312 ----
extern int ParseDateTime(const char *timestr, char *workbuf, size_t buflen,
char **field, int *ftype,
int maxfields, int *numfields);
! extern int DecodeDateTime(char **field, int *ftype, int nf,
! int *dtype, struct pg_tm * tm, fsec_t *fsec, int *tzp,
! Interval *offset);
extern int DecodeTimeOnly(char **field, int *ftype,
int nf, int *dtype,
struct pg_tm * tm, fsec_t *fsec, int *tzp);