diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 3f6d431523062c9fa93672387e7242257e09f83c..a38bbda9af1e5d0e0e5e0c30ed3499a899ea2ffa 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.109 2004/06/03 17:57:09 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.110 2004/08/20 03:45:13 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -2727,11 +2727,26 @@ timestamp_trunc(PG_FUNCTION_ARGS) fsec = 0; break; case DTK_MILLENNIUM: - tm->tm_year = (tm->tm_year / 1000) * 1000; + /* see comments in timestamptz_trunc */ + if (tm->tm_year > 0) + tm->tm_year = ((tm->tm_year+999) / 1000) * 1000 - 999; + else + tm->tm_year = - ((999 - (tm->tm_year-1))/1000) * 1000 + 1; case DTK_CENTURY: - tm->tm_year = (tm->tm_year / 100) * 100; + /* see comments in timestamptz_trunc */ + if (tm->tm_year > 0) + tm->tm_year = ((tm->tm_year+99) / 100) * 100 - 99; + else + tm->tm_year = - ((99 - (tm->tm_year-1))/100) * 100 + 1; case DTK_DECADE: - tm->tm_year = (tm->tm_year / 10) * 10; + /* see comments in timestamptz_trunc */ + if (val != DTK_MILLENNIUM && val != DTK_CENTURY) + { + if (tm->tm_year > 0) + tm->tm_year = (tm->tm_year / 10) * 10; + else + tm->tm_year = - ((8-(tm->tm_year-1)) / 10) * 10; + } case DTK_YEAR: tm->tm_mon = 1; case DTK_QUARTER: @@ -2830,12 +2845,33 @@ timestamptz_trunc(PG_FUNCTION_ARGS) tm->tm_sec = 0; fsec = 0; break; + /* one may consider DTK_THOUSAND and DTK_HUNDRED... */ case DTK_MILLENNIUM: - tm->tm_year = (tm->tm_year / 1000) * 1000; + /* truncating to the millennium? what is this supposed to mean? + * let us put the first year of the millennium... + * i.e. -1000, 1, 1001, 2001... + */ + if (tm->tm_year > 0) + tm->tm_year = ((tm->tm_year+999) / 1000) * 1000 - 999; + else + tm->tm_year = - ((999 - (tm->tm_year-1))/1000) * 1000 + 1; case DTK_CENTURY: - tm->tm_year = (tm->tm_year / 100) * 100; + /* truncating to the century? as above: -100, 1, 101... */ + if (tm->tm_year > 0) + tm->tm_year = ((tm->tm_year+99) / 100) * 100 - 99 ; + else + tm->tm_year = - ((99 - (tm->tm_year-1))/100) * 100 + 1; case DTK_DECADE: - tm->tm_year = (tm->tm_year / 10) * 10; + /* truncating to the decade? first year of the decade. + * must not be applied if year was truncated before! + */ + if (val != DTK_MILLENNIUM && val != DTK_CENTURY) + { + if (tm->tm_year > 0) + tm->tm_year = (tm->tm_year / 10) * 10; + else + tm->tm_year = - ((8-(tm->tm_year-1)) / 10) * 10; + } case DTK_YEAR: tm->tm_mon = 1; case DTK_QUARTER: @@ -2923,10 +2959,13 @@ interval_trunc(PG_FUNCTION_ARGS) switch (val) { case DTK_MILLENNIUM: + /* caution: C division may have negative remainder */ tm->tm_year = (tm->tm_year / 1000) * 1000; case DTK_CENTURY: + /* caution: C division may have negative remainder */ tm->tm_year = (tm->tm_year / 100) * 100; case DTK_DECADE: + /* caution: C division may have negative remainder */ tm->tm_year = (tm->tm_year / 10) * 10; case DTK_YEAR: tm->tm_mon = 0; @@ -3221,7 +3260,14 @@ timestamp_part(PG_FUNCTION_ARGS) break; case DTK_DECADE: - result = (tm->tm_year / 10); + /* what is a decade wrt dates? + * let us assume that decade 199 is 1990 thru 1999... + * decade 0 starts on year 1 BC, and -1 is 11 BC thru 2 BC... + */ + if (tm->tm_year>=0) + result = (tm->tm_year / 10); + else + result = -((8-(tm->tm_year-1)) / 10); break; case DTK_CENTURY: @@ -3232,7 +3278,7 @@ timestamp_part(PG_FUNCTION_ARGS) if (tm->tm_year > 0) result = ((tm->tm_year+99) / 100); else - /* caution: C division may yave negative remainder */ + /* caution: C division may have negative remainder */ result = - ((99 - (tm->tm_year-1))/100); break; @@ -3445,15 +3491,27 @@ timestamptz_part(PG_FUNCTION_ARGS) break; case DTK_DECADE: - result = (tm->tm_year / 10); + /* see comments in timestamp_part */ + if (tm->tm_year>0) + result = (tm->tm_year / 10); + else + result = - ((8-(tm->tm_year-1)) / 10); break; case DTK_CENTURY: - result = (tm->tm_year / 100); + /* see comments in timestamp_part */ + if (tm->tm_year > 0) + result = ((tm->tm_year+99) / 100); + else + result = - ((99 - (tm->tm_year-1))/100); break; case DTK_MILLENNIUM: - result = (tm->tm_year / 1000); + /* see comments in timestamp_part */ + if (tm->tm_year > 0) + result = ((tm->tm_year+999) / 1000); + else + result = - ((999 - (tm->tm_year-1))/1000); break; case DTK_JULIAN: @@ -3606,14 +3664,17 @@ interval_part(PG_FUNCTION_ARGS) break; case DTK_DECADE: + /* caution: C division may have negative remainder */ result = (tm->tm_year / 10); break; case DTK_CENTURY: + /* caution: C division may have negative remainder */ result = (tm->tm_year / 100); break; case DTK_MILLENNIUM: + /* caution: C division may have negative remainder */ result = (tm->tm_year / 1000); break; diff --git a/src/test/regress/expected/date.out b/src/test/regress/expected/date.out index 77bbdb967624ef745babdb83f95250fed8822dd2..24018de56060e165f5796d6946eb7a8d63c9d99f 100644 --- a/src/test/regress/expected/date.out +++ b/src/test/regress/expected/date.out @@ -930,3 +930,152 @@ SELECT EXTRACT(MILLENNIUM FROM CURRENT_DATE); -- 3 3 (1 row) +-- +-- decade +-- +SELECT EXTRACT(DECADE FROM DATE '1994-12-25'); -- 199 + date_part +----------- + 199 +(1 row) + +SELECT EXTRACT(DECADE FROM DATE '0010-01-01'); -- 1 + date_part +----------- + 1 +(1 row) + +SELECT EXTRACT(DECADE FROM DATE '0009-12-31'); -- 0 + date_part +----------- + 0 +(1 row) + +SELECT EXTRACT(DECADE FROM DATE '0001-01-01 BC'); -- 0 + date_part +----------- + 0 +(1 row) + +SELECT EXTRACT(DECADE FROM DATE '0002-12-31 BC'); -- -1 + date_part +----------- + -1 +(1 row) + +SELECT EXTRACT(DECADE FROM DATE '0011-01-01 BC'); -- -1 + date_part +----------- + -1 +(1 row) + +SELECT EXTRACT(DECADE FROM DATE '0012-12-31 BC'); -- -2 + date_part +----------- + -2 +(1 row) + +-- +-- some other types: +-- +-- on a timestamp. +SELECT EXTRACT(CENTURY FROM NOW())>=21 AS True; -- true + true +------ + t +(1 row) + +SELECT EXTRACT(CENTURY FROM TIMESTAMP '1970-03-20 04:30:00.00000'); -- 20 + date_part +----------- + 20 +(1 row) + +-- on an interval +SELECT EXTRACT(CENTURY FROM INTERVAL '100 y'); -- 1 + date_part +----------- + 1 +(1 row) + +SELECT EXTRACT(CENTURY FROM INTERVAL '99 y'); -- 0 + date_part +----------- + 0 +(1 row) + +SELECT EXTRACT(CENTURY FROM INTERVAL '-99 y'); -- 0 + date_part +----------- + 0 +(1 row) + +SELECT EXTRACT(CENTURY FROM INTERVAL '-100 y'); -- -1 + date_part +----------- + -1 +(1 row) + +-- +-- test trunc function! +-- +SELECT DATE_TRUNC('MILLENNIUM', TIMESTAMP '1970-03-20 04:30:00.00000'); -- 1001 + date_trunc +-------------------------- + Thu Jan 01 00:00:00 1001 +(1 row) + +SELECT DATE_TRUNC('MILLENNIUM', DATE '1970-03-20'); -- 1001-01-01 + date_trunc +------------------------------ + Thu Jan 01 00:00:00 1001 PST +(1 row) + +SELECT DATE_TRUNC('CENTURY', TIMESTAMP '1970-03-20 04:30:00.00000'); -- 1901 + date_trunc +-------------------------- + Tue Jan 01 00:00:00 1901 +(1 row) + +SELECT DATE_TRUNC('CENTURY', DATE '1970-03-20'); -- 1901 + date_trunc +------------------------------ + Tue Jan 01 00:00:00 1901 PST +(1 row) + +SELECT DATE_TRUNC('CENTURY', DATE '2004-08-10'); -- 2001-01-01 + date_trunc +------------------------------ + Mon Jan 01 00:00:00 2001 PST +(1 row) + +SELECT DATE_TRUNC('CENTURY', DATE '0002-02-04'); -- 0001-01-01 + date_trunc +------------------------------ + Mon Jan 01 00:00:00 0001 PST +(1 row) + +SELECT DATE_TRUNC('CENTURY', DATE '0055-08-10 BC'); -- 0100-01-01 BC + date_trunc +--------------------------------- + Tue Jan 01 00:00:00 0100 PST BC +(1 row) + +SELECT DATE_TRUNC('DECADE', DATE '1993-12-25'); -- 1990-01-01 + date_trunc +------------------------------ + Mon Jan 01 00:00:00 1990 PST +(1 row) + +SELECT DATE_TRUNC('DECADE', DATE '0004-12-25'); -- 0001-01-01 BC + date_trunc +--------------------------------- + Sat Jan 01 00:00:00 0001 PST BC +(1 row) + +SELECT DATE_TRUNC('DECADE', DATE '0002-12-31 BC'); -- 0011-01-01 BC + date_trunc +--------------------------------- + Mon Jan 01 00:00:00 0011 PST BC +(1 row) + diff --git a/src/test/regress/sql/date.sql b/src/test/regress/sql/date.sql index a3cad66ed2e16d95f8ee41be25ff8fa383d8e8e6..97ddbe9e45aca8f398a08895cb14f624107cafa4 100644 --- a/src/test/regress/sql/date.sql +++ b/src/test/regress/sql/date.sql @@ -235,3 +235,37 @@ SELECT EXTRACT(MILLENNIUM FROM DATE '2000-12-31'); -- 2 SELECT EXTRACT(MILLENNIUM FROM DATE '2001-01-01'); -- 3 -- next test to be fixed on the turn of the next millennium;-) SELECT EXTRACT(MILLENNIUM FROM CURRENT_DATE); -- 3 +-- +-- decade +-- +SELECT EXTRACT(DECADE FROM DATE '1994-12-25'); -- 199 +SELECT EXTRACT(DECADE FROM DATE '0010-01-01'); -- 1 +SELECT EXTRACT(DECADE FROM DATE '0009-12-31'); -- 0 +SELECT EXTRACT(DECADE FROM DATE '0001-01-01 BC'); -- 0 +SELECT EXTRACT(DECADE FROM DATE '0002-12-31 BC'); -- -1 +SELECT EXTRACT(DECADE FROM DATE '0011-01-01 BC'); -- -1 +SELECT EXTRACT(DECADE FROM DATE '0012-12-31 BC'); -- -2 +-- +-- some other types: +-- +-- on a timestamp. +SELECT EXTRACT(CENTURY FROM NOW())>=21 AS True; -- true +SELECT EXTRACT(CENTURY FROM TIMESTAMP '1970-03-20 04:30:00.00000'); -- 20 +-- on an interval +SELECT EXTRACT(CENTURY FROM INTERVAL '100 y'); -- 1 +SELECT EXTRACT(CENTURY FROM INTERVAL '99 y'); -- 0 +SELECT EXTRACT(CENTURY FROM INTERVAL '-99 y'); -- 0 +SELECT EXTRACT(CENTURY FROM INTERVAL '-100 y'); -- -1 +-- +-- test trunc function! +-- +SELECT DATE_TRUNC('MILLENNIUM', TIMESTAMP '1970-03-20 04:30:00.00000'); -- 1001 +SELECT DATE_TRUNC('MILLENNIUM', DATE '1970-03-20'); -- 1001-01-01 +SELECT DATE_TRUNC('CENTURY', TIMESTAMP '1970-03-20 04:30:00.00000'); -- 1901 +SELECT DATE_TRUNC('CENTURY', DATE '1970-03-20'); -- 1901 +SELECT DATE_TRUNC('CENTURY', DATE '2004-08-10'); -- 2001-01-01 +SELECT DATE_TRUNC('CENTURY', DATE '0002-02-04'); -- 0001-01-01 +SELECT DATE_TRUNC('CENTURY', DATE '0055-08-10 BC'); -- 0100-01-01 BC +SELECT DATE_TRUNC('DECADE', DATE '1993-12-25'); -- 1990-01-01 +SELECT DATE_TRUNC('DECADE', DATE '0004-12-25'); -- 0001-01-01 BC +SELECT DATE_TRUNC('DECADE', DATE '0002-12-31 BC'); -- 0011-01-01 BC