@ -483,6 +483,234 @@ timestamptz_in(PG_FUNCTION_ARGS)
PG_RETURN_TIMESTAMPTZ ( result ) ;
}
/*
* Try to parse a timezone specification , and return its timezone offset value
* if it ' s acceptable . Otherwise , an error is thrown .
*/
static int
parse_sane_timezone ( struct pg_tm * tm , text * zone )
{
char tzname [ TZ_STRLEN_MAX + 1 ] ;
int rt ;
int tz ;
text_to_cstring_buffer ( zone , tzname , sizeof ( tzname ) ) ;
/*
* Look up the requested timezone . First we try to interpret it as a
* numeric timezone specification ; if DecodeTimezone decides it doesn ' t
* like the format , we look in the date token table ( to handle cases like
* " EST " ) , and if that also fails , we look in the timezone database ( to
* handle cases like " America/New_York " ) . ( This matches the order in
* which timestamp input checks the cases ; it ' s important because the
* timezone database unwisely uses a few zone names that are identical to
* offset abbreviations . )
*
* Note pg_tzset happily parses numeric input that DecodeTimezone would
* reject . To avoid having it accept input that would otherwise be seen
* as invalid , it ' s enough to disallow having a digit in the first
* position of our input string .
*/
if ( isdigit ( * tzname ) )
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_PARAMETER_VALUE ) ,
errmsg ( " invalid input syntax for numeric time zone: \" %s \" " ,
tzname ) ,
errhint ( " Numeric time zones must have \" - \" or \" + \" as first character. " ) ) ) ;
rt = DecodeTimezone ( tzname , & tz ) ;
if ( rt ! = 0 )
{
char * lowzone ;
int type ,
val ;
if ( rt = = DTERR_TZDISP_OVERFLOW )
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_PARAMETER_VALUE ) ,
errmsg ( " numeric time zone \" %s \" out of range " , tzname ) ) ) ;
else if ( rt ! = DTERR_BAD_FORMAT )
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_PARAMETER_VALUE ) ,
errmsg ( " time zone \" %s \" not recognized " , tzname ) ) ) ;
lowzone = downcase_truncate_identifier ( tzname ,
strlen ( tzname ) ,
false ) ;
type = DecodeSpecial ( 0 , lowzone , & val ) ;
if ( type = = TZ | | type = = DTZ )
tz = val * MINS_PER_HOUR ;
else
{
pg_tz * tzp ;
tzp = pg_tzset ( tzname ) ;
if ( tzp )
tz = DetermineTimeZoneOffset ( tm , tzp ) ;
else
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_PARAMETER_VALUE ) ,
errmsg ( " time zone \" %s \" not recognized " , tzname ) ) ) ;
}
}
return tz ;
}
/*
* make_timestamp_internal
* workhorse for make_timestamp and make_timestamptz
*/
static Timestamp
make_timestamp_internal ( int year , int month , int day ,
int hour , int min , double sec )
{
struct pg_tm tm ;
TimeOffset date ;
TimeOffset time ;
int dterr ;
Timestamp result ;
tm . tm_year = year ;
tm . tm_mon = month ;
tm . tm_mday = day ;
/*
* Note : we ' ll reject zero or negative year values . Perhaps negatives
* should be allowed to represent BC years ?
*/
dterr = ValidateDate ( DTK_DATE_M , false , false , false , & tm ) ;
if ( dterr ! = 0 )
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_FIELD_OVERFLOW ) ,
errmsg ( " date field value out of range: %d-%02d-%02d " ,
year , month , day ) ) ) ;
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: %d-%02d-%02d " ,
year , month , day ) ) ) ;
date = date2j ( tm . tm_year , tm . tm_mon , tm . tm_mday ) - POSTGRES_EPOCH_JDATE ;
/* This should match the checks in DecodeTimeOnly */
if ( hour < 0 | | min < 0 | | min > MINS_PER_HOUR - 1 | |
sec < 0 | | sec > SECS_PER_MINUTE | |
hour > HOURS_PER_DAY | |
/* test for > 24:00:00 */
( hour = = HOURS_PER_DAY & & ( min > 0 | | sec > 0 ) ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_FIELD_OVERFLOW ) ,
errmsg ( " time field value out of range: %d:%02d:%02g " ,
hour , min , sec ) ) ) ;
/* This should match tm2time */
# ifdef HAVE_INT64_TIMESTAMP
time = ( ( ( hour * MINS_PER_HOUR + min ) * SECS_PER_MINUTE )
* USECS_PER_SEC ) + rint ( sec * USECS_PER_SEC ) ;
result = date * USECS_PER_DAY + time ;
/* check for major overflow */
if ( ( result - time ) / USECS_PER_DAY ! = date )
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " timestamp out of range: %d-%02d-%02d %d:%02d:%02g " ,
year , month , day ,
hour , min , sec ) ) ) ;
/* check for just-barely overflow (okay except time-of-day wraps) */
/* caution: we want to allow 1999-12-31 24:00:00 */
if ( ( result < 0 & & date > 0 ) | |
( result > 0 & & date < - 1 ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " timestamp out of range: %d-%02d-%02d %d:%02d:%02g " ,
year , month , day ,
hour , min , sec ) ) ) ;
# else
time = ( ( hour * MINS_PER_HOUR + min ) * SECS_PER_MINUTE ) + sec ;
result = date * SECS_PER_DAY + time ;
# endif
return result ;
}
/*
* make_timestamp ( ) - timestamp constructor
*/
Datum
make_timestamp ( PG_FUNCTION_ARGS )
{
int32 year = PG_GETARG_INT32 ( 0 ) ;
int32 month = PG_GETARG_INT32 ( 1 ) ;
int32 mday = PG_GETARG_INT32 ( 2 ) ;
int32 hour = PG_GETARG_INT32 ( 3 ) ;
int32 min = PG_GETARG_INT32 ( 4 ) ;
float8 sec = PG_GETARG_FLOAT8 ( 5 ) ;
Timestamp result ;
result = make_timestamp_internal ( year , month , mday ,
hour , min , sec ) ;
PG_RETURN_TIMESTAMP ( result ) ;
}
/*
* make_timestamptz ( ) - timestamp with time zone constructor
*/
Datum
make_timestamptz ( PG_FUNCTION_ARGS )
{
int32 year = PG_GETARG_INT32 ( 0 ) ;
int32 month = PG_GETARG_INT32 ( 1 ) ;
int32 mday = PG_GETARG_INT32 ( 2 ) ;
int32 hour = PG_GETARG_INT32 ( 3 ) ;
int32 min = PG_GETARG_INT32 ( 4 ) ;
float8 sec = PG_GETARG_FLOAT8 ( 5 ) ;
Timestamp result ;
result = make_timestamp_internal ( year , month , mday ,
hour , min , sec ) ;
PG_RETURN_TIMESTAMPTZ ( timestamp2timestamptz ( result ) ) ;
}
/*
* Construct a timestamp with time zone .
* As above , but the time zone is specified as seventh argument .
*/
Datum
make_timestamptz_at_timezone ( PG_FUNCTION_ARGS )
{
int32 year = PG_GETARG_INT32 ( 0 ) ;
int32 month = PG_GETARG_INT32 ( 1 ) ;
int32 mday = PG_GETARG_INT32 ( 2 ) ;
int32 hour = PG_GETARG_INT32 ( 3 ) ;
int32 min = PG_GETARG_INT32 ( 4 ) ;
float8 sec = PG_GETARG_FLOAT8 ( 5 ) ;
text * zone = PG_GETARG_TEXT_PP ( 6 ) ;
Timestamp timestamp ;
struct pg_tm tt ;
int tz ;
fsec_t fsec ;
timestamp = make_timestamp_internal ( year , month , mday ,
hour , min , sec ) ;
if ( timestamp2tm ( timestamp , NULL , & tt , & fsec , NULL , NULL ) ! = 0 )
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " timestamp out of range " ) ) ) ;
tz = parse_sane_timezone ( & tt , zone ) ;
PG_RETURN_TIMESTAMPTZ ( ( TimestampTz ) dt2local ( timestamp , - tz ) ) ;
}
/* timestamptz_out()
* Convert a timestamp to external form .
*/
@ -1220,6 +1448,42 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
}
}
/*
* make_interval - numeric Interval constructor
*/
Datum
make_interval ( PG_FUNCTION_ARGS )
{
int32 years = PG_GETARG_INT32 ( 0 ) ;
int32 months = PG_GETARG_INT32 ( 1 ) ;
int32 weeks = PG_GETARG_INT32 ( 2 ) ;
int32 days = PG_GETARG_INT32 ( 3 ) ;
int32 hours = PG_GETARG_INT32 ( 4 ) ;
int32 mins = PG_GETARG_INT32 ( 5 ) ;
double secs = PG_GETARG_FLOAT8 ( 6 ) ;
Interval * result ;
result = ( Interval * ) palloc ( sizeof ( Interval ) ) ;
result - > month = years * MONTHS_PER_YEAR + months ;
result - > day = weeks * 7 + days ;
# ifdef HAVE_INT64_TIMESTAMP
result - > time = ( ( ( ( hours * INT64CONST ( 60 ) ) +
mins ) * INT64CONST ( 60 ) ) +
secs ) * USECS_PER_SEC ;
# else
result - > time = ( ( ( hours * ( double ) MINS_PER_HOUR ) +
mins ) * ( double ) SECS_PER_MINUTE ) +
secs ;
# endif
# ifdef NOT_USED
/* this is a no-op for negative typmods */
AdjustIntervalForTypmod ( result , - 1 ) ;
# endif
PG_RETURN_INTERVAL_P ( result ) ;
}
/* EncodeSpecialTimestamp()
* Convert reserved timestamp data type to string .