DateADT
GetSQLCurrentDate(void)
{
- TimestampTz ts;
- struct pg_tm tt,
- *tm = &tt;
- fsec_t fsec;
- int tz;
+ struct pg_tm tm;
- ts = GetCurrentTransactionStartTimestamp();
+ static int cache_year = 0;
+ static int cache_mon = 0;
+ static int cache_mday = 0;
+ static DateADT cache_date;
- if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0)
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("timestamp out of range")));
+ GetCurrentDateTime(&tm);
- return date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
+ /*
+ * date2j involves several integer divisions; moreover, unless our session
+ * lives across local midnight, we don't really have to do it more than
+ * once. So it seems worth having a separate cache here.
+ */
+ if (tm.tm_year != cache_year ||
+ tm.tm_mon != cache_mon ||
+ tm.tm_mday != cache_mday)
+ {
+ cache_date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
+ cache_year = tm.tm_year;
+ cache_mon = tm.tm_mon;
+ cache_mday = tm.tm_mday;
+ }
+
+ return cache_date;
}
/*
GetSQLCurrentTime(int32 typmod)
{
TimeTzADT *result;
- TimestampTz ts;
struct pg_tm tt,
*tm = &tt;
fsec_t fsec;
int tz;
- ts = GetCurrentTransactionStartTimestamp();
-
- if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0)
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("timestamp out of range")));
+ GetCurrentTimeUsec(tm, &fsec, &tz);
result = (TimeTzADT *) palloc(sizeof(TimeTzADT));
tm2timetz(tm, fsec, tz, result);
GetSQLLocalTime(int32 typmod)
{
TimeADT result;
- TimestampTz ts;
struct pg_tm tt,
*tm = &tt;
fsec_t fsec;
int tz;
- ts = GetCurrentTransactionStartTimestamp();
-
- if (timestamp2tm(ts, &tz, tm, &fsec, NULL, NULL) != 0)
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("timestamp out of range")));
+ GetCurrentTimeUsec(tm, &fsec, &tz);
tm2time(tm, fsec, &result);
AdjustTimeForTypmod(&result, typmod);
/*
* GetCurrentDateTime()
*
- * Get the transaction start time ("now()") broken down as a struct pg_tm.
+ * Get the transaction start time ("now()") broken down as a struct pg_tm,
+ * converted according to the session timezone setting.
+ *
+ * This is just a convenience wrapper for GetCurrentTimeUsec, to cover the
+ * case where caller doesn't need either fractional seconds or tz offset.
*/
void
GetCurrentDateTime(struct pg_tm *tm)
{
- int tz;
fsec_t fsec;
- timestamp2tm(GetCurrentTransactionStartTimestamp(), &tz, tm, &fsec,
- NULL, NULL);
- /* Note: don't pass NULL tzp to timestamp2tm; affects behavior */
+ GetCurrentTimeUsec(tm, &fsec, NULL);
}
/*
* GetCurrentTimeUsec()
*
* Get the transaction start time ("now()") broken down as a struct pg_tm,
- * including fractional seconds and timezone offset.
+ * including fractional seconds and timezone offset. The time is converted
+ * according to the session timezone setting.
+ *
+ * Callers may pass tzp = NULL if they don't need the offset, but this does
+ * not affect the conversion behavior (unlike timestamp2tm()).
+ *
+ * Internally, we cache the result, since this could be called many times
+ * in a transaction, within which now() doesn't change.
*/
void
GetCurrentTimeUsec(struct pg_tm *tm, fsec_t *fsec, int *tzp)
{
- int tz;
+ TimestampTz cur_ts = GetCurrentTransactionStartTimestamp();
+
+ /*
+ * The cache key must include both current time and current timezone. By
+ * representing the timezone by just a pointer, we're assuming that
+ * distinct timezone settings could never have the same pointer value.
+ * This is true by virtue of the hashtable used inside pg_tzset();
+ * however, it might need another look if we ever allow entries in that
+ * hash to be recycled.
+ */
+ static TimestampTz cache_ts = 0;
+ static pg_tz *cache_timezone = NULL;
+ static struct pg_tm cache_tm;
+ static fsec_t cache_fsec;
+ static int cache_tz;
+
+ if (cur_ts != cache_ts || session_timezone != cache_timezone)
+ {
+ /*
+ * Make sure cache is marked invalid in case of error after partial
+ * update within timestamp2tm.
+ */
+ cache_timezone = NULL;
+
+ /*
+ * Perform the computation, storing results into cache. We do not
+ * really expect any error here, since current time surely ought to be
+ * within range, but check just for sanity's sake.
+ */
+ if (timestamp2tm(cur_ts, &cache_tz, &cache_tm, &cache_fsec,
+ NULL, session_timezone) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ errmsg("timestamp out of range")));
+
+ /* OK, so mark the cache valid. */
+ cache_ts = cur_ts;
+ cache_timezone = session_timezone;
+ }
- timestamp2tm(GetCurrentTransactionStartTimestamp(), &tz, tm, fsec,
- NULL, NULL);
- /* Note: don't pass NULL tzp to timestamp2tm; affects behavior */
+ *tm = cache_tm;
+ *fsec = cache_fsec;
if (tzp != NULL)
- *tzp = tz;
+ *tzp = cache_tz;
}