Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit ac78c41

Browse files
committed
Fix to_char(), to_date(), and to_timestamp() to handle negative/BC
century specifications just like positive/AD centuries. Previously the behavior was either wrong or inconsistent with positive/AD handling. Centuries without years now always assume the first year of the century, which is now documented.
1 parent fbcfa90 commit ac78c41

File tree

4 files changed

+39
-22
lines changed

4 files changed

+39
-22
lines changed

doc/src/sgml/func.sgml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5575,7 +5575,9 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
55755575
if there is a <literal>YYY</literal>, <literal>YYYY</literal> or
55765576
<literal>Y,YYY</literal> field. If <literal>CC</literal> is used with
55775577
<literal>YY</literal> or <literal>Y</literal> then the year is computed
5578-
as <literal>(CC-1)*100+YY</literal>.
5578+
as the year in the specified century. If the century is
5579+
specified but the year is not, the first year of the century
5580+
is assumed.
55795581
</para>
55805582
</listitem>
55815583

src/backend/utils/adt/formatting.c

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2640,8 +2640,15 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid col
26402640
case DCH_CC:
26412641
if (is_interval) /* straight calculation */
26422642
i = tm->tm_year / 100;
2643-
else /* century 21 starts in 2001 */
2644-
i = (tm->tm_year - 1) / 100 + 1;
2643+
else
2644+
{
2645+
if (tm->tm_year > 0)
2646+
/* Century 20 == 1901 - 2000 */
2647+
i = (tm->tm_year - 1) / 100 + 1;
2648+
else
2649+
/* Century 6BC == 600BC - 501BC */
2650+
i = tm->tm_year / 100 - 1;
2651+
}
26452652
if (i <= 99 && i >= -99)
26462653
sprintf(s, "%0*d", S_FM(n->suffix) ? 0 : 2, i);
26472654
else
@@ -3465,33 +3472,41 @@ do_to_timestamp(text *date_txt, text *fmt,
34653472
/*
34663473
* If CC and YY (or Y) are provided, use YY as 2 low-order digits for
34673474
* the year in the given century. Keep in mind that the 21st century
3468-
* runs from 2001-2100, not 2000-2099.
3469-
*
3470-
* If a 4-digit year is provided, we use that and ignore CC.
3475+
* AD runs from 2001-2100, not 2000-2099; 6th century BC runs from
3476+
* 600BC to 501BC.
34713477
*/
34723478
if (tmfc.cc && tmfc.yysz <= 2)
34733479
{
3480+
if (tmfc.bc)
3481+
tmfc.cc = -tmfc.cc;
34743482
tm->tm_year = tmfc.year % 100;
34753483
if (tm->tm_year)
3476-
tm->tm_year += (tmfc.cc - 1) * 100;
3484+
{
3485+
if (tmfc.cc >= 0)
3486+
tm->tm_year += (tmfc.cc - 1) * 100;
3487+
else
3488+
tm->tm_year = (tmfc.cc + 1) * 100 - tm->tm_year + 1;
3489+
}
34773490
else
3478-
tm->tm_year = tmfc.cc * 100;
3491+
/* find century year for dates ending in "00" */
3492+
tm->tm_year = tmfc.cc * 100 + ((tmfc.cc >= 0) ? 0 : 1);
34793493
}
34803494
else
3495+
/* If a 4-digit year is provided, we use that and ignore CC. */
3496+
{
34813497
tm->tm_year = tmfc.year;
3498+
if (tmfc.bc && tm->tm_year > 0)
3499+
tm->tm_year = -(tm->tm_year - 1);
3500+
}
34823501
}
3483-
else if (tmfc.cc) /* use first year of century */
3484-
tm->tm_year = (tmfc.cc - 1) * 100 + 1;
3485-
3486-
if (tmfc.bc)
3502+
else if (tmfc.cc) /* use first year of century */
34873503
{
3488-
if (tm->tm_year > 0)
3489-
tm->tm_year = -(tm->tm_year - 1);
3504+
if (tmfc.bc)
3505+
tmfc.cc = -tmfc.cc;
3506+
if (tmfc.cc >= 0)
3507+
tm->tm_year = (tmfc.cc - 1) * 100 + 1;
34903508
else
3491-
ereport(ERROR,
3492-
(errcode(ERRCODE_INVALID_DATETIME_FORMAT),
3493-
errmsg("inconsistent use of year %04d and \"BC\"",
3494-
tm->tm_year)));
3509+
tm->tm_year = tmfc.cc * 100 + 1;
34953510
}
34963511

