--- /dev/null
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <ecpg_informix.h>
+#include <pgtypes_error.h>
+#include <pgtypes_date.h>
+
+/* we start with the numeric functions */
+int
+decadd(Numeric *arg1, Numeric *arg2, Numeric *sum)
+{
+ int i = PGTYPESnumeric_add(arg1, arg2, sum);
+
+ if (i == 0) /* No error */
+ return 0;
+ if (errno == PGTYPES_NUM_OVERFLOW)
+ return -1200;
+
+ return -1201;
+}
+
+int
+deccmp(Numeric *arg1, Numeric *arg2)
+{
+ int i = PGTYPESnumeric_cmp(arg1, arg2);
+
+ /* TODO: Need to return DECUNKNOWN instead of PGTYPES_NUM_BAD_NUMERIC */
+ return (i);
+}
+
+void
+deccopy(Numeric *src, Numeric *target)
+{
+ PGTYPESnumeric_copy(src, target);
+}
+
+static char *
+strndup(char *str, int len)
+{
+ int real_len = strlen(str);
+ int use_len = (real_len > len) ? len : real_len;
+
+ char *new = malloc(use_len + 1);
+
+ if (new)
+ {
+ memcpy(str, new, use_len);
+ new[use_len] = '\0';
+ }
+ else
+ errno = ENOMEM;
+
+ return new;
+}
+
+int
+deccvasc(char *cp, int len, Numeric *np)
+{
+ char *str = strndup(cp, len); /* Numeric_in always converts the complete string */
+ int ret = 0;
+
+ if (!str)
+ ret = -1201;
+ else
+ {
+ np = PGTYPESnumeric_aton(str, NULL);
+ if (!np)
+ {
+ switch (errno)
+ {
+ case PGTYPES_NUM_OVERFLOW: ret = -1200;
+ break;
+ case PGTYPES_NUM_BAD_NUMERIC: ret = -1213;
+ break;
+ default: ret = -1216;
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+int
+deccvdbl(double dbl, Numeric *np)
+{
+ return(PGTYPESnumeric_dton(dbl, np));
+}
+
+int
+deccvint(int in, Numeric *np)
+{
+ return(PGTYPESnumeric_iton(in, np));
+}
+
+int
+deccvlong(long lng, Numeric *np)
+{
+ return(PGTYPESnumeric_lton(lng, np));
+}
+
+int
+decdiv(Numeric *n1, Numeric *n2, Numeric *n3)
+{
+ int i = PGTYPESnumeric_div(n1, n2, n3), ret = 0;
+
+ if (i != 0)
+ switch (errno)
+ {
+ case PGTYPES_NUM_DIVIDE_ZERO: ret = -1202;
+ break;
+ case PGTYPES_NUM_OVERFLOW: ret = -1200;
+ break;
+ default: ret = -1201;
+ break;
+ }
+
+ return ret;
+}
+
+int
+decmul(Numeric *n1, Numeric *n2, Numeric *n3)
+{
+ int i = PGTYPESnumeric_mul(n1, n2, n3), ret = 0;
+
+ if (i != 0)
+ switch (errno)
+ {
+ case PGTYPES_NUM_OVERFLOW: ret = -1200;
+ break;
+ default: ret = -1201;
+ break;
+ }
+
+ return ret;
+}
+
+int
+decsub(Numeric *n1, Numeric *n2, Numeric *n3)
+{
+ int i = PGTYPESnumeric_sub(n1, n2, n3), ret = 0;
+
+ if (i != 0)
+ switch (errno)
+ {
+ case PGTYPES_NUM_OVERFLOW: ret = -1200;
+ break;
+ default: ret = -1201;
+ break;
+ }
+
+ return ret;
+}
+
+int
+dectoasc(Numeric *np, char *cp, int len, int right)
+{
+ char *str;
+
+ if (right >= 0)
+ str = PGTYPESnumeric_ntoa(np, right);
+ else
+ str = PGTYPESnumeric_ntoa(np, 0);
+
+ if (!str)
+ return -1;
+
+ /* TODO: have to take care of len here and create exponatial notion if necessary */
+ strncpy(cp, str, len);
+ free (str);
+
+ return 0;
+}
+
+int
+dectodbl(Numeric *np, double *dblp)
+{
+ return(PGTYPESnumeric_ntod(np, dblp));
+}
+
+int
+dectoint(Numeric *np, int *ip)
+{
+ int ret = PGTYPESnumeric_ntoi(np, ip);
+
+ if (ret == PGTYPES_NUM_OVERFLOW)
+ ret = -1200;
+
+ return ret;
+}
+
+int
+dectolong(Numeric *np, long *lngp)
+{
+ int ret = PGTYPESnumeric_ntol(np, lngp);
+
+ if (ret == PGTYPES_NUM_OVERFLOW)
+ ret = -1200;
+
+ return ret;
+}
+
+/* Now the date functions */
+int
+rdatestr (Date d, char *str)
+{
+ char *tmp = PGTYPESdate_dtoa(d);
+
+ if (!tmp)
+ return -1210;
+
+ /* move to user allocated buffer */
+ strcpy(tmp, str);
+ free(str);
+
+ return 0;
+}
+
+void
+rtoday (Date *d)
+{
+ PGTYPESdate_today(d);
+ return;
+}
+
+int
+rjulmdy (Date d, short mdy[3])
+{
+ PGTYPESdate_julmdy(d, (int *)mdy);
+ return 0;
+}
+
+int
+rdefmtdate (Date *d, char *fmt, char *str)
+{
+ /* TODO: take care of DBCENTURY environment variable */
+ /* PGSQL functions allow all centuries */
+
+ if (PGTYPESdate_defmtdate(d, fmt, str) == 0)
+ return 0;
+
+ switch (errno)
+ {
+ case PGTYPES_DATE_ERR_ENOSHORTDATE: return -1209;
+ case PGTYPES_DATE_ERR_EARGS:
+ case PGTYPES_DATE_ERR_ENOTDMY: return -1212;
+ case PGTYPES_DATE_BAD_DAY: return -1204;
+ case PGTYPES_DATE_BAD_MONTH: return -1205;
+ default: return -1206;
+ }
+}
+
+int
+rfmtdate (Date d, char *fmt, char *str)
+{
+ if (PGTYPESdate_fmtdate(d, fmt, str) == 0)
+ return 0;
+
+ if (errno == ENOMEM)
+ return -1211;
+
+ return -1210;
+}
+
+int
+rmdyjul (short mdy[3], Date *d)
+{
+ PGTYPESdate_mdyjul((int *)mdy, d);
+ return 0;
+}
+
+/* And the datetime stuff */
+
+void
+dtcurrent (Timestamp *ts)
+{
+ return;
+}
+
+int
+dtcvasc (char *str, Timestamp *ts)
+{
+ return 0;
+}
+
+int
+dtsub (Timestamp *ts1, Timestamp *ts2, Interval *iv)
+{
+ return 0;
+}
+
+int
+dttoasc (Timestamp *ts, char *output)
+{
+ return 0;
+}
+
+int
+dttofmtasc (Timestamp *ts, char *output, int str_len, char *fmtstr)
+{
+ return 0;
+}
+
+int
+intoasc(Interval *i, char *str)
+{
+ return 0;
+}
+
+/* And finally some misc functions */
+int
+rstrdate (char *str, Date *d)
+{
+ return 0;
+}
+
+int
+rfmtlong(long lvalue, char *format, char *outbuf)
+{
+ return 0;
+}
+
+int
+rgetmsg(int msgnum, char *s, int maxsize)
+{
+ return 0;
+}
+
+int
+risnull(int vtype, char *pcvar)
+{
+ return 0;
+}
+
+int
+rsetnull(int vtype, char *pcvar)
+{
+ return 0;
+}
+
+int
+rtypalign(int offset, int type)
+{
+ return 0;
+}
+
+int
+rtypmsize(int type, int len)
+{
+ return 0;
+}
+
+void
+rupshift(char *s)
+{
+ return;
+}
+
+
+
--- /dev/null
+#include <math.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+#include <float.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __FAST_MATH__
+#error -ffast-math is known to break this code
+#endif
+
+#include "dt.h"
+#include "extern.h"
+#include "pgtypes_error.h"
+#include "pgtypes_interval.h"
+#include "datetime.h"
+
+/* TrimTrailingZeros()
+ * ... resulting from printing numbers with full precision.
+ */
+static void
+TrimTrailingZeros(char *str)
+{
+ int len = strlen(str);
+
+ /* chop off trailing zeros... but leave at least 2 fractional digits */
+ while ((*(str + len - 1) == '0')
+ && (*(str + len - 3) != '.'))
+ {
+ len--;
+ *(str + len) = '\0';
+ }
+}
+
+/* DecodeTime()
+ * Decode time string which includes delimiters.
+ * Only check the lower limit on hours, since this same code
+ * can be used to represent time spans.
+ */
+static int
+DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, fsec_t *fsec)
+{
+ char *cp;
+
+ *tmask = DTK_TIME_M;
+
+ tm->tm_hour = strtol(str, &cp, 10);
+ if (*cp != ':')
+ return -1;
+ str = cp + 1;
+ tm->tm_min = strtol(str, &cp, 10);
+ if (*cp == '\0')
+ {
+ tm->tm_sec = 0;
+ *fsec = 0;
+ }
+ else if (*cp != ':')
+ return -1;
+ else
+ {
+ str = cp + 1;
+ tm->tm_sec = strtol(str, &cp, 10);
+ if (*cp == '\0')
+ *fsec = 0;
+ else if (*cp == '.')
+ {
+#ifdef HAVE_INT64_TIMESTAMP
+ char fstr[MAXDATELEN + 1];
+
+ /*
+ * OK, we have at most six digits to work with. Let's
+ * construct a string and then do the conversion to an
+ * integer.
+ */
+ strncpy(fstr, (cp + 1), 7);
+ strcpy((fstr + strlen(fstr)), "000000");
+ *(fstr + 6) = '\0';
+ *fsec = strtol(fstr, &cp, 10);
+#else
+ str = cp;
+ *fsec = strtod(str, &cp);
+#endif
+ if (*cp != '\0')
+ return -1;
+ }
+ else
+ return -1;
+ }
+
+ /* 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 > 59)
+ || (*fsec >= INT64CONST(1000000)))
+ return -1;
+#else
+ if ((tm->tm_hour < 0)
+ || (tm->tm_min < 0) || (tm->tm_min > 59)
+ || (tm->tm_sec < 0) || (tm->tm_sec > 59)
+ || (*fsec >= 1))
+ return -1;
+#endif
+
+ return 0;
+} /* DecodeTime() */
+
+/* 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 *= 60;
+ 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 *= 3600;
+ 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 *= 86400;
+ 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 sec;
+
+ fval *= (7 * 86400);
+ 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 sec;
+
+ fval *= (30 * 86400);
+ 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 * 12);
+ 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 * 120);
+ 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 * 1200);
+ 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 * 12000);
+ 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 / INT64CONST(1000000));
+ *fsec -= (sec * INT64CONST(1000000));
+#else
+ TMODULO(*fsec, sec, 1e0);
+#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() */
+
+/* EncodeInterval()
+ * Interpret time structure as a delta time and convert to string.
+ *
+ * Support "traditional Postgres" and ISO-8601 styles.
+ * Actually, afaik ISO does not address time interval formatting,
+ * but this looks similar to the spec for absolute date/time.
+ * - thomas 1998-04-30
+ */
+int
+EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str)
+{
+ int is_before = FALSE;
+ int is_nonzero = FALSE;
+ char *cp = str;
+
+ /*
+ * The sign of year and month are guaranteed to match, since they are
+ * stored internally as "month". But we'll need to check for is_before
+ * and is_nonzero when determining the signs of hour/minute/seconds
+ * fields.
+ */
+ switch (style)
+ {
+ /* compatible with ISO date formats */
+ case USE_ISO_DATES:
+ if (tm->tm_year != 0)
+ {
+ sprintf(cp, "%d year%s",
+ tm->tm_year, ((tm->tm_year != 1) ? "s" : ""));
+ cp += strlen(cp);
+ is_before = (tm->tm_year < 0);
+ is_nonzero = TRUE;
+ }
+
+ if (tm->tm_mon != 0)
+ {
+ sprintf(cp, "%s%s%d mon%s", (is_nonzero ? " " : ""),
+ ((is_before && (tm->tm_mon > 0)) ? "+" : ""),
+ tm->tm_mon, ((tm->tm_mon != 1) ? "s" : ""));
+ cp += strlen(cp);
+ is_before = (tm->tm_mon < 0);
+ is_nonzero = TRUE;
+ }
+
+ if (tm->tm_mday != 0)
+ {
+ sprintf(cp, "%s%s%d day%s", (is_nonzero ? " " : ""),
+ ((is_before && (tm->tm_mday > 0)) ? "+" : ""),
+ tm->tm_mday, ((tm->tm_mday != 1) ? "s" : ""));
+ cp += strlen(cp);
+ is_before = (tm->tm_mday < 0);
+ is_nonzero = TRUE;
+ }
+ if ((!is_nonzero) || (tm->tm_hour != 0) || (tm->tm_min != 0)
+ || (tm->tm_sec != 0) || (fsec != 0))
+ {
+ int minus = ((tm->tm_hour < 0) || (tm->tm_min < 0)
+ || (tm->tm_sec < 0) || (fsec < 0));
+
+ sprintf(cp, "%s%s%02d:%02d", (is_nonzero ? " " : ""),
+ (minus ? "-" : (is_before ? "+" : "")),
+ abs(tm->tm_hour), abs(tm->tm_min));
+ cp += strlen(cp);
+ /* Mark as "non-zero" since the fields are now filled in */
+ is_nonzero = TRUE;
+
+ /* fractional seconds? */
+ if (fsec != 0)
+ {
+#ifdef HAVE_INT64_TIMESTAMP
+ sprintf(cp, ":%02d", abs(tm->tm_sec));
+ cp += strlen(cp);
+ sprintf(cp, ".%06d", ((fsec >= 0) ? fsec : -(fsec)));
+#else
+ fsec += tm->tm_sec;
+ sprintf(cp, ":%013.10f", fabs(fsec));
+#endif
+ TrimTrailingZeros(cp);
+ cp += strlen(cp);
+ is_nonzero = TRUE;
+ }
+ /* otherwise, integer seconds only? */
+ else if (tm->tm_sec != 0)
+ {
+ sprintf(cp, ":%02d", abs(tm->tm_sec));
+ cp += strlen(cp);
+ is_nonzero = TRUE;
+ }
+ }
+ break;
+
+ case USE_POSTGRES_DATES:
+ default:
+ strcpy(cp, "@ ");
+ cp += strlen(cp);
+
+ if (tm->tm_year != 0)
+ {
+ int year = tm->tm_year;
+
+ if (tm->tm_year < 0)
+ year = -year;
+
+ sprintf(cp, "%d year%s", year,
+ ((year != 1) ? "s" : ""));
+ cp += strlen(cp);
+ is_before = (tm->tm_year < 0);
+ is_nonzero = TRUE;
+ }
+
+ if (tm->tm_mon != 0)
+ {
+ int mon = tm->tm_mon;
+
+ if (is_before || ((!is_nonzero) && (tm->tm_mon < 0)))
+ mon = -mon;
+
+ sprintf(cp, "%s%d mon%s", (is_nonzero ? " " : ""), mon,
+ ((mon != 1) ? "s" : ""));
+ cp += strlen(cp);
+ if (!is_nonzero)
+ is_before = (tm->tm_mon < 0);
+ is_nonzero = TRUE;
+ }
+
+ if (tm->tm_mday != 0)
+ {
+ int day = tm->tm_mday;
+
+ if (is_before || ((!is_nonzero) && (tm->tm_mday < 0)))
+ day = -day;
+
+ sprintf(cp, "%s%d day%s", (is_nonzero ? " " : ""), day,
+ ((day != 1) ? "s" : ""));
+ cp += strlen(cp);
+ if (!is_nonzero)
+ is_before = (tm->tm_mday < 0);
+ is_nonzero = TRUE;
+ }
+ if (tm->tm_hour != 0)
+ {
+ int hour = tm->tm_hour;
+
+ if (is_before || ((!is_nonzero) && (tm->tm_hour < 0)))
+ hour = -hour;
+
+ sprintf(cp, "%s%d hour%s", (is_nonzero ? " " : ""), hour,
+ ((hour != 1) ? "s" : ""));
+ cp += strlen(cp);
+ if (!is_nonzero)
+ is_before = (tm->tm_hour < 0);
+ is_nonzero = TRUE;
+ }
+
+ if (tm->tm_min != 0)
+ {
+ int min = tm->tm_min;
+
+ if (is_before || ((!is_nonzero) && (tm->tm_min < 0)))
+ min = -min;
+
+ sprintf(cp, "%s%d min%s", (is_nonzero ? " " : ""), min,
+ ((min != 1) ? "s" : ""));
+ cp += strlen(cp);
+ if (!is_nonzero)
+ is_before = (tm->tm_min < 0);
+ is_nonzero = TRUE;
+ }
+
+ /* fractional seconds? */
+ if (fsec != 0)
+ {
+#ifdef HAVE_INT64_TIMESTAMP
+ if (is_before || ((!is_nonzero) && (tm->tm_sec < 0)))
+ tm->tm_sec = -tm->tm_sec;
+ sprintf(cp, "%s%d.%02d secs", (is_nonzero ? " " : ""),
+ tm->tm_sec, (((int) fsec) / 10000));
+ cp += strlen(cp);
+ if (!is_nonzero)
+ is_before = (fsec < 0);
+#else
+ fsec_t sec;
+
+ fsec += tm->tm_sec;
+ sec = fsec;
+ if (is_before || ((!is_nonzero) && (fsec < 0)))
+ sec = -sec;
+
+ sprintf(cp, "%s%.2f secs", (is_nonzero ? " " : ""), sec);
+ cp += strlen(cp);
+ if (!is_nonzero)
+ is_before = (fsec < 0);
+#endif
+ is_nonzero = TRUE;
+
+ /* otherwise, integer seconds only? */
+ }
+ else if (tm->tm_sec != 0)
+ {
+ int sec = tm->tm_sec;
+
+ if (is_before || ((!is_nonzero) && (tm->tm_sec < 0)))
+ sec = -sec;
+
+ sprintf(cp, "%s%d sec%s", (is_nonzero ? " " : ""), sec,
+ ((sec != 1) ? "s" : ""));
+ cp += strlen(cp);
+ if (!is_nonzero)
+ is_before = (tm->tm_sec < 0);
+ is_nonzero = TRUE;
+ }
+ break;
+ }
+
+ /* identically zero? then put in a unitless zero... */
+ if (!is_nonzero)
+ {
+ strcat(cp, "0");
+ cp += strlen(cp);
+ }
+
+ if (is_before && (style == USE_POSTGRES_DATES))
+ {
+ strcat(cp, " ago");
+ cp += strlen(cp);
+ }
+
+ return 0;
+} /* EncodeInterval() */
+
+/* interval2tm()
+ * Convert a interval data type to a tm structure.
+ */
+static int
+interval2tm(Interval span, struct tm * tm, fsec_t *fsec)
+{
+#ifdef HAVE_INT64_TIMESTAMP
+ int64 time;
+
+#else
+ double time;
+#endif
+
+ if (span.month != 0)
+ {
+ tm->tm_year = span.month / 12;
+ tm->tm_mon = span.month % 12;
+
+ }
+ else
+ {
+ tm->tm_year = 0;
+ tm->tm_mon = 0;
+ }
+
+ time = span.time;
+
+#ifdef HAVE_INT64_TIMESTAMP
+ tm->tm_mday = (time / INT64CONST(86400000000));
+ time -= (tm->tm_mday * INT64CONST(86400000000));
+ tm->tm_hour = (time / INT64CONST(3600000000));
+ time -= (tm->tm_hour * INT64CONST(3600000000));
+ tm->tm_min = (time / INT64CONST(60000000));
+ time -= (tm->tm_min * INT64CONST(60000000));
+ tm->tm_sec = (time / INT64CONST(1000000));
+ *fsec = (time - (tm->tm_sec * INT64CONST(1000000)));
+#else
+ TMODULO(time, tm->tm_mday, 86400e0);
+ TMODULO(time, tm->tm_hour, 3600e0);
+ TMODULO(time, tm->tm_min, 60e0);
+ TMODULO(time, tm->tm_sec, 1e0);
+ *fsec = time;
+#endif
+
+ return 0;
+} /* interval2tm() */
+
+static int
+tm2interval(struct tm * tm, fsec_t fsec, Interval *span)
+{
+ span->month = ((tm->tm_year * 12) + tm->tm_mon);
+#ifdef HAVE_INT64_TIMESTAMP
+ span->time = ((((((((tm->tm_mday * INT64CONST(24))
+ + tm->tm_hour) * INT64CONST(60))
+ + tm->tm_min) * INT64CONST(60))
+ + tm->tm_sec) * INT64CONST(1000000)) + fsec);
+#else
+ span->time = ((((((tm->tm_mday * 24.0)
+ + tm->tm_hour) * 60.0)
+ + tm->tm_min) * 60.0)
+ + tm->tm_sec);
+ span->time = JROUND(span->time + fsec);
+#endif
+
+ return 0;
+} /* tm2interval() */
+
+Interval *
+PGTYPESinterval_atoi(char *str, char **endptr)
+{
+ Interval *result = NULL;
+ fsec_t fsec;
+ struct tm tt,
+ *tm = &tt;
+ int dtype;
+ int nf;
+ char *field[MAXDATEFIELDS];
+ int ftype[MAXDATEFIELDS];
+ char lowstr[MAXDATELEN + MAXDATEFIELDS];
+ char *realptr;
+ char **ptr = (endptr != NULL) ? endptr : &realptr;
+
+ 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;
+
+ if (strlen(str) >= sizeof(lowstr))
+ {
+ errno = PGTYPES_INTVL_BAD_INTERVAL;
+ return NULL;
+ }
+
+ if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf, ptr) != 0)
+ || (DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0))
+ {
+ errno = PGTYPES_INTVL_BAD_INTERVAL;
+ return NULL;
+ }
+
+ result = (Interval *) pgtypes_alloc(sizeof(Interval));
+ if (!result)
+ return NULL;
+
+ if (dtype != DTK_DELTA)
+ {
+ errno = PGTYPES_INTVL_BAD_INTERVAL;
+ return NULL;
+ }
+
+ if (tm2interval(tm, fsec, result) != 0)
+ {
+ errno = PGTYPES_INTVL_BAD_INTERVAL;
+ return NULL;
+ }
+
+ return result;
+}
+
+char *
+PGTYPESinterval_itoa(Interval *span)
+{
+ struct tm tt,
+ *tm = &tt;
+ fsec_t fsec;
+ char buf[MAXDATELEN + 1];
+ int DateStyle=0;
+
+ if (interval2tm(*span, tm, &fsec) != 0)
+ {
+ errno = PGTYPES_INTVL_BAD_INTERVAL;
+ return NULL;
+ }
+
+ if (EncodeInterval(tm, fsec, DateStyle, buf) != 0)
+ {
+ errno = PGTYPES_INTVL_BAD_INTERVAL;
+ return NULL;
+ }
+
+ return pgtypes_strdup(buf);
+}
+
+int
+PGTYPESinterval_copy(Interval *intvlsrc, Interval *intrcldest)
+{
+ intrcldest->time = intvlsrc->time;
+ intrcldest->month = intvlsrc->month;
+
+ return 0;
+}
+