@ -992,6 +992,11 @@ typedef struct NUMProc
* L_currency_symbol ;
} NUMProc ;
/* Return flags for DCH_from_char() */
# define DCH_DATED 0x01
# define DCH_TIMED 0x02
# define DCH_ZONED 0x04
/* ----------
* Functions
* - - - - - - - - - -
@ -1025,7 +1030,8 @@ static int from_char_parse_int(int *dest, char **src, FormatNode *node);
static int seq_search ( char * name , const char * const * array , int type , int max , int * len ) ;
static int from_char_seq_search ( int * dest , char * * src , const char * const * array , int type , int max , FormatNode * node ) ;
static void do_to_timestamp ( text * date_txt , text * fmt , bool std ,
struct pg_tm * tm , fsec_t * fsec , int * fprec ) ;
struct pg_tm * tm , fsec_t * fsec , int * fprec ,
uint32 * flags ) ;
static char * fill_str ( char * str , int c , int max ) ;
static FormatNode * NUM_cache ( int len , NUMDesc * Num , text * pars_str , bool * shouldFree ) ;
static char * int_to_roman ( int number ) ;
@ -3517,6 +3523,109 @@ DCH_prevent_counter_overflow(void)
}
}
/* Get mask of date/time/zone components present in format nodes. */
static int
DCH_datetime_type ( FormatNode * node )
{
FormatNode * n ;
int flags = 0 ;
for ( n = node ; n - > type ! = NODE_TYPE_END ; n + + )
{
if ( n - > type ! = NODE_TYPE_ACTION )
continue ;
switch ( n - > key - > id )
{
case DCH_FX :
break ;
case DCH_A_M :
case DCH_P_M :
case DCH_a_m :
case DCH_p_m :
case DCH_AM :
case DCH_PM :
case DCH_am :
case DCH_pm :
case DCH_HH :
case DCH_HH12 :
case DCH_HH24 :
case DCH_MI :
case DCH_SS :
case DCH_MS : /* millisecond */
case DCH_US : /* microsecond */
case DCH_FF1 :
case DCH_FF2 :
case DCH_FF3 :
case DCH_FF4 :
case DCH_FF5 :
case DCH_FF6 :
case DCH_SSSS :
flags | = DCH_TIMED ;
break ;
case DCH_tz :
case DCH_TZ :
case DCH_OF :
ereport ( ERROR ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " formatting field \" %s \" is only supported in to_char " ,
n - > key - > name ) ) ) ;
flags | = DCH_ZONED ;
break ;
case DCH_TZH :
case DCH_TZM :
flags | = DCH_ZONED ;
break ;
case DCH_A_D :
case DCH_B_C :
case DCH_a_d :
case DCH_b_c :
case DCH_AD :
case DCH_BC :
case DCH_ad :
case DCH_bc :
case DCH_MONTH :
case DCH_Month :
case DCH_month :
case DCH_MON :
case DCH_Mon :
case DCH_mon :
case DCH_MM :
case DCH_DAY :
case DCH_Day :
case DCH_day :
case DCH_DY :
case DCH_Dy :
case DCH_dy :
case DCH_DDD :
case DCH_IDDD :
case DCH_DD :
case DCH_D :
case DCH_ID :
case DCH_WW :
case DCH_Q :
case DCH_CC :
case DCH_Y_YYY :
case DCH_YYYY :
case DCH_IYYY :
case DCH_YYY :
case DCH_IYY :
case DCH_YY :
case DCH_IY :
case DCH_Y :
case DCH_I :
case DCH_RM :
case DCH_rm :
case DCH_W :
case DCH_J :
flags | = DCH_DATED ;
break ;
}
}
return flags ;
}
/* select a DCHCacheEntry to hold the given format picture */
static DCHCacheEntry *
DCH_cache_getnew ( const char * str , bool std )
@ -3808,7 +3917,7 @@ to_timestamp(PG_FUNCTION_ARGS)
fsec_t fsec ;
int fprec ;
do_to_timestamp ( date_txt , fmt , false , & tm , & fsec , & fprec ) ;
do_to_timestamp ( date_txt , fmt , false , & tm , & fsec , & fprec , NULL ) ;
/* Use the specified time zone, if any. */
if ( tm . tm_zone )
@ -3847,7 +3956,7 @@ to_date(PG_FUNCTION_ARGS)
struct pg_tm tm ;
fsec_t fsec ;
do_to_timestamp ( date_txt , fmt , false , & tm , & fsec , NULL ) ;
do_to_timestamp ( date_txt , fmt , false , & tm , & fsec , NULL , NULL ) ;
/* Prevent overflow in Julian-day routines */
if ( ! IS_VALID_JULIAN ( tm . tm_year , tm . tm_mon , tm . tm_mday ) )
@ -3868,6 +3977,176 @@ to_date(PG_FUNCTION_ARGS)
PG_RETURN_DATEADT ( result ) ;
}
/*
* Convert the ' date_txt ' input to a datetime type using argument ' fmt ' as a format string .
* The actual data type ( returned in ' typid ' , ' typmod ' ) is determined by
* the presence of date / time / zone components in the format string .
*
* When timezone component is present , the corresponding offset is set to ' * tz ' .
*/
Datum
parse_datetime ( text * date_txt , text * fmt , bool strict , Oid * typid ,
int32 * typmod , int * tz )
{
struct pg_tm tm ;
fsec_t fsec ;
int fprec = 0 ;
uint32 flags ;
do_to_timestamp ( date_txt , fmt , strict , & tm , & fsec , & fprec , & flags ) ;
* typmod = fprec ? fprec : - 1 ; /* fractional part precision */
if ( flags & DCH_DATED )
{
if ( flags & DCH_TIMED )
{
if ( flags & DCH_ZONED )
{
TimestampTz result ;
if ( tm . tm_zone )
{
int dterr = DecodeTimezone ( unconstify ( char * , tm . tm_zone ) , tz ) ;
if ( dterr )
DateTimeParseError ( dterr , text_to_cstring ( date_txt ) , " timestamptz " ) ;
}
else
{
/*
* Time zone is present in format string , but not in input
* string . Assuming do_to_timestamp ( ) triggers no error
* this should be possible only in non - strict case .
*/
Assert ( ! strict ) ;
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_DATETIME_FORMAT ) ,
errmsg ( " missing time zone in input string for type timestamptz " ) ) ) ;
}
if ( tm2timestamp ( & tm , fsec , tz , & result ) ! = 0 )
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " timestamptz out of range " ) ) ) ;
AdjustTimestampForTypmod ( & result , * typmod ) ;
* typid = TIMESTAMPTZOID ;
return TimestampTzGetDatum ( result ) ;
}
else
{
Timestamp result ;
if ( tm2timestamp ( & tm , fsec , NULL , & result ) ! = 0 )
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " timestamp out of range " ) ) ) ;
AdjustTimestampForTypmod ( & result , * typmod ) ;
* typid = TIMESTAMPOID ;
return TimestampGetDatum ( result ) ;
}
}
else
{
if ( flags & DCH_ZONED )
{
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_DATETIME_FORMAT ) ,
errmsg ( " datetime format is zoned but not timed " ) ) ) ;
}
else
{
DateADT result ;
/* Prevent overflow in Julian-day routines */
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 \" " ,
text_to_cstring ( date_txt ) ) ) ) ;
result = date2j ( tm . tm_year , tm . tm_mon , tm . tm_mday ) -
POSTGRES_EPOCH_JDATE ;
/* Now check for just-out-of-range dates */
if ( ! IS_VALID_DATE ( result ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " date out of range: \" %s \" " ,
text_to_cstring ( date_txt ) ) ) ) ;
* typid = DATEOID ;
return DateADTGetDatum ( result ) ;
}
}
}
else if ( flags & DCH_TIMED )
{
if ( flags & DCH_ZONED )
{
TimeTzADT * result = palloc ( sizeof ( TimeTzADT ) ) ;
if ( tm . tm_zone )
{
int dterr = DecodeTimezone ( unconstify ( char * , tm . tm_zone ) , tz ) ;
if ( dterr )
DateTimeParseError ( dterr , text_to_cstring ( date_txt ) , " timetz " ) ;
}
else
{
/*
* Time zone is present in format string , but not in input
* string . Assuming do_to_timestamp ( ) triggers no error this
* should be possible only in non - strict case .
*/
Assert ( ! strict ) ;
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_DATETIME_FORMAT ) ,
errmsg ( " missing time zone in input string for type timetz " ) ) ) ;
}
if ( tm2timetz ( & tm , fsec , * tz , result ) ! = 0 )
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " timetz out of range " ) ) ) ;
AdjustTimeForTypmod ( & result - > time , * typmod ) ;
* typid = TIMETZOID ;
return TimeTzADTPGetDatum ( result ) ;
}
else
{
TimeADT result ;
if ( tm2time ( & tm , fsec , & result ) ! = 0 )
ereport ( ERROR ,
( errcode ( ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ) ,
errmsg ( " time out of range " ) ) ) ;
AdjustTimeForTypmod ( & result , * typmod ) ;
* typid = TIMEOID ;
return TimeADTGetDatum ( result ) ;
}
}
else
{
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_DATETIME_FORMAT ) ,
errmsg ( " datetime format is not dated and not timed " ) ) ) ;
}
return ( Datum ) 0 ;
}
/*
* do_to_timestamp : shared code for to_timestamp and to_date
*
@ -3883,7 +4162,8 @@ to_date(PG_FUNCTION_ARGS)
*/
static void
do_to_timestamp ( text * date_txt , text * fmt , bool std ,
struct pg_tm * tm , fsec_t * fsec , int * fprec )
struct pg_tm * tm , fsec_t * fsec , int * fprec ,
uint32 * flags )
{
FormatNode * format ;
TmFromChar tmfc ;
@ -3940,6 +4220,9 @@ do_to_timestamp(text *date_txt, text *fmt, bool std,
pfree ( fmt_str ) ;
if ( flags )
* flags = DCH_datetime_type ( format ) ;
if ( ! incache )
pfree ( format ) ;
}