@@ -992,6 +992,11 @@ typedef struct NUMProc
992992 * L_currency_symbol ;
993993} NUMProc ;
994994
995+ /* Return flags for DCH_from_char() */
996+ #define DCH_DATED 0x01
997+ #define DCH_TIMED 0x02
998+ #define DCH_ZONED 0x04
999+
9951000/* ----------
9961001 * Functions
9971002 * ----------
@@ -1025,7 +1030,8 @@ static int from_char_parse_int(int *dest, char **src, FormatNode *node);
10251030static int seq_search (char * name , const char * const * array , int type , int max , int * len );
10261031static int from_char_seq_search (int * dest , char * * src , const char * const * array , int type , int max , FormatNode * node );
10271032static void do_to_timestamp (text * date_txt , text * fmt , bool std ,
1028- struct pg_tm * tm , fsec_t * fsec , int * fprec );
1033+ struct pg_tm * tm , fsec_t * fsec , int * fprec ,
1034+ uint32 * flags );
10291035static char * fill_str (char * str , int c , int max );
10301036static FormatNode * NUM_cache (int len , NUMDesc * Num , text * pars_str , bool * shouldFree );
10311037static char * int_to_roman (int number );
@@ -3517,6 +3523,109 @@ DCH_prevent_counter_overflow(void)
35173523 }
35183524}
35193525
3526+ /* Get mask of date/time/zone components present in format nodes. */
3527+ static int
3528+ DCH_datetime_type (FormatNode * node )
3529+ {
3530+ FormatNode * n ;
3531+ int flags = 0 ;
3532+
3533+ for (n = node ; n -> type != NODE_TYPE_END ; n ++ )
3534+ {
3535+ if (n -> type != NODE_TYPE_ACTION )
3536+ continue ;
3537+
3538+ switch (n -> key -> id )
3539+ {
3540+ case DCH_FX :
3541+ break ;
3542+ case DCH_A_M :
3543+ case DCH_P_M :
3544+ case DCH_a_m :
3545+ case DCH_p_m :
3546+ case DCH_AM :
3547+ case DCH_PM :
3548+ case DCH_am :
3549+ case DCH_pm :
3550+ case DCH_HH :
3551+ case DCH_HH12 :
3552+ case DCH_HH24 :
3553+ case DCH_MI :
3554+ case DCH_SS :
3555+ case DCH_MS : /* millisecond */
3556+ case DCH_US : /* microsecond */
3557+ case DCH_FF1 :
3558+ case DCH_FF2 :
3559+ case DCH_FF3 :
3560+ case DCH_FF4 :
3561+ case DCH_FF5 :
3562+ case DCH_FF6 :
3563+ case DCH_SSSS :
3564+ flags |= DCH_TIMED ;
3565+ break ;
3566+ case DCH_tz :
3567+ case DCH_TZ :
3568+ case DCH_OF :
3569+ ereport (ERROR ,
3570+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
3571+ errmsg ("formatting field \"%s\" is only supported in to_char" ,
3572+ n -> key -> name )));
3573+ flags |= DCH_ZONED ;
3574+ break ;
3575+ case DCH_TZH :
3576+ case DCH_TZM :
3577+ flags |= DCH_ZONED ;
3578+ break ;
3579+ case DCH_A_D :
3580+ case DCH_B_C :
3581+ case DCH_a_d :
3582+ case DCH_b_c :
3583+ case DCH_AD :
3584+ case DCH_BC :
3585+ case DCH_ad :
3586+ case DCH_bc :
3587+ case DCH_MONTH :
3588+ case DCH_Month :
3589+ case DCH_month :
3590+ case DCH_MON :
3591+ case DCH_Mon :
3592+ case DCH_mon :
3593+ case DCH_MM :
3594+ case DCH_DAY :
3595+ case DCH_Day :
3596+ case DCH_day :
3597+ case DCH_DY :
3598+ case DCH_Dy :
3599+ case DCH_dy :
3600+ case DCH_DDD :
3601+ case DCH_IDDD :
3602+ case DCH_DD :
3603+ case DCH_D :
3604+ case DCH_ID :
3605+ case DCH_WW :
3606+ case DCH_Q :
3607+ case DCH_CC :
3608+ case DCH_Y_YYY :
3609+ case DCH_YYYY :
3610+ case DCH_IYYY :
3611+ case DCH_YYY :
3612+ case DCH_IYY :
3613+ case DCH_YY :
3614+ case DCH_IY :
3615+ case DCH_Y :
3616+ case DCH_I :
3617+ case DCH_RM :
3618+ case DCH_rm :
3619+ case DCH_W :
3620+ case DCH_J :
3621+ flags |= DCH_DATED ;
3622+ break ;
3623+ }
3624+ }
3625+
3626+ return flags ;
3627+ }
3628+
35203629/* select a DCHCacheEntry to hold the given format picture */
35213630static DCHCacheEntry *
35223631DCH_cache_getnew (const char * str , bool std )
@@ -3808,7 +3917,7 @@ to_timestamp(PG_FUNCTION_ARGS)
38083917 fsec_t fsec ;
38093918 int fprec ;
38103919
3811- do_to_timestamp (date_txt , fmt , false, & tm , & fsec , & fprec );
3920+ do_to_timestamp (date_txt , fmt , false, & tm , & fsec , & fprec , NULL );
38123921
38133922 /* Use the specified time zone, if any. */
38143923 if (tm .tm_zone )
@@ -3847,7 +3956,7 @@ to_date(PG_FUNCTION_ARGS)
38473956 struct pg_tm tm ;
38483957 fsec_t fsec ;
38493958
3850- do_to_timestamp (date_txt , fmt , false, & tm , & fsec , NULL );
3959+ do_to_timestamp (date_txt , fmt , false, & tm , & fsec , NULL , NULL );
38513960
38523961 /* Prevent overflow in Julian-day routines */
38533962 if (!IS_VALID_JULIAN (tm .tm_year , tm .tm_mon , tm .tm_mday ))
@@ -3868,6 +3977,176 @@ to_date(PG_FUNCTION_ARGS)
38683977 PG_RETURN_DATEADT (result );
38693978}
38703979
3980+ /*
3981+ * Convert the 'date_txt' input to a datetime type using argument 'fmt' as a format string.
3982+ * The actual data type (returned in 'typid', 'typmod') is determined by
3983+ * the presence of date/time/zone components in the format string.
3984+ *
3985+ * When timezone component is present, the corresponding offset is set to '*tz'.
3986+ */
3987+ Datum
3988+ parse_datetime (text * date_txt , text * fmt , bool strict , Oid * typid ,
3989+ int32 * typmod , int * tz )
3990+ {
3991+ struct pg_tm tm ;
3992+ fsec_t fsec ;
3993+ int fprec = 0 ;
3994+ uint32 flags ;
3995+
3996+ do_to_timestamp (date_txt , fmt , strict , & tm , & fsec , & fprec , & flags );
3997+
3998+ * typmod = fprec ? fprec : -1 ; /* fractional part precision */
3999+
4000+ if (flags & DCH_DATED )
4001+ {
4002+ if (flags & DCH_TIMED )
4003+ {
4004+ if (flags & DCH_ZONED )
4005+ {
4006+ TimestampTz result ;
4007+
4008+ if (tm .tm_zone )
4009+ {
4010+ int dterr = DecodeTimezone (unconstify (char * , tm .tm_zone ), tz );
4011+
4012+ if (dterr )
4013+ DateTimeParseError (dterr , text_to_cstring (date_txt ), "timestamptz" );
4014+ }
4015+ else
4016+ {
4017+ /*
4018+ * Time zone is present in format string, but not in input
4019+ * string. Assuming do_to_timestamp() triggers no error
4020+ * this should be possible only in non-strict case.
4021+ */
4022+ Assert (!strict );
4023+
4024+ ereport (ERROR ,
4025+ (errcode (ERRCODE_INVALID_DATETIME_FORMAT ),
4026+ errmsg ("missing time zone in input string for type timestamptz" )));
4027+ }
4028+
4029+ if (tm2timestamp (& tm , fsec , tz , & result ) != 0 )
4030+ ereport (ERROR ,
4031+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
4032+ errmsg ("timestamptz out of range" )));
4033+
4034+ AdjustTimestampForTypmod (& result , * typmod );
4035+
4036+ * typid = TIMESTAMPTZOID ;
4037+ return TimestampTzGetDatum (result );
4038+ }
4039+ else
4040+ {
4041+ Timestamp result ;
4042+
4043+ if (tm2timestamp (& tm , fsec , NULL , & result ) != 0 )
4044+ ereport (ERROR ,
4045+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
4046+ errmsg ("timestamp out of range" )));
4047+
4048+ AdjustTimestampForTypmod (& result , * typmod );
4049+
4050+ * typid = TIMESTAMPOID ;
4051+ return TimestampGetDatum (result );
4052+ }
4053+ }
4054+ else
4055+ {
4056+ if (flags & DCH_ZONED )
4057+ {
4058+ ereport (ERROR ,
4059+ (errcode (ERRCODE_INVALID_DATETIME_FORMAT ),
4060+ errmsg ("datetime format is zoned but not timed" )));
4061+ }
4062+ else
4063+ {
4064+ DateADT result ;
4065+
4066+ /* Prevent overflow in Julian-day routines */
4067+ if (!IS_VALID_JULIAN (tm .tm_year , tm .tm_mon , tm .tm_mday ))
4068+ ereport (ERROR ,
4069+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
4070+ errmsg ("date out of range: \"%s\"" ,
4071+ text_to_cstring (date_txt ))));
4072+
4073+ result = date2j (tm .tm_year , tm .tm_mon , tm .tm_mday ) -
4074+ POSTGRES_EPOCH_JDATE ;
4075+
4076+ /* Now check for just-out-of-range dates */
4077+ if (!IS_VALID_DATE (result ))
4078+ ereport (ERROR ,
4079+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
4080+ errmsg ("date out of range: \"%s\"" ,
4081+ text_to_cstring (date_txt ))));
4082+
4083+ * typid = DATEOID ;
4084+ return DateADTGetDatum (result );
4085+ }
4086+ }
4087+ }
4088+ else if (flags & DCH_TIMED )
4089+ {
4090+ if (flags & DCH_ZONED )
4091+ {
4092+ TimeTzADT * result = palloc (sizeof (TimeTzADT ));
4093+
4094+ if (tm .tm_zone )
4095+ {
4096+ int dterr = DecodeTimezone (unconstify (char * , tm .tm_zone ), tz );
4097+
4098+ if (dterr )
4099+ DateTimeParseError (dterr , text_to_cstring (date_txt ), "timetz" );
4100+ }
4101+ else
4102+ {
4103+ /*
4104+ * Time zone is present in format string, but not in input
4105+ * string. Assuming do_to_timestamp() triggers no error this
4106+ * should be possible only in non-strict case.
4107+ */
4108+ Assert (!strict );
4109+
4110+ ereport (ERROR ,
4111+ (errcode (ERRCODE_INVALID_DATETIME_FORMAT ),
4112+ errmsg ("missing time zone in input string for type timetz" )));
4113+ }
4114+
4115+ if (tm2timetz (& tm , fsec , * tz , result ) != 0 )
4116+ ereport (ERROR ,
4117+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
4118+ errmsg ("timetz out of range" )));
4119+
4120+ AdjustTimeForTypmod (& result -> time , * typmod );
4121+
4122+ * typid = TIMETZOID ;
4123+ return TimeTzADTPGetDatum (result );
4124+ }
4125+ else
4126+ {
4127+ TimeADT result ;
4128+
4129+ if (tm2time (& tm , fsec , & result ) != 0 )
4130+ ereport (ERROR ,
4131+ (errcode (ERRCODE_DATETIME_VALUE_OUT_OF_RANGE ),
4132+ errmsg ("time out of range" )));
4133+
4134+ AdjustTimeForTypmod (& result , * typmod );
4135+
4136+ * typid = TIMEOID ;
4137+ return TimeADTGetDatum (result );
4138+ }
4139+ }
4140+ else
4141+ {
4142+ ereport (ERROR ,
4143+ (errcode (ERRCODE_INVALID_DATETIME_FORMAT ),
4144+ errmsg ("datetime format is not dated and not timed" )));
4145+ }
4146+
4147+ return (Datum ) 0 ;
4148+ }
4149+
38714150/*
38724151 * do_to_timestamp: shared code for to_timestamp and to_date
38734152 *
@@ -3883,7 +4162,8 @@ to_date(PG_FUNCTION_ARGS)
38834162 */
38844163static void
38854164do_to_timestamp (text * date_txt , text * fmt , bool std ,
3886- struct pg_tm * tm , fsec_t * fsec , int * fprec )
4165+ struct pg_tm * tm , fsec_t * fsec , int * fprec ,
4166+ uint32 * flags )
38874167{
38884168 FormatNode * format ;
38894169 TmFromChar tmfc ;
@@ -3940,6 +4220,9 @@ do_to_timestamp(text *date_txt, text *fmt, bool std,
39404220
39414221 pfree (fmt_str );
39424222
4223+ if (flags )
4224+ * flags = DCH_datetime_type (format );
4225+
39434226 if (!incache )
39444227 pfree (format );
39454228 }
0 commit comments