]> granicus.if.org Git - postgresql/commitdiff
Fix assorted breakage in to_char()'s OF format option.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 17 Mar 2016 19:50:33 +0000 (15:50 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 17 Mar 2016 19:50:33 +0000 (15:50 -0400)
In HEAD, fix incorrect field width for hours part of OF when tm_gmtoff is
negative.  This was introduced by commit 2d87eedc1d4468d3 as a result of
falsely applying a pattern that's correct when + signs are omitted, which
is not the case for OF.

In 9.4, fix missing abs() call that allowed a sign to be attached to the
minutes part of OF.  This was fixed in 9.5 by 9b43d73b3f9bef27, but for
inscrutable reasons not back-patched.

In all three versions, ensure that the sign of tm_gmtoff is correctly
reported even when the GMT offset is less than 1 hour.

Add regression tests, which evidently we desperately need here.

Thomas Munro and Tom Lane, per report from David Fetter

src/backend/utils/adt/formatting.c
src/test/regress/expected/timestamptz.out
src/test/regress/sql/timestamptz.sql

index 2b5622a9ee0d0078b0cbf32f39fefb12ee731bf9..c90f48d00a6dc8c6967e7af27a3abb6239c46ee3 100644 (file)
@@ -2506,12 +2506,15 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
                                break;
                        case DCH_OF:
                                INVALID_FOR_INTERVAL;
-                               sprintf(s, "%+0*d", S_FM(n->suffix) ? 0 : (tm->tm_gmtoff >= 0) ? 3 : 4,
-                                               (int) tm->tm_gmtoff / SECS_PER_HOUR);
+                               sprintf(s, "%c%0*d",
+                                               (tm->tm_gmtoff >= 0) ? '+' : '-',
+                                               S_FM(n->suffix) ? 0 : 2,
+                                               abs((int) tm->tm_gmtoff) / SECS_PER_HOUR);
                                s += strlen(s);
-                               if ((int) tm->tm_gmtoff % SECS_PER_HOUR != 0)
+                               if (abs((int) tm->tm_gmtoff) % SECS_PER_HOUR != 0)
                                {
-                                       sprintf(s, ":%02d", abs((int) tm->tm_gmtoff % SECS_PER_HOUR) / SECS_PER_MINUTE);
+                                       sprintf(s, ":%02d",
+                                                       (abs((int) tm->tm_gmtoff) % SECS_PER_HOUR) / SECS_PER_MINUTE);
                                        s += strlen(s);
                                }
                                break;
index fffcaf4bf8d7b7380bddd61b36a643b91d5aa2fb..271873d326de3240f6484653634f1836f2dda57a 100644 (file)
@@ -1699,6 +1699,57 @@ SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID')
             | 2001 1 1 1 1 1 1
 (66 rows)
 
+-- Check OF with various zone offsets, particularly fractional hours
+SET timezone = '00:00';
+SELECT to_char(now(), 'OF');
+ to_char 
+---------
+ +00
+(1 row)
+
+SET timezone = '+02:00';
+SELECT to_char(now(), 'OF');
+ to_char 
+---------
+ -02
+(1 row)
+
+SET timezone = '-13:00';
+SELECT to_char(now(), 'OF');
+ to_char 
+---------
+ +13
+(1 row)
+
+SET timezone = '-00:30';
+SELECT to_char(now(), 'OF');
+ to_char 
+---------
+ +00:30
+(1 row)
+
+SET timezone = '00:30';
+SELECT to_char(now(), 'OF');
+ to_char 
+---------
+ -00:30
+(1 row)
+
+SET timezone = '-04:30';
+SELECT to_char(now(), 'OF');
+ to_char 
+---------
+ +04:30
+(1 row)
+
+SET timezone = '04:30';
+SELECT to_char(now(), 'OF');
+ to_char 
+---------
+ -04:30
+(1 row)
+
+RESET timezone;
 CREATE TABLE TIMESTAMPTZ_TST (a int , b timestamptz);
 -- Test year field value with len > 4
 INSERT INTO TIMESTAMPTZ_TST VALUES(1, 'Sat Mar 12 23:58:48 1000 IST');
index 03dbc05aab7bfca7d25f6f18bdee8a7debe64e69..5ec92e55c8ff07cae29e717f23886b959be936d1 100644 (file)
@@ -248,6 +248,23 @@ SELECT '' AS to_char_10, to_char(d1, 'IYYY IYY IY I IW IDDD ID')
 SELECT '' AS to_char_11, to_char(d1, 'FMIYYY FMIYY FMIY FMI FMIW FMIDDD FMID')
    FROM TIMESTAMPTZ_TBL;
 
+-- Check OF with various zone offsets, particularly fractional hours
+SET timezone = '00:00';
+SELECT to_char(now(), 'OF');
+SET timezone = '+02:00';
+SELECT to_char(now(), 'OF');
+SET timezone = '-13:00';
+SELECT to_char(now(), 'OF');
+SET timezone = '-00:30';
+SELECT to_char(now(), 'OF');
+SET timezone = '00:30';
+SELECT to_char(now(), 'OF');
+SET timezone = '-04:30';
+SELECT to_char(now(), 'OF');
+SET timezone = '04:30';
+SELECT to_char(now(), 'OF');
+RESET timezone;
+
 CREATE TABLE TIMESTAMPTZ_TST (a int , b timestamptz);
 
 -- Test year field value with len > 4