@@ -424,7 +424,10 @@ typedef struct
424424 j ,
425425 us ,
426426 yysz , /* is it YY or YYYY ? */
427- clock ; /* 12 or 24 hour clock? */
427+ clock , /* 12 or 24 hour clock? */
428+ tzsign , /* +1, -1 or 0 if timezone info is absent */
429+ tzh ,
430+ tzm ;
428431} TmFromChar ;
429432
430433#define ZERO_tmfc (_X ) memset(_X, 0, sizeof(TmFromChar))
@@ -470,6 +473,7 @@ do { \
470473 (_X)->tm_sec = (_X)->tm_year = (_X)->tm_min = (_X)->tm_wday = \
471474 (_X)->tm_hour = (_X)->tm_yday = (_X)->tm_isdst = 0; \
472475 (_X)->tm_mday = (_X)->tm_mon = 1; \
476+ (_X)->tm_zone = NULL; \
473477} while(0)
474478
475479#define ZERO_tmtc (_X ) \
@@ -609,6 +613,8 @@ typedef enum
609613 DCH_RM ,
610614 DCH_SSSS ,
611615 DCH_SS ,
616+ DCH_TZH ,
617+ DCH_TZM ,
612618 DCH_TZ ,
613619 DCH_US ,
614620 DCH_WW ,
@@ -756,7 +762,9 @@ static const KeyWord DCH_keywords[] = {
756762 {"RM" , 2 , DCH_RM , false, FROM_CHAR_DATE_GREGORIAN }, /* R */
757763 {"SSSS" , 4 , DCH_SSSS , true, FROM_CHAR_DATE_NONE }, /* S */
758764 {"SS" , 2 , DCH_SS , true, FROM_CHAR_DATE_NONE },
759- {"TZ" , 2 , DCH_TZ , false, FROM_CHAR_DATE_NONE }, /* T */
765+ {"TZH" , 3 , DCH_TZH , false, FROM_CHAR_DATE_NONE }, /* T */
766+ {"TZM" , 3 , DCH_TZM , true, FROM_CHAR_DATE_NONE },
767+ {"TZ" , 2 , DCH_TZ , false, FROM_CHAR_DATE_NONE },
760768 {"US" , 2 , DCH_US , true, FROM_CHAR_DATE_NONE }, /* U */
761769 {"WW" , 2 , DCH_WW , true, FROM_CHAR_DATE_GREGORIAN }, /* W */
762770 {"W" , 1 , DCH_W , true, FROM_CHAR_DATE_GREGORIAN },
@@ -879,7 +887,7 @@ static const int DCH_index[KeyWord_INDEX_SIZE] = {
879887 -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 ,
880888 -1 , -1 , -1 , -1 , -1 , DCH_A_D , DCH_B_C , DCH_CC , DCH_DAY , -1 ,
881889 DCH_FX , -1 , DCH_HH24 , DCH_IDDD , DCH_J , -1 , -1 , DCH_MI , -1 , DCH_OF ,
882- DCH_P_M , DCH_Q , DCH_RM , DCH_SSSS , DCH_TZ , DCH_US , -1 , DCH_WW , -1 , DCH_Y_YYY ,
890+ DCH_P_M , DCH_Q , DCH_RM , DCH_SSSS , DCH_TZH , DCH_US , -1 , DCH_WW , -1 , DCH_Y_YYY ,
883891 -1 , -1 , -1 , -1 , -1 , -1 , -1 , DCH_a_d , DCH_b_c , DCH_cc ,
884892 DCH_day , -1 , DCH_fx , -1 , DCH_hh24 , DCH_iddd , DCH_j , -1 , -1 , DCH_mi ,
885893 -1 , -1 , DCH_p_m , DCH_q , DCH_rm , DCH_ssss , DCH_tz , DCH_us , -1 , DCH_ww ,
@@ -2519,6 +2527,19 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
25192527 s += strlen (s );
25202528 }
25212529 break ;
2530+ case DCH_TZH :
2531+ INVALID_FOR_INTERVAL ;
2532+ sprintf (s , "%c%02d" ,
2533+ (tm -> tm_gmtoff >= 0 ) ? '+' : '-' ,
2534+ abs ((int ) tm -> tm_gmtoff ) / SECS_PER_HOUR );
2535+ s += strlen (s );
2536+ break ;
2537+ case DCH_TZM :
2538+ INVALID_FOR_INTERVAL ;
2539+ sprintf (s , "%02d" ,
2540+ (abs ((int ) tm -> tm_gmtoff ) % SECS_PER_HOUR ) / SECS_PER_MINUTE );
2541+ s += strlen (s );
2542+ break ;
25222543 case DCH_OF :
25232544 INVALID_FOR_INTERVAL ;
25242545 sprintf (s , "%c%0*d" ,
@@ -3070,6 +3091,20 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
30703091 errmsg ("formatting field \"%s\" is only supported in to_char" ,
30713092 n -> key -> name )));
30723093 break ;
3094+ case DCH_TZH :
3095+ out -> tzsign = * s == '-' ? -1 : +1 ;
3096+
3097+ if (* s == '+' || * s == '-' || * s == ' ' )
3098+ s ++ ;
3099+
3100+ from_char_parse_int_len (& out -> tzh , & s , 2 , n );
3101+ break ;
3102+ case DCH_TZM :
3103+ /* assign positive timezone sign if TZH was not seen before */
3104+ if (!out -> tzsign )
3105+ out -> tzsign = +1 ;
3106+ from_char_parse_int_len (& out -> tzm , & s , 2 , n );
3107+ break ;
30733108 case DCH_A_D :
30743109 case DCH_B_C :
30753110 case DCH_a_d :
@@ -3536,7 +3571,16 @@ to_timestamp(PG_FUNCTION_ARGS)
35363571
35373572 do_to_timestamp (date_txt , fmt , & tm , & fsec );
35383573
3539- tz = DetermineTimeZoneOffset (& tm , session_timezone );
3574+ /* Use the specified time zone, if any. */
3575+ if (tm .tm_zone )
3576+ {
3577+ int dterr = DecodeTimezone ((char * ) tm .tm_zone , & tz );
3578+
3579+ if (dterr )
3580+ DateTimeParseError (dterr , text_to_cstring (date_txt ), "timestamptz" );
3581+ }
3582+ else
3583+ tz = DetermineTimeZoneOffset (& tm , session_timezone );
35403584
35413585 if (tm2timestamp (& tm , fsec , & tz , & result ) != 0 )
35423586 ereport (ERROR ,
@@ -3858,6 +3902,23 @@ do_to_timestamp(text *date_txt, text *fmt,
38583902 * fsec < INT64CONST (0 ) || * fsec >= USECS_PER_SEC )
38593903 DateTimeParseError (DTERR_FIELD_OVERFLOW , date_str , "timestamp" );
38603904
3905+ /* Save parsed time-zone into tm->tm_zone if it was specified */
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+
38613922 DEBUG_TM (tm );
38623923
38633924 pfree (date_str );
0 commit comments