34973512
if (tmfc.j)

src/test/regress/expected/timestamp.out

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,7 @@ SELECT '' AS to_char_3, to_char(d1, 'Y,YYY YYYY YYY YY Y CC Q MM WW DDD DD D J')
992992
| 1,997 1997 997 97 7 20 1 02 07 045 14 6 2450494
993993
| 1,997 1997 997 97 7 20 1 02 07 046 15 7 2450495
994994
| 1,997 1997 997 97 7 20 1 02 07 047 16 1 2450496
995-
| 0,097 0097 097 97 7 01 1 02 07 047 16 3 1686042
995+
| 0,097 0097 097 97 7 -1 1 02 07 047 16 3 1686042
996996
| 0,097 0097 097 97 7 01 1 02 07 047 16 7 1756536
997997
| 0,597 0597 597 97 7 06 1 02 07 047 16 5 1939157
998998
| 1,097 1097 097 97 7 11 1 02 07 047 16 3 2121778
@@ -1063,7 +1063,7 @@ SELECT '' AS to_char_4, to_char(d1, 'FMY,YYY FMYYYY FMYYY FMYY FMY FMCC FMQ FMMM
10631063
| 1,997 1997 997 97 7 20 1 2 7 45 14 6 2450494
10641064
| 1,997 1997 997 97 7 20 1 2 7 46 15 7 2450495
10651065
| 1,997 1997 997 97 7 20 1 2 7 47 16 1 2450496
1066-
| 0,097 97 97 97 7 1 1 2 7 47 16 3 1686042
1066+
| 0,097 97 97 97 7 -1 1 2 7 47 16 3 1686042
10671067
| 0,097 97 97 97 7 1 1 2 7 47 16 7 1756536
10681068
| 0,597 597 597 97 7 6 1 2 7 47 16 5 1939157
10691069
| 1,097 1097 97 97 7 11 1 2 7 47 16 3 2121778

src/test/regress/expected/timestamptz.out

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,7 +1074,7 @@ SELECT '' AS to_char_3, to_char(d1, 'Y,YYY YYYY YYY YY Y CC Q MM WW DDD DD D J')
10741074
| 1,997 1997 997 97 7 20 1 02 07 045 14 6 2450494
10751075
| 1,997 1997 997 97 7 20 1 02 07 046 15 7 2450495
10761076
| 1,997 1997 997 97 7 20 1 02 07 047 16 1 2450496
1077-
| 0,097 0097 097 97 7 01 1 02 07 047 16 3 1686042
1077+
| 0,097 0097 097 97 7 -1 1 02 07 047 16 3 1686042
10781078
| 0,097 0097 097 97 7 01 1 02 07 047 16 7 1756536
10791079
| 0,597 0597 597 97 7 06 1 02 07 047 16 5 1939157
10801080
| 1,097 1097 097 97 7 11 1 02 07 047 16 3 2121778
@@ -1146,7 +1146,7 @@ SELECT '' AS to_char_4, to_char(d1, 'FMY,YYY FMYYYY FMYYY FMYY FMY FMCC FMQ FMMM
11461146
| 1,997 1997 997 97 7 20 1 2 7 45 14 6 2450494
11471147
| 1,997 1997 997 97 7 20 1 2 7 46 15 7 2450495
11481148
| 1,997 1997 997 97 7 20 1 2 7 47 16 1 2450496
1149-
| 0,097 97 97 97 7 1 1 2 7 47 16 3 1686042
1149+
| 0,097 97 97 97 7 -1 1 2 7 47 16 3 1686042
11501150
| 0,097 97 97 97 7 1 1 2 7 47 16 7 1756536
11511151
| 0,597 597 597 97 7 6 1 2 7 47 16 5 1939157
11521152
| 1,097 1097 97 97 7 11 1 2 7 47 16 3 2121778

0 commit comments

Comments
 (0)