* date.c
* implements DATE and TIME data types specified in SQL-92 standard
*
- * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994-5, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.115 2005/07/21 04:41:43 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.137 2007/11/15 21:14:38 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <ctype.h>
#include <limits.h>
#include <float.h>
-#include <time.h>
+#include <time.h>
#include "access/hash.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "parser/scansup.h"
+#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/nabstime.h"
-#include "utils/timestamp.h"
/*
* gcc's -ffast-math switch breaks routines that expect exact results from
static int tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result);
static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
+
+/* common code for timetypmodin and timetztypmodin */
+static int32
+anytime_typmodin(bool istz, ArrayType *ta)
+{
+ int32 typmod;
+ int32 *tl;
+ int n;
+
+ tl = ArrayGetIntegerTypmods(ta, &n);
+
+ /*
+ * we're not too tense about good error message here because grammar
+ * shouldn't allow wrong number of modifiers for TIME
+ */
+ if (n != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid type modifier")));
+
+ if (*tl < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("TIME(%d)%s precision must not be negative",
+ *tl, (istz ? " WITH TIME ZONE" : ""))));
+ if (*tl > MAX_TIME_PRECISION)
+ {
+ ereport(WARNING,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
+ *tl, (istz ? " WITH TIME ZONE" : ""),
+ MAX_TIME_PRECISION)));
+ typmod = MAX_TIME_PRECISION;
+ }
+ else
+ typmod = *tl;
+
+ return typmod;
+}
+
+/* common code for timetypmodout and timetztypmodout */
+static char *
+anytime_typmodout(bool istz, int32 typmod)
+{
+ char *res = (char *) palloc(64);
+ const char *tz = istz ? " with time zone" : " without time zone";
+
+ if (typmod >= 0)
+ snprintf(res, 64, "(%d)%s", (int) typmod, tz);
+ else
+ snprintf(res, 64, "%s", tz);
+ return res;
+}
+
+
/*****************************************************************************
* Date ADT
*****************************************************************************/
date_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
- DateADT date;
+ DateADT date;
fsec_t fsec;
struct pg_tm tt,
*tm = &tt;
case DTK_CURRENT:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("date/time value \"current\" is no longer supported")));
+ errmsg("date/time value \"current\" is no longer supported")));
GetCurrentDateTime(tm);
break;
break;
}
+ if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("date out of range: \"%s\"", str)));
+
date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
PG_RETURN_DATEADT(date);
Datum
date_out(PG_FUNCTION_ARGS)
{
- DateADT date = PG_GETARG_DATEADT(0);
+ DateADT date = PG_GETARG_DATEADT(0);
char *result;
struct pg_tm tt,
*tm = &tt;
char buf[MAXDATELEN + 1];
- j2date(date +POSTGRES_EPOCH_JDATE,
+ j2date(date + POSTGRES_EPOCH_JDATE,
&(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
EncodeDateOnly(tm, DateStyle, buf);
Datum
date_send(PG_FUNCTION_ARGS)
{
- DateADT date = PG_GETARG_DATEADT(0);
+ DateADT date = PG_GETARG_DATEADT(0);
StringInfoData buf;
pq_begintypsend(&buf);
* time zone
*/
+static Timestamp
+date2timestamp(DateADT dateVal)
+{
+ Timestamp result;
+
#ifdef HAVE_INT64_TIMESTAMP
-/* date is days since 2000, timestamp is microseconds since same... */
-#define date2timestamp(dateVal) \
- ((Timestamp) ((dateVal) * USECS_PER_DAY))
+ /* date is days since 2000, timestamp is microseconds since same... */
+ result = dateVal * USECS_PER_DAY;
+ /* Date's range is wider than timestamp's, so must check for overflow */
+ if (result / USECS_PER_DAY != dateVal)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("date out of range for timestamp")));
#else
-/* date is days since 2000, timestamp is seconds since same... */
-#define date2timestamp(dateVal) \
- ((Timestamp) ((dateVal) * (double)SECS_PER_DAY))
+ /* date is days since 2000, timestamp is seconds since same... */
+ result = dateVal * (double) SECS_PER_DAY;
#endif
+ return result;
+}
+
static TimestampTz
date2timestamptz(DateADT dateVal)
{
tm->tm_hour = 0;
tm->tm_min = 0;
tm->tm_sec = 0;
- tz = DetermineLocalTimeZone(tm);
+ tz = DetermineTimeZoneOffset(tm, session_timezone);
#ifdef HAVE_INT64_TIMESTAMP
result = dateVal * USECS_PER_DAY + tz * USECS_PER_SEC;
+ /* Date's range is wider than timestamp's, so must check for overflow */
+ if ((result - tz * USECS_PER_SEC) / USECS_PER_DAY != dateVal)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("date out of range for timestamp")));
#else
- result = dateVal * (double)SECS_PER_DAY + tz;
+ result = dateVal * (double) SECS_PER_DAY + tz;
#endif
return result;
Datum
timestamp_date(PG_FUNCTION_ARGS)
{
- Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
+ Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
DateADT result;
struct pg_tm tt,
*tm = &tt;
case NOEND_ABSTIME:
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot convert reserved abstime value to date")));
+ errmsg("cannot convert reserved abstime value to date")));
/*
- * pretend to drop through to make compiler think that result
- * will be set
+ * pretend to drop through to make compiler think that result will
+ * be set
*/
default:
}
-/* date_text()
- * Convert date to text data type.
- */
-Datum
-date_text(PG_FUNCTION_ARGS)
-{
- /* Input is a Date, but may as well leave it in Datum form */
- Datum date = PG_GETARG_DATUM(0);
- text *result;
- char *str;
- int len;
-
- str = DatumGetCString(DirectFunctionCall1(date_out, date));
-
- len = strlen(str) + VARHDRSZ;
-
- result = palloc(len);
-
- VARATT_SIZEP(result) = len;
- memmove(VARDATA(result), str, (len - VARHDRSZ));
-
- pfree(str);
-
- PG_RETURN_TEXT_P(result);
-}
-
-
-/* text_date()
- * Convert text string to date.
- * Text type is not null terminated, so use temporary string
- * then call the standard input routine.
- */
-Datum
-text_date(PG_FUNCTION_ARGS)
-{
- text *str = PG_GETARG_TEXT_P(0);
- int i;
- char *sp,
- *dp,
- dstr[MAXDATELEN + 1];
-
- if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("invalid input syntax for type date: \"%s\"",
- VARDATA(str))));
-
- sp = VARDATA(str);
- dp = dstr;
- for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
- *dp++ = *sp++;
- *dp = '\0';
-
- return DirectFunctionCall1(date_in,
- CStringGetDatum(dstr));
-}
-
-
/*****************************************************************************
* Time ADT
*****************************************************************************/
tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result)
{
#ifdef HAVE_INT64_TIMESTAMP
- *result = ((((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
- * USECS_PER_SEC) + fsec;
+ *result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec)
+ * USECS_PER_SEC) + fsec;
#else
- *result = ((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
+ *result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
#endif
return 0;
}
#else
double trem;
+recalc:
trem = time;
- TMODULO(trem, tm->tm_hour, (double)SECS_PER_HOUR);
- TMODULO(trem, tm->tm_min, (double)SECS_PER_MINUTE);
+ TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
+ TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
TMODULO(trem, tm->tm_sec, 1.0);
+ trem = TIMEROUND(trem);
+ /* roundoff may need to propagate to higher-order fields */
+ if (trem >= 1.0)
+ {
+ time = ceil(time);
+ goto recalc;
+ }
*fsec = trem;
#endif
time_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
+Datum
+timetypmodin(PG_FUNCTION_ARGS)
+{
+ ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
+
+ PG_RETURN_INT32(anytime_typmodin(false, ta));
+}
+
+Datum
+timetypmodout(PG_FUNCTION_ARGS)
+{
+ int32 typmod = PG_GETARG_INT32(0);
+
+ PG_RETURN_CSTRING(anytime_typmodout(false, typmod));
+}
+
/* time_scale()
* Adjust time type for specified scale factor.
INT64CONST(5),
INT64CONST(0)
};
-
#else
/* note MAX_TIME_PRECISION differs in this case */
static const double TimeScales[MAX_TIME_PRECISION + 1] = {
if (typmod >= 0 && typmod <= MAX_TIME_PRECISION)
{
/*
- * Note: this round-to-nearest code is not completely consistent
- * about rounding values that are exactly halfway between integral
- * values. On most platforms, rint() will implement
- * round-to-nearest-even, but the integer code always rounds up
- * (away from zero). Is it worth trying to be consistent?
+ * Note: this round-to-nearest code is not completely consistent about
+ * rounding values that are exactly halfway between integral values.
+ * On most platforms, rint() will implement round-to-nearest-even, but
+ * the integer code always rounds up (away from zero). Is it worth
+ * trying to be consistent?
*/
#ifdef HAVE_INT64_TIMESTAMP
if (*time >= INT64CONST(0))
- {
*time = ((*time + TimeOffsets[typmod]) / TimeScales[typmod]) *
- TimeScales[typmod];
- }
+ TimeScales[typmod];
else
- {
*time = -((((-*time) + TimeOffsets[typmod]) / TimeScales[typmod]) *
- TimeScales[typmod]);
- }
+ TimeScales[typmod]);
#else
- *time = rint((double)*time * TimeScales[typmod])
- / TimeScales[typmod];
+ *time = rint((double) *time * TimeScales[typmod]) / TimeScales[typmod];
#endif
}
}
PG_RETURN_INT32(0);
}
+Datum
+time_hash(PG_FUNCTION_ARGS)
+{
+ /* We can use either hashint8 or hashfloat8 directly */
+#ifdef HAVE_INT64_TIMESTAMP
+ return hashint8(fcinfo);
+#else
+ return hashfloat8(fcinfo);
+#endif
+}
+
Datum
time_larger(PG_FUNCTION_ARGS)
{
overlaps_time(PG_FUNCTION_ARGS)
{
/*
- * The arguments are TimeADT, but we leave them as generic Datums to
- * avoid dereferencing nulls (TimeADT is pass-by-reference!)
+ * The arguments are TimeADT, but we leave them as generic Datums to avoid
+ * dereferencing nulls (TimeADT is pass-by-reference!)
*/
Datum ts1 = PG_GETARG_DATUM(0);
Datum te1 = PG_GETARG_DATUM(1);
(DatumGetTimeADT(t1) < DatumGetTimeADT(t2))
/*
- * If both endpoints of interval 1 are null, the result is null
- * (unknown). If just one endpoint is null, take ts1 as the non-null
- * one. Otherwise, take ts1 as the lesser endpoint.
+ * If both endpoints of interval 1 are null, the result is null (unknown).
+ * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
+ * take ts1 as the lesser endpoint.
*/
if (ts1IsNull)
{
if (TIMEADT_GT(ts1, ts2))
{
/*
- * This case is ts1 < te2 OR te1 < te2, which may look redundant
- * but in the presence of nulls it's not quite completely so.
+ * This case is ts1 < te2 OR te1 < te2, which may look redundant but
+ * in the presence of nulls it's not quite completely so.
*/
if (te2IsNull)
PG_RETURN_NULL();
PG_RETURN_NULL();
/*
- * If te1 is not null then we had ts1 <= te1 above, and we just
- * found ts1 >= te2, hence te1 >= te2.
+ * If te1 is not null then we had ts1 <= te1 above, and we just found
+ * ts1 >= te2, hence te1 >= te2.
*/
PG_RETURN_BOOL(false);
}
PG_RETURN_NULL();
/*
- * If te2 is not null then we had ts2 <= te2 above, and we just
- * found ts2 >= te1, hence te2 >= te1.
+ * If te2 is not null then we had ts2 <= te2 above, and we just found
+ * ts2 >= te1, hence te2 >= te1.
*/
PG_RETURN_BOOL(false);
}
{
/*
* For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
- * rather silly way of saying "true if both are nonnull, else
- * null".
+ * rather silly way of saying "true if both are nonnull, else null".
*/
if (te1IsNull || te2IsNull)
PG_RETURN_NULL();
Datum
timestamp_time(PG_FUNCTION_ARGS)
{
- Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
+ Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
TimeADT result;
struct pg_tm tt,
*tm = &tt;
* Could also do this with time = (timestamp / USECS_PER_DAY *
* USECS_PER_DAY) - timestamp;
*/
- result = ((((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
- USECS_PER_SEC) + fsec;
+ result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
+ USECS_PER_SEC) + fsec;
#else
- result = ((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
+ result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
#endif
PG_RETURN_TIMEADT(result);
* Could also do this with time = (timestamp / USECS_PER_DAY *
* USECS_PER_DAY) - timestamp;
*/
- result = ((((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
- USECS_PER_SEC) + fsec;
+ result = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
+ USECS_PER_SEC) + fsec;
#else
- result = ((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
+ result = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
#endif
PG_RETURN_TIMEADT(result);
Datum
datetime_timestamp(PG_FUNCTION_ARGS)
{
- DateADT date = PG_GETARG_DATEADT(0);
+ DateADT date = PG_GETARG_DATEADT(0);
TimeADT time = PG_GETARG_TIMEADT(1);
Timestamp result;
result = DatumGetTimestamp(DirectFunctionCall1(date_timestamp,
- DateADTGetDatum(date)));
+ DateADTGetDatum(date)));
result += time;
PG_RETURN_TIMESTAMP(result);
}
#else
result = span->time;
- if (result >= (double)SECS_PER_DAY || result < 0)
- result -= floor(result / (double)SECS_PER_DAY) * (double)SECS_PER_DAY;
+ if (result >= (double) SECS_PER_DAY || result < 0)
+ result -= floor(result / (double) SECS_PER_DAY) * (double) SECS_PER_DAY;
#endif
PG_RETURN_TIMEADT(result);
TimeADT time1;
result = time + span->time;
- TMODULO(result, time1, (double)SECS_PER_DAY);
+ TMODULO(result, time1, (double) SECS_PER_DAY);
if (result < 0)
result += SECS_PER_DAY;
#endif
TimeADT time1;
result = time - span->time;
- TMODULO(result, time1, (double)SECS_PER_DAY);
+ TMODULO(result, time1, (double) SECS_PER_DAY);
if (result < 0)
result += SECS_PER_DAY;
#endif
}
-/* time_text()
- * Convert time to text data type.
- */
-Datum
-time_text(PG_FUNCTION_ARGS)
-{
- /* Input is a Time, but may as well leave it in Datum form */
- Datum time = PG_GETARG_DATUM(0);
- text *result;
- char *str;
- int len;
-
- str = DatumGetCString(DirectFunctionCall1(time_out, time));
-
- len = strlen(str) + VARHDRSZ;
-
- result = palloc(len);
-
- VARATT_SIZEP(result) = len;
- memmove(VARDATA(result), str, (len - VARHDRSZ));
-
- pfree(str);
-
- PG_RETURN_TEXT_P(result);
-}
-
-
-/* text_time()
- * Convert text string to time.
- * Text type is not null terminated, so use temporary string
- * then call the standard input routine.
- */
-Datum
-text_time(PG_FUNCTION_ARGS)
-{
- text *str = PG_GETARG_TEXT_P(0);
- int i;
- char *sp,
- *dp,
- dstr[MAXDATELEN + 1];
-
- if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("invalid input syntax for type time: \"%s\"",
- VARDATA(str))));
-
- sp = VARDATA(str);
- dp = dstr;
- for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
- *dp++ = *sp++;
- *dp = '\0';
-
- return DirectFunctionCall3(time_in,
- CStringGetDatum(dstr),
- ObjectIdGetDatum(InvalidOid),
- Int32GetDatum(-1));
-}
-
/* time_part()
* Extract specified field from time type.
*/
case DTK_DECADE:
case DTK_CENTURY:
case DTK_MILLENNIUM:
+ case DTK_ISOYEAR:
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("\"time\" units \"%s\" not recognized",
- DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(units))))));
+ DatumGetCString(DirectFunctionCall1(textout,
+ PointerGetDatum(units))))));
result = 0;
}
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("\"time\" units \"%s\" not recognized",
DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(units))))));
+ PointerGetDatum(units))))));
result = 0;
}
tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result)
{
#ifdef HAVE_INT64_TIMESTAMP
- result->time = ((((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
+ result->time = ((((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) *
USECS_PER_SEC) + fsec;
#else
- result->time = ((tm->tm_hour * SECS_PER_MINUTE + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
+ result->time = ((tm->tm_hour * MINS_PER_HOUR + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec + fsec;
#endif
result->zone = tz;
timetz_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
+Datum
+timetztypmodin(PG_FUNCTION_ARGS)
+{
+ ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
+
+ PG_RETURN_INT32(anytime_typmodin(true, ta));
+}
+
+Datum
+timetztypmodout(PG_FUNCTION_ARGS)
+{
+ int32 typmod = PG_GETARG_INT32(0);
+
+ PG_RETURN_CSTRING(anytime_typmodout(true, typmod));
+}
+
/* timetz2tm()
* Convert TIME WITH TIME ZONE data type to POSIX time structure.
#else
double trem = time->time;
- TMODULO(trem, tm->tm_hour, (double)SECS_PER_HOUR);
- TMODULO(trem, tm->tm_min, (double)SECS_PER_MINUTE);
+recalc:
+ TMODULO(trem, tm->tm_hour, (double) SECS_PER_HOUR);
+ TMODULO(trem, tm->tm_min, (double) SECS_PER_MINUTE);
TMODULO(trem, tm->tm_sec, 1.0);
+ trem = TIMEROUND(trem);
+ /* roundoff may need to propagate to higher-order fields */
+ if (trem >= 1.0)
+ {
+ trem = ceil(time->time);
+ goto recalc;
+ }
*fsec = trem;
#endif
PG_RETURN_INT32(timetz_cmp_internal(time1, time2));
}
-/*
- * timetz, being an unusual size, needs a specialized hash function.
- */
Datum
timetz_hash(PG_FUNCTION_ARGS)
{
TimeTzADT *key = PG_GETARG_TIMETZADT_P(0);
+ uint32 thash;
/*
- * Specify hash length as sizeof(double) + sizeof(int4), not as
- * sizeof(TimeTzADT), so that any garbage pad bytes in the structure
- * won't be included in the hash!
+ * To avoid any problems with padding bytes in the struct, we figure the
+ * field hashes separately and XOR them. This also provides a convenient
+ * framework for dealing with the fact that the time field might be either
+ * double or int64.
*/
- return hash_any((unsigned char *) key, sizeof(key->time) + sizeof(key->zone));
+#ifdef HAVE_INT64_TIMESTAMP
+ thash = DatumGetUInt32(DirectFunctionCall1(hashint8,
+ Int64GetDatumFast(key->time)));
+#else
+ thash = DatumGetUInt32(DirectFunctionCall1(hashfloat8,
+ Float8GetDatumFast(key->time)));
+#endif
+ thash ^= DatumGetUInt32(hash_uint32(key->zone));
+ PG_RETURN_UINT32(thash);
}
Datum
result->time += USECS_PER_DAY;
#else
result->time = time->time + span->time;
- TMODULO(result->time, time1.time, (double)SECS_PER_DAY);
+ TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
if (result->time < 0)
result->time += SECS_PER_DAY;
#endif
result->time += USECS_PER_DAY;
#else
result->time = time->time - span->time;
- TMODULO(result->time, time1.time, (double)SECS_PER_DAY);
+ TMODULO(result->time, time1.time, (double) SECS_PER_DAY);
if (result->time < 0)
result->time += SECS_PER_DAY;
#endif
overlaps_timetz(PG_FUNCTION_ARGS)
{
/*
- * The arguments are TimeTzADT *, but we leave them as generic Datums
- * for convenience of notation --- and to avoid dereferencing nulls.
+ * The arguments are TimeTzADT *, but we leave them as generic Datums for
+ * convenience of notation --- and to avoid dereferencing nulls.
*/
Datum ts1 = PG_GETARG_DATUM(0);
Datum te1 = PG_GETARG_DATUM(1);
DatumGetBool(DirectFunctionCall2(timetz_lt,t1,t2))
/*
- * If both endpoints of interval 1 are null, the result is null
- * (unknown). If just one endpoint is null, take ts1 as the non-null
- * one. Otherwise, take ts1 as the lesser endpoint.
+ * If both endpoints of interval 1 are null, the result is null (unknown).
+ * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
+ * take ts1 as the lesser endpoint.
*/
if (ts1IsNull)
{
if (TIMETZ_GT(ts1, ts2))
{
/*
- * This case is ts1 < te2 OR te1 < te2, which may look redundant
- * but in the presence of nulls it's not quite completely so.
+ * This case is ts1 < te2 OR te1 < te2, which may look redundant but
+ * in the presence of nulls it's not quite completely so.
*/
if (te2IsNull)
PG_RETURN_NULL();
PG_RETURN_NULL();
/*
- * If te1 is not null then we had ts1 <= te1 above, and we just
- * found ts1 >= te2, hence te1 >= te2.
+ * If te1 is not null then we had ts1 <= te1 above, and we just found
+ * ts1 >= te2, hence te1 >= te2.
*/
PG_RETURN_BOOL(false);
}
PG_RETURN_NULL();
/*
- * If te2 is not null then we had ts2 <= te2 above, and we just
- * found ts2 >= te1, hence te2 >= te1.
+ * If te2 is not null then we had ts2 <= te2 above, and we just found
+ * ts2 >= te1, hence te2 >= te1.
*/
PG_RETURN_BOOL(false);
}
{
/*
* For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
- * rather silly way of saying "true if both are nonnull, else
- * null".
+ * rather silly way of saying "true if both are nonnull, else null".
*/
if (te1IsNull || te2IsNull)
PG_RETURN_NULL();
GetCurrentDateTime(tm);
time2tm(time, tm, &fsec);
- tz = DetermineLocalTimeZone(tm);
+ tz = DetermineTimeZoneOffset(tm, session_timezone);
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
Datum
datetimetz_timestamptz(PG_FUNCTION_ARGS)
{
- DateADT date = PG_GETARG_DATEADT(0);
+ DateADT date = PG_GETARG_DATEADT(0);
TimeTzADT *time = PG_GETARG_TIMETZADT_P(1);
TimestampTz result;
#ifdef HAVE_INT64_TIMESTAMP
- result = (date * USECS_PER_DAY + time->time) + time->zone * USECS_PER_SEC;
+ result = date * USECS_PER_DAY + time->time + time->zone * USECS_PER_SEC;
#else
- result = date * (double)SECS_PER_DAY + time->time + time->zone;
+ result = date * (double) SECS_PER_DAY + time->time + time->zone;
#endif
PG_RETURN_TIMESTAMP(result);
}
-/* timetz_text()
- * Convert timetz to text data type.
- */
-Datum
-timetz_text(PG_FUNCTION_ARGS)
-{
- /* Input is a Timetz, but may as well leave it in Datum form */
- Datum timetz = PG_GETARG_DATUM(0);
- text *result;
- char *str;
- int len;
-
- str = DatumGetCString(DirectFunctionCall1(timetz_out, timetz));
-
- len = strlen(str) + VARHDRSZ;
-
- result = palloc(len);
-
- VARATT_SIZEP(result) = len;
- memmove(VARDATA(result), str, (len - VARHDRSZ));
-
- pfree(str);
-
- PG_RETURN_TEXT_P(result);
-}
-
-
-/* text_timetz()
- * Convert text string to timetz.
- * Text type is not null terminated, so use temporary string
- * then call the standard input routine.
- */
-Datum
-text_timetz(PG_FUNCTION_ARGS)
-{
- text *str = PG_GETARG_TEXT_P(0);
- int i;
- char *sp,
- *dp,
- dstr[MAXDATELEN + 1];
-
- if (VARSIZE(str) - VARHDRSZ > MAXDATELEN)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_DATETIME_FORMAT),
- errmsg("invalid input syntax for type time with time zone: \"%s\"",
- VARDATA(str))));
-
- sp = VARDATA(str);
- dp = dstr;
- for (i = 0; i < (VARSIZE(str) - VARHDRSZ); i++)
- *dp++ = *sp++;
- *dp = '\0';
-
- return DirectFunctionCall3(timetz_in,
- CStringGetDatum(dstr),
- ObjectIdGetDatum(InvalidOid),
- Int32GetDatum(-1));
-}
-
/* timetz_part()
* Extract specified field from time type.
*/
case DTK_TZ_MINUTE:
result = -tz;
result /= SECS_PER_MINUTE;
- FMODULO(result, dummy, (double)SECS_PER_MINUTE);
+ FMODULO(result, dummy, (double) SECS_PER_MINUTE);
break;
case DTK_TZ_HOUR:
dummy = -tz;
- FMODULO(dummy, result, (double)SECS_PER_HOUR);
+ FMODULO(dummy, result, (double) SECS_PER_HOUR);
break;
case DTK_MICROSEC:
default:
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("\"time with time zone\" units \"%s\" not recognized",
- DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(units))))));
+ errmsg("\"time with time zone\" units \"%s\" not recognized",
+ DatumGetCString(DirectFunctionCall1(textout,
+ PointerGetDatum(units))))));
result = 0;
}
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("\"time with time zone\" units \"%s\" not recognized",
- DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(units))))));
+ errmsg("\"time with time zone\" units \"%s\" not recognized",
+ DatumGetCString(DirectFunctionCall1(textout,
+ PointerGetDatum(units))))));
result = 0;
}
TimeTzADT *t = PG_GETARG_TIMETZADT_P(1);
TimeTzADT *result;
int tz;
- char tzname[TZ_STRLEN_MAX];
- int len;
+ char tzname[TZ_STRLEN_MAX + 1];
+ int len;
pg_tz *tzp;
- struct pg_tm *tm;
- pg_time_t now;
-
- /* Find the specified timezone */
- len = (VARSIZE(zone)-VARHDRSZ>TZ_STRLEN_MAX) ?
- TZ_STRLEN_MAX : VARSIZE(zone) - VARHDRSZ;
- memcpy(tzname,VARDATA(zone),len);
- tzname[len]=0;
+
+ /*
+ * Look up the requested timezone. First we look in the timezone database
+ * (to handle cases like "America/New_York"), and if that fails, we look
+ * in the date token table (to handle cases like "EST").
+ */
+ len = Min(VARSIZE(zone) - VARHDRSZ, TZ_STRLEN_MAX);
+ memcpy(tzname, VARDATA(zone), len);
+ tzname[len] = '\0';
tzp = pg_tzset(tzname);
- if (!tzp) {
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("time zone \"%s\" not recognized", tzname)));
- PG_RETURN_NULL();
+ if (tzp)
+ {
+ /* Get the offset-from-GMT that is valid today for the selected zone */
+ pg_time_t now;
+ struct pg_tm *tm;
+
+ now = time(NULL);
+ tm = pg_localtime(&now, tzp);
+ tz = -tm->tm_gmtoff;
}
+ else
+ {
+ char *lowzone;
+ int type,
+ val;
- /* Get the offset-from-GMT that is valid today for the selected zone */
- if ((now = time(NULL)) < 0 ||
- (tm = pg_localtime(&now, tzp)) == NULL) {
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("could not determine current time")));
- PG_RETURN_NULL();
+ lowzone = downcase_truncate_identifier(VARDATA(zone),
+ VARSIZE(zone) - VARHDRSZ,
+ false);
+ type = DecodeSpecial(0, lowzone, &val);
+
+ if (type == TZ || type == DTZ)
+ tz = val * 60;
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("time zone \"%s\" not recognized", tzname)));
+ tz = 0; /* keep compiler quiet */
+ }
}
- result = (TimeTzADT *)palloc(sizeof(TimeTzADT));
-
- tz = -tm->tm_gmtoff;
+ result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
+
#ifdef HAVE_INT64_TIMESTAMP
result->time = t->time + (t->zone - tz) * USECS_PER_SEC;
while (result->time < INT64CONST(0))
result->zone = tz;
PG_RETURN_TIMETZADT_P(result);
-} /* timetz_zone() */
+}
/* timetz_izone()
* Encode time with time zone type with specified time interval as time zone.
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("\"interval\" time zone \"%s\" not valid",
DatumGetCString(DirectFunctionCall1(interval_out,
- PointerGetDatum(zone))))));
+ PointerGetDatum(zone))))));
#ifdef HAVE_INT64_TIMESTAMP
tz = -(zone->time / USECS_PER_SEC);
result->zone = tz;
PG_RETURN_TIMETZADT_P(result);
-} /* timetz_izone() */
+}