@@ -426,7 +426,10 @@ typedef struct
426426 j ,
427427 us ,
428428 yysz , /* is it YY or YYYY ? */
429- clock ; /* 12 or 24 hour clock? */
429+ clock , /* 12 or 24 hour clock? */
430+ tzsign ,
431+ tzh ,
432+ tzm ;
430433} TmFromChar ;
431434
432435#define ZERO_tmfc (_X ) memset(_X, 0, sizeof(TmFromChar))
@@ -472,6 +475,7 @@ do { \
472475 (_X)->tm_sec = (_X)->tm_year = (_X)->tm_min = (_X)->tm_wday = \
473476 (_X)->tm_hour = (_X)->tm_yday = (_X)->tm_isdst = 0; \
474477 (_X)->tm_mday = (_X)->tm_mon = 1; \
478+ (_X)->tm_zone = NULL; \
475479} while(0)
476480
477481#define ZERO_tmtc (_X ) \
@@ -611,6 +615,8 @@ typedef enum
611615 DCH_RM ,
612616 DCH_SSSS ,
613617 DCH_SS ,
618+ DCH_TZH ,
619+ DCH_TZM ,
614620 DCH_TZ ,
615621 DCH_US ,
616622 DCH_WW ,
@@ -758,7 +764,9 @@ static const KeyWord DCH_keywords[] = {
758764 {"RM" , 2 , DCH_RM , FALSE, FROM_CHAR_DATE_GREGORIAN }, /* R */
759765 {"SSSS" , 4 , DCH_SSSS , TRUE, FROM_CHAR_DATE_NONE }, /* S */
760766 {"SS" , 2 , DCH_SS , TRUE, FROM_CHAR_DATE_NONE },
761- {"TZ" , 2 , DCH_TZ , FALSE, FROM_CHAR_DATE_NONE }, /* T */
767+ {"TZH" , 3 , DCH_TZH , FALSE, FROM_CHAR_DATE_NONE }, /* T */
768+ {"TZM" , 3 , DCH_TZM , TRUE, FROM_CHAR_DATE_NONE },
769+ {"TZ" , 2 , DCH_TZ , FALSE, FROM_CHAR_DATE_NONE },
762770 {"US" , 2 , DCH_US , TRUE, FROM_CHAR_DATE_NONE }, /* U */
763771 {"WW" , 2 , DCH_WW , TRUE, FROM_CHAR_DATE_GREGORIAN }, /* W */
764772 {"W" , 1 , DCH_W , TRUE, FROM_CHAR_DATE_GREGORIAN },
@@ -881,7 +889,7 @@ static const int DCH_index[KeyWord_INDEX_SIZE] = {
881889 -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
882890 -1 , -1 , -1 , -1 , -1 , DCH_A_D , DCH_B_C , DCH_CC , DCH_DAY , -1 ,
883891 DCH_FX , -1 , DCH_HH24 , DCH_IDDD , DCH_J , -1 , -1 , DCH_MI , -1 , DCH_OF ,
884- DCH_P_M , DCH_Q , DCH_RM , DCH_SSSS , DCH_TZ , DCH_US , -1 , DCH_WW , -1 , DCH_Y_YYY ,
892+ DCH_P_M , DCH_Q , DCH_RM , DCH_SSSS , DCH_TZH , DCH_US , -1 , DCH_WW , -1 , DCH_Y_YYY ,
885893 -1 , -1 , -1 , -1 , -1 , -1 , -1 , DCH_a_d , DCH_b_c , DCH_cc ,
886894 DCH_day , -1 , DCH_fx , -1 , DCH_hh24 , DCH_iddd , DCH_j , -1 , -1 , DCH_mi ,
887895 -1 , -1 , DCH_p_m , DCH_q , DCH_rm , DCH_ssss , DCH_tz , DCH_us , -1 , DCH_ww ,
@@ -2529,6 +2537,13 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
25292537 s += strlen (s );
25302538 }
25312539 break ;
2540+ case DCH_TZH :
2541+ case DCH_TZM :
2542+ ereport (ERROR ,
2543+ (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
2544+ errmsg ("formatting field \"%s\" is only supported in "
2545+ "to_timestamp" , n -> key -> name )));
2546+ break ;
25322547 case DCH_OF :
25332548 INVALID_FOR_INTERVAL ;
25342549 sprintf (s , "%c%0*d" ,
@@ -3080,6 +3095,19 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30803095 errmsg ("formatting field \"%s\" is only supported in to_char" ,
30813096 n -> key -> name )));
30823097 break ;
3098+ case DCH_TZH :
3099+ out -> tzsign = * s == '-' ? -1 : +1 ;
3100+
3101+ if (* s == '+' || * s == '-' || * s == ' ' )
3102+ s ++ ;
3103+
3104+ from_char_parse_int_len (& out -> tzh , & s , 2 , n );
3105+ break ;
3106+ case DCH_TZM :
3107+ if (!out -> tzsign )
3108+ out -> tzsign = +1 ;
3109+ from_char_parse_int_len (& out -> tzm , & s , 2 , n );
3110+ break ;
30833111 case DCH_A_D :
30843112 case DCH_B_C :
30853113 case DCH_a_d :
@@ -3545,8 +3573,15 @@ to_timestamp(PG_FUNCTION_ARGS)
35453573 fsec_t fsec ;
35463574
35473575 do_to_timestamp (date_txt , fmt , & tm , & fsec );
3576+ if (tm .tm_zone )
3577+ {
3578+ int dterr = DecodeTimezone ((char * ) tm .tm_zone , & tz );
35483579
3549- tz = DetermineTimeZoneOffset (& tm , session_timezone );
3580+ if (dterr )
3581+ DateTimeParseError (dterr , text_to_cstring (date_txt ), "timestamptz" );
3582+ }
3583+ else
3584+ tz = DetermineTimeZoneOffset (& tm , session_timezone );
35503585
35513586 if (tm2timestamp (& tm , fsec , & tz , & result ) != 0 )
35523587 ereport (ERROR ,
@@ -3868,6 +3903,22 @@ do_to_timestamp(text *date_txt, text *fmt,
38683903 * fsec < INT64CONST (0 ) || * fsec >= USECS_PER_SEC )
38693904 DateTimeParseError (DTERR_FIELD_OVERFLOW , date_str , "timestamp" );
38703905
3906+ if (tmfc .tzsign )
3907+ {
3908+ char * tz ;
3909+
3910+ if (tmfc .tzh < 0 || tmfc .tzh > MAX_TZDISP_HOUR ||
3911+ tmfc .tzm < 0 || tmfc .tzm >= MINS_PER_HOUR )
3912+ DateTimeParseError (DTERR_TZDISP_OVERFLOW , date_str , "timestamp" );
3913+
3914+ tz = palloc (7 );
3915+
3916+ snprintf (tz , 7 , "%c%02d:%02d" ,
3917+ tmfc .tzsign > 0 ? '+' : '-' , tmfc .tzh , tmfc .tzm );
3918+
3919+ tm -> tm_zone = tz ;
3920+ }
3921+
38713922 DEBUG_TM (tm );
38723923
38733924 pfree (date_str );
0 commit comments