Fix assorted breakage in to_char()'s OF format option.
authorTom Lane
Thu, 17 Mar 2016 19:50:33 +0000 (15:50 -0400)
committerTom Lane
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 5391ea0bf0ba83b27f2d33ddfe7761cb323a009b..0c72d6d2451b88c8a9de19c36a9fcca7b56c872c 100644 (file)
@@ -2503,11 +2503,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 : 3, (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 e5a21305c006cb675c48732199e1e4363c99832d..50661bb5929990953f35dcbe5fa7925e19783952 100644 (file)
@@ -1675,6 +1675,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 f4b455e7032e8682e5905fa1e8976d6cbfcf5a95..5e52f5f0c15fd4b12b20ca3e7c1039b2c398655e 100644 (file)
@@ -241,6 +241,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