*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.54 2000/10/29 13:17:33 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/datetime.c,v 1.55 2000/11/06 15:57:00 thomas Exp $
*
*-------------------------------------------------------------------------
*/
{"pst", TZ, NEG(48)}, /* Pacific Standard Time */
{"sadt", DTZ, 63}, /* S. Australian Dayl. Time */
{"sast", TZ, 57}, /* South Australian Std Time */
+#if USE_AUSTRALIAN_RULES
+ {"sat", TZ, 57},
+#else
{"sat", DOW, 6},
+#endif
{"saturday", DOW, 6},
{"sep", MONTH, 9},
{"sept", MONTH, 9},
{"tue", DOW, 2},
{"tues", DOW, 2},
{"tuesday", DOW, 2},
- {"undefined", RESERV, DTK_INVALID}, /* "undefined" pre-v6.1 invalid
- * time */
+ {"undefined", RESERV, DTK_INVALID}, /* pre-v6.1 invalid time */
{"ut", TZ, 0},
{"utc", TZ, 0},
{"wadt", DTZ, 48}, /* West Australian DST */
{"ydt", DTZ, NEG(48)}, /* Yukon Daylight Time */
{YESTERDAY, RESERV, DTK_YESTERDAY}, /* yesterday midnight */
{"yst", TZ, NEG(54)}, /* Yukon Standard Time */
+ {"z", RESERV, DTK_ZULU}, /* 00:00:00 */
{"zp4", TZ, NEG(24)}, /* GMT +4 hours. */
{"zp5", TZ, NEG(30)}, /* GMT +5 hours. */
{"zp6", TZ, NEG(36)}, /* GMT +6 hours. */
- {"z", RESERV, DTK_ZULU}, /* 00:00:00 */
{ZULU, RESERV, DTK_ZULU}, /* 00:00:00 */
};
*/
if ((*cp == '-') || (*cp == '/') || (*cp == '.'))
{
-#if 0
-
- /*
- * special case of Posix timezone "GMT-0800" Note that
- * other sign (e.g. "GMT+0800" is recognized as two
- * separate fields and handled later. XXX There is no room
- * for a delimiter between the "GMT" and the "-0800", so
- * we are going to just swallow the "GMT". But this leads
- * to other troubles with the definition of signs, so we
- * have to flip - thomas 2000-02-06
- */
- if ((*cp == '-') && isdigit(*(cp + 1))
- && (strncmp(field[nf], "gmt", 3) == 0))
- {
- *cp = '+';
- continue;
- }
-#endif
-
ftype[nf] = DTK_DATE;
while (isdigit((int) *cp) || (*cp == '-') || (*cp == '/') || (*cp == '.'))
*lp++ = tolower(*cp++);
tmask,
type;
int i;
- int flen,
- val;
+ int val;
double fval;
double sec;
break;
case DTK_TZ:
-
/*
* Timezone is a token with a leading sign character and
- * otherwise the same as a non-signed numeric field
+ * otherwise the same as a non-signed time field
*/
+ Assert((*field[i] == '-') || (*field[i] == '+'));
+ /* 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++;
+ 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;
+ }
+ /* DROP THROUGH */
+
case DTK_DATE:
case DTK_NUMBER:
val = strtol(field[i], &cp, 10);
+
if (*cp == '.')
{
fval = strtod(cp, &cp);
else
return -1;
- flen = strlen(field[i]);
tmask = 0; /* DTK_M(type); */
switch (type)
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:
- break;
-
- default:
- strcpy(cp, "@ ");
- cp += strlen(cp);
- break;
- }
-
- if (tm->tm_year != 0)
- {
- is_before |= (tm->tm_year < 0);
- sprintf(cp, "%d year%s",
- abs(tm->tm_year), ((abs(tm->tm_year) != 1) ? "s" : ""));
- cp += strlen(cp);
- is_nonzero = TRUE;
- }
-
- if (tm->tm_mon != 0)
- {
- is_before |= (tm->tm_mon < 0);
- sprintf(cp, "%s%d mon%s", (is_nonzero ? " " : ""),
- abs(tm->tm_mon), ((abs(tm->tm_mon) != 1) ? "s" : ""));
- cp += strlen(cp);
- is_nonzero = TRUE;
- }
-
- switch (style)
- {
- /* compatible with ISO date formats */
- case USE_ISO_DATES:
- if (tm->tm_mday != 0)
+ if (tm->tm_year != 0)
{
- is_before |= (tm->tm_mday < 0);
- sprintf(cp, "%s%d", (is_nonzero ? " " : ""), abs(tm->tm_mday));
+ sprintf(cp, "%d year%s",
+ tm->tm_year, ((tm->tm_year != 1) ? "s" : ""));
cp += strlen(cp);
is_nonzero = TRUE;
}
- is_before |= ((tm->tm_hour < 0) || (tm->tm_min < 0));
- sprintf(cp, "%s%02d:%02d", (is_nonzero ? " " : ""),
- 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)
+ if (tm->tm_mon != 0)
{
- fsec += tm->tm_sec;
- is_before |= (fsec < 0);
- sprintf(cp, ":%05.2f", fabs(fsec));
+ sprintf(cp, "%s%d mon%s", (is_nonzero ? " " : ""),
+ tm->tm_mon, ((tm->tm_mon != 1) ? "s" : ""));
cp += strlen(cp);
is_nonzero = TRUE;
+ }
- /* otherwise, integer seconds only? */
+ if (tm->tm_mday != 0)
+ {
+ sprintf(cp, "%s%d", (is_nonzero ? " " : ""), tm->tm_mday);
+ cp += strlen(cp);
+ is_nonzero = TRUE;
}
- else if (tm->tm_sec != 0)
{
- is_before |= (tm->tm_sec < 0);
- sprintf(cp, ":%02d", abs(tm->tm_sec));
+ 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 ? "-" : "+"),
+ 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)
+ {
+ fsec += tm->tm_sec;
+ sprintf(cp, ":%05.2f", fabs(fsec));
+ 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)
+ {
+ is_before = (tm->tm_year < 0);
+ if (is_before)
+ tm->tm_year = -tm->tm_year;
+ sprintf(cp, "%d year%s",
+ tm->tm_year, ((tm->tm_year != 1) ? "s" : ""));
+ cp += strlen(cp);
+ is_nonzero = TRUE;
+ }
+
+ if (tm->tm_mon != 0)
+ {
+ if (! is_nonzero)
+ is_before = (tm->tm_mon < 0);
+ if (is_before)
+ tm->tm_mon = -tm->tm_mon;
+ sprintf(cp, "%s%d mon%s", (is_nonzero ? " " : ""),
+ tm->tm_mon, ((tm->tm_mon != 1) ? "s" : ""));
+ cp += strlen(cp);
+ is_nonzero = TRUE;
+ }
+
if (tm->tm_mday != 0)
{
- is_before |= (tm->tm_mday < 0);
+ if (! is_nonzero)
+ is_before = (tm->tm_mday < 0);
+ if (is_before)
+ tm->tm_mday = -tm->tm_mday;
sprintf(cp, "%s%d day%s", (is_nonzero ? " " : ""),
- abs(tm->tm_mday), ((abs(tm->tm_mday) != 1) ? "s" : ""));
+ tm->tm_mday, ((tm->tm_mday != 1) ? "s" : ""));
cp += strlen(cp);
is_nonzero = TRUE;
}
if (tm->tm_hour != 0)
{
- is_before |= (tm->tm_hour < 0);
+ if (! is_nonzero)
+ is_before = (tm->tm_hour < 0);
+ if (is_before)
+ tm->tm_hour = -tm->tm_hour;
sprintf(cp, "%s%d hour%s", (is_nonzero ? " " : ""),
- abs(tm->tm_hour), ((abs(tm->tm_hour) != 1) ? "s" : ""));
+ tm->tm_hour, ((tm->tm_hour != 1) ? "s" : ""));
cp += strlen(cp);
is_nonzero = TRUE;
}
if (tm->tm_min != 0)
{
- is_before |= (tm->tm_min < 0);
+ if (! is_nonzero)
+ is_before = (tm->tm_min < 0);
+ if (is_before)
+ tm->tm_min = -tm->tm_min;
sprintf(cp, "%s%d min%s", (is_nonzero ? " " : ""),
- abs(tm->tm_min), ((abs(tm->tm_min) != 1) ? "s" : ""));
+ tm->tm_min, ((tm->tm_min != 1) ? "s" : ""));
cp += strlen(cp);
is_nonzero = TRUE;
}
if (fsec != 0)
{
fsec += tm->tm_sec;
- is_before |= (fsec < 0);
- sprintf(cp, "%s%.2f secs", (is_nonzero ? " " : ""), fabs(fsec));
+ if (! is_nonzero)
+ is_before = (fsec < 0);
+ if (is_before)
+ fsec = -fsec;
+ sprintf(cp, "%s%.2f secs", (is_nonzero ? " " : ""), fsec);
cp += strlen(cp);
is_nonzero = TRUE;
}
else if (tm->tm_sec != 0)
{
- is_before |= (tm->tm_sec < 0);
+ if (! is_nonzero)
+ is_before = (tm->tm_sec < 0);
+ if (is_before)
+ tm->tm_sec = -tm->tm_sec;
sprintf(cp, "%s%d sec%s", (is_nonzero ? " " : ""),
- abs(tm->tm_sec), ((abs(tm->tm_sec) != 1) ? "s" : ""));
+ tm->tm_sec, ((tm->tm_sec != 1) ? "s" : ""));
cp += strlen(cp);
is_nonzero = TRUE;
}
}
/* identically zero? then put in a unitless zero... */
- if (!is_nonzero)
+ if (! is_nonzero)
{
strcat(cp, "0");
cp += strlen(cp);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.36 2000/10/29 13:17:34 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.37 2000/11/06 15:57:00 thomas Exp $
*
*-------------------------------------------------------------------------
*/
if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);
+ if (IS_VALID_UTIME(tm->tm_year, tm->tm_mon, tm->tm_mday))
+ {
+#if defined(HAVE_TM_ZONE) || defined(HAVE_INT_TIMEZONE)
+ tm->tm_isdst = -1;
+ tm->tm_year -= 1900;
+ tm->tm_mon -= 1;
+ tm->tm_isdst = -1;
+ mktime(tm);
+ tm->tm_year += 1900;
+ tm->tm_mon += 1;
+
+# if defined(HAVE_TM_ZONE)
+ tz = -(tm->tm_gmtoff); /* tm_gmtoff is Sun/DEC-ism */
+# elif defined(HAVE_INT_TIMEZONE)
+
+# ifdef __CYGWIN__
+ tz = (tm->tm_isdst ? (_timezone - 3600) : _timezone);
+# else
+ tz = (tm->tm_isdst ? (timezone - 3600) : timezone);
+# endif
+
+# endif
+
+#else /* not (HAVE_TM_ZONE || HAVE_INT_TIMEZONE) */
+ tz = CTimeZone;
+#endif
+ }
+ else
+ {
+ tm->tm_isdst = 0;
+ tz = 0;
+ }
+
if (tm2timestamp(tm, fsec, &tz, &dt) != 0)
elog(ERROR, "Unable to add timestamp and interval");
if (TIMESTAMP_NOT_FINITE(timestamp))
{
-#if NOT_USED
-/* should return null but Postgres doesn't like that currently. - tgl 97/06/12 */
- elog(ERROR, "Timestamp is not finite");
-#endif
- result = 0;
-
+ PG_RETURN_NULL();
}
else
{
if (TIMESTAMP_NOT_FINITE(timestamp))
{
-#if NOT_USED
-/* should return null but Postgres doesn't like that currently. - tgl 97/06/12 */
- elog(ERROR, "Timestamp is not finite", NULL);
-#endif
PG_RETURN_NULL();
}
else
if (TIMESTAMP_NOT_FINITE(timestamp))
{
-
- /*
- * could return null but Postgres doesn't like that currently. -
- * tgl 97/06/12
- *
- * Could do it now if you wanted ... the other tgl 2000/06/08
- */
- elog(ERROR, "Timestamp is not finite");
- result = NULL;
+ PG_RETURN_NULL();
}
else if ((type == TZ) || (type == DTZ))
{
}
PG_RETURN_TEXT_P(result);
-}
+} /* timestamp_zone() */
+
+/* timestamp_izone()
+ * Encode timestamp type with specified time interval as time zone.
+ * Require ISO-formatted result, since character-string time zone is not available.
+ */
+Datum
+timestamp_izone(PG_FUNCTION_ARGS)
+{
+ Interval *zone = PG_GETARG_INTERVAL_P(0);
+ Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
+ text *result;
+ Timestamp dt;
+ int tz;
+ char *tzn = "";
+ double fsec;
+ struct tm tt,
+ *tm = &tt;
+ char buf[MAXDATELEN + 1];
+ int len;
+
+ if (TIMESTAMP_NOT_FINITE(timestamp))
+ PG_RETURN_NULL();
+
+ if (zone->month != 0)
+ elog(ERROR, "INTERVAL time zone not legal (month specified)");
+
+ tm->tm_isdst = -1;
+ tz = -(zone->time);
+
+ dt = (TIMESTAMP_IS_RELATIVE(timestamp) ? SetTimestamp(timestamp) : timestamp);
+ dt = dt2local(dt, tz);
+
+ if (timestamp2tm(dt, NULL, tm, &fsec, NULL) != 0)
+ elog(ERROR, "Timestamp not legal");
+
+ EncodeDateTime(tm, fsec, &tz, &tzn, USE_ISO_DATES, buf);
+ len = (strlen(buf) + VARHDRSZ);
+
+ result = palloc(len);
+ VARATT_SIZEP(result) = len;
+ memmove(VARDATA(result), buf, (len - VARHDRSZ));
+
+ PG_RETURN_TEXT_P(result);
+} /* timestamp_izone() */