@@ -424,7 +424,10 @@ typedef struct
424
424
j ,
425
425
us ,
426
426
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 ;
428
431
}TmFromChar ;
429
432
430
433
#define ZERO_tmfc (_X ) memset(_X, 0, sizeof(TmFromChar))
@@ -470,6 +473,7 @@ do {\
470
473
(_X)->tm_sec = (_X)->tm_year = (_X)->tm_min = (_X)->tm_wday = \
471
474
(_X)->tm_hour = (_X)->tm_yday = (_X)->tm_isdst = 0; \
472
475
(_X)->tm_mday = (_X)->tm_mon = 1; \
476
+ (_X)->tm_zone = NULL; \
473
477
} while(0)
474
478
475
479
#define ZERO_tmtc (_X ) \
@@ -609,6 +613,8 @@ typedef enum
609
613
DCH_RM ,
610
614
DCH_SSSS ,
611
615
DCH_SS ,
616
+ DCH_TZH ,
617
+ DCH_TZM ,
612
618
DCH_TZ ,
613
619
DCH_US ,
614
620
DCH_WW ,
@@ -756,7 +762,9 @@ static const KeyWord DCH_keywords[] = {
756
762
{"RM" ,2 ,DCH_RM , false,FROM_CHAR_DATE_GREGORIAN },/* R */
757
763
{"SSSS" ,4 ,DCH_SSSS , true,FROM_CHAR_DATE_NONE },/* S */
758
764
{"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 },
760
768
{"US" ,2 ,DCH_US , true,FROM_CHAR_DATE_NONE },/* U */
761
769
{"WW" ,2 ,DCH_WW , true,FROM_CHAR_DATE_GREGORIAN },/* W */
762
770
{"W" ,1 ,DCH_W , true,FROM_CHAR_DATE_GREGORIAN },
@@ -879,7 +887,7 @@ static const int DCH_index[KeyWord_INDEX_SIZE] = {
879
887
-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,
880
888
-1 ,-1 ,-1 ,-1 ,-1 ,DCH_A_D ,DCH_B_C ,DCH_CC ,DCH_DAY ,-1 ,
881
889
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 ,
883
891
-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,-1 ,DCH_a_d ,DCH_b_c ,DCH_cc ,
884
892
DCH_day ,-1 ,DCH_fx ,-1 ,DCH_hh24 ,DCH_iddd ,DCH_j ,-1 ,-1 ,DCH_mi ,
885
893
-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
2519
2527
s += strlen (s );
2520
2528
}
2521
2529
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 ;
2522
2543
case DCH_OF :
2523
2544
INVALID_FOR_INTERVAL ;
2524
2545
sprintf (s ,"%c%0*d" ,
@@ -3070,6 +3091,20 @@ DCH_from_char(FormatNode *node, char *in, TmFromChar *out)
3070
3091
errmsg ("formatting field \"%s\" is only supported in to_char" ,
3071
3092
n -> key -> name )));
3072
3093
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 ;
3073
3108
case DCH_A_D :
3074
3109
case DCH_B_C :
3075
3110
case DCH_a_d :
@@ -3536,7 +3571,16 @@ to_timestamp(PG_FUNCTION_ARGS)
3536
3571
3537
3572
do_to_timestamp (date_txt ,fmt ,& tm ,& fsec );
3538
3573
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 );
3540
3584
3541
3585
if (tm2timestamp (& tm ,fsec ,& tz ,& result )!= 0 )
3542
3586
ereport (ERROR ,
@@ -3858,6 +3902,23 @@ do_to_timestamp(text *date_txt, text *fmt,
3858
3902
* fsec < INT64CONST (0 )|| * fsec >=USECS_PER_SEC )
3859
3903
DateTimeParseError (DTERR_FIELD_OVERFLOW ,date_str ,"timestamp" );
3860
3904
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
+
3861
3922
DEBUG_TM (tm );
3862
3923
3863
3924
pfree (date_str );