@@ -992,6 +992,11 @@ typedef struct NUMProc
992
992
* L_currency_symbol ;
993
993
} NUMProc ;
994
994
995
+ /* Return flags for DCH_from_char() */
996
+ #define DCH_DATED 0x01
997
+ #define DCH_TIMED 0x02
998
+ #define DCH_ZONED 0x04
999
+
995
1000
/* ----------
996
1001
* Functions
997
1002
* ----------
@@ -1025,7 +1030,8 @@ static int from_char_parse_int(int *dest, char **src, FormatNode *node);
1025
1030
static int seq_search (char * name , const char * const * array , int type , int max , int * len );
1026
1031
static int from_char_seq_search (int * dest , char * * src , const char * const * array , int type , int max , FormatNode * node );
1027
1032
static 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 );
1029
1035
static char * fill_str (char * str , int c , int max );
1030
1036
static FormatNode * NUM_cache (int len , NUMDesc * Num , text * pars_str , bool * shouldFree );
1031
1037
static char * int_to_roman (int number );
@@ -3517,6 +3523,109 @@ DCH_prevent_counter_overflow(void)
3517
3523
}
3518
3524
}
3519
3525
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
+
3520
3629
/* select a DCHCacheEntry to hold the given format picture */
3521
3630
static DCHCacheEntry *
3522
3631
DCH_cache_getnew (const char * str , bool std )
@@ -3808,7 +3917,7 @@ to_timestamp(PG_FUNCTION_ARGS)
3808
3917
fsec_t fsec ;
3809
3918
int fprec ;
3810
3919
3811
- do_to_timestamp (date_txt , fmt , false, & tm , & fsec , & fprec );
3920
+ do_to_timestamp (date_txt , fmt , false, & tm , & fsec , & fprec , NULL );
3812
3921
3813
3922
/* Use the specified time zone, if any. */
3814
3923
if (tm .tm_zone )
@@ -3847,7 +3956,7 @@ to_date(PG_FUNCTION_ARGS)
3847
3956
struct pg_tm tm ;
3848
3957
fsec_t fsec ;
3849
3958
3850
- do_to_timestamp (date_txt , fmt , false, & tm , & fsec , NULL );
3959
+ do_to_timestamp (date_txt , fmt , false, & tm , & fsec , NULL , NULL );
3851
3960
3852
3961
/* Prevent overflow in Julian-day routines */
3853
3962
if (!IS_VALID_JULIAN (tm .tm_year , tm .tm_mon , tm .tm_mday ))
@@ -3868,6 +3977,176 @@ to_date(PG_FUNCTION_ARGS)
3868
3977
PG_RETURN_DATEADT (result );
3869
3978
}
3870
3979
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
+
3871
4150
/*
3872
4151
* do_to_timestamp: shared code for to_timestamp and to_date
3873
4152
*
@@ -3883,7 +4162,8 @@ to_date(PG_FUNCTION_ARGS)
3883
4162
*/
3884
4163
static void
3885
4164
do_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 )
3887
4167
{
3888
4168
FormatNode * format ;
3889
4169
TmFromChar tmfc ;
@@ -3940,6 +4220,9 @@ do_to_timestamp(text *date_txt, text *fmt, bool std,
3940
4220
3941
4221
pfree (fmt_str );
3942
4222
4223
+ if (flags )
4224
+ * flags = DCH_datetime_type (format );
4225
+
3943
4226
if (!incache )
3944
4227
pfree (format );
3945
4228
}
0 commit comments