diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index eff243d73aa70493b5c58e8f132ef3136b4ca445..4574105b05eb18aa6a3bab2cfa1de50c3ec10669 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,5 +1,5 @@ @@ -5287,11 +5287,14 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})'); isfinite + + justify_days + justify_hours - justify_days + justify_interval localtime @@ -5429,6 +5432,14 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})'); true + + justify_days(interval) + interval + Adjust interval so 30-day time periods are represented as months + justify_days(interval '30 days') + 1 month + + justify_hours(interval) interval @@ -5438,11 +5449,11 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})'); - justify_days(interval) + justify_interval(interval) interval - Adjust interval so 30-day time periods are represented as months - justify_days(interval '30 days') - 1 month + Adjust interval using justify_days and justify_hours, with additional sign adjustments + justify_interval(interval '1 mon -1 hour') + 29 days 23:00:00 @@ -5486,13 +5497,6 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})'); - - If you are using both justify_hours and - justify_days, it is best to use justify_hours - first so any additional days will be included in the - justify_days calculation. - - In addition to these functions, the SQL OVERLAPS operator is supported: diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index ce73f714f8bbfa49645c0fd776ec3d28f4e8fcb4..0524bf123944b0e947cc4c84e07d880a521c93c5 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.161 2006/03/05 15:58:44 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.162 2006/03/06 22:49:16 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -1974,6 +1974,82 @@ timestamp_mi(PG_FUNCTION_ARGS) PG_RETURN_INTERVAL_P(result); } +/* + * interval_justify_interval() + * + * Adjust interval so 'month', 'day', and 'time' portions are within + * customary bounds. Specifically: + * + * 0 <= abs(time) < 24 hours + * 0 <= abs(day) < 30 days + * + * Also, the sign bit on all three fields is made equal, so either + * all three fields are negative or all are positive. + */ +Datum +interval_justify_interval(PG_FUNCTION_ARGS) +{ + Interval *span = PG_GETARG_INTERVAL_P(0); + Interval *result; + +#ifdef HAVE_INT64_TIMESTAMP + int64 wholeday; +#else + double wholeday; +#endif + int32 wholemonth; + + result = (Interval *) palloc(sizeof(Interval)); + result->month = span->month; + result->day = span->day; + result->time = span->time; + +#ifdef HAVE_INT64_TIMESTAMP + TMODULO(result->time, wholeday, USECS_PER_DAY); +#else + TMODULO(result->time, wholeday, (double) SECS_PER_DAY); +#endif + result->day += wholeday; /* could overflow... */ + + wholemonth = result->day / DAYS_PER_MONTH; + result->day -= wholemonth * DAYS_PER_MONTH; + result->month += wholemonth; + + if (result->month > 0 && + (result->day < 0 || (result->day == 0 && result->time < 0))) + { + result->day += DAYS_PER_MONTH; + result->month--; + } + else if (result->month < 0 && + (result->day > 0 || (result->day == 0 && result->time > 0))) + { + result->day -= DAYS_PER_MONTH; + result->month++; + } + + if (result->day > 0 && result->time < 0) + { +#ifdef HAVE_INT64_TIMESTAMP + result->time += USECS_PER_DAY; +#else + result->time += (double) SECS_PER_DAY; +#endif + result->day--; + } + else if (result->day < 0 && result->time > 0) + { +#ifdef HAVE_INT64_TIMESTAMP + result->time -= USECS_PER_DAY; +#else + result->time -= (double) SECS_PER_DAY; +#endif + result->day++; + } + + PG_RETURN_INTERVAL_P(result); +} + /* * interval_justify_hours() * @@ -2006,6 +2082,25 @@ interval_justify_hours(PG_FUNCTION_ARGS) #endif result->day += wholeday; /* could overflow... */ + if (result->day > 0 && result->time < 0) + { +#ifdef HAVE_INT64_TIMESTAMP + result->time += USECS_PER_DAY; +#else + result->time += (double) SECS_PER_DAY; +#endif + result->day--; + } + else if (result->day < 0 && result->time > 0) + { +#ifdef HAVE_INT64_TIMESTAMP + result->time -= USECS_PER_DAY; +#else + result->time -= (double) SECS_PER_DAY; +#endif + result->day++; + } + PG_RETURN_INTERVAL_P(result); } @@ -2031,6 +2126,17 @@ interval_justify_days(PG_FUNCTION_ARGS) result->day -= wholemonth * DAYS_PER_MONTH; result->month += wholemonth; + if (result->month > 0 && result->day < 0) + { + result->day += DAYS_PER_MONTH; + result->month--; + } + else if (result->month < 0 && result->day > 0) + { + result->day -= DAYS_PER_MONTH; + result->month++; + } + PG_RETURN_INTERVAL_P(result); } diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index f99eba80ba3a1387578724d6f43e9ee54086d2cc..978e35af8e0f647d5b291b50ccb7094ea4b3e6e4 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.318 2006/03/05 15:58:54 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.319 2006/03/06 22:49:16 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200602281 +#define CATALOG_VERSION_NO 200603061 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index b183960602699068393936a3a7b2925f5a3969b0..5121991c58a5f7936d719378a6777d27219da260 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.400 2006/03/05 15:58:54 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.401 2006/03/06 22:49:16 momjian Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -1462,6 +1462,8 @@ DATA(insert OID = 1173 ( timestamptz PGNSP PGUID 12 f f t f i 1 1184 "702" _ DESCR("convert abstime to timestamp with time zone"); DATA(insert OID = 1174 ( timestamptz PGNSP PGUID 12 f f t f s 1 1184 "1082" _null_ _null_ _null_ date_timestamptz - _null_ )); DESCR("convert date to timestamp with time zone"); +DATA(insert OID = 2711 ( justify_interval PGNSP PGUID 12 f f t f i 1 1186 "1186" _null_ _null_ _null_ interval_justify_interval - _null_ )); +DESCR("promote groups of 24 hours to numbers of days and promote groups of 30 days to numbers of months"); DATA(insert OID = 1175 ( justify_hours PGNSP PGUID 12 f f t f i 1 1186 "1186" _null_ _null_ _null_ interval_justify_hours - _null_ )); DESCR("promote groups of 24 hours to numbers of days"); DATA(insert OID = 1295 ( justify_days PGNSP PGUID 12 f f t f i 1 1186 "1186" _null_ _null_ _null_ interval_justify_days - _null_ )); diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h index 7469f40723a1ce3cebc036e64fa76cef8cfdbbf9..9747b300b6c27105b16c529cb8327a2328c85b46 100644 --- a/src/include/utils/timestamp.h +++ b/src/include/utils/timestamp.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/timestamp.h,v 1.58 2006/03/05 15:59:08 momjian Exp $ + * $PostgreSQL: pgsql/src/include/utils/timestamp.h,v 1.59 2006/03/06 22:49:17 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -234,6 +234,7 @@ extern Datum interval_cmp(PG_FUNCTION_ARGS); extern Datum interval_hash(PG_FUNCTION_ARGS); extern Datum interval_smaller(PG_FUNCTION_ARGS); extern Datum interval_larger(PG_FUNCTION_ARGS); +extern Datum interval_justify_interval(PG_FUNCTION_ARGS); extern Datum interval_justify_hours(PG_FUNCTION_ARGS); extern Datum interval_justify_days(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out index d07dd3013dc4ffa746e58ea4695a0033c1d39b26..7a43e90d5dbc34329623b6796b182b3a8506c0c9 100644 --- a/src/test/regress/expected/interval.out +++ b/src/test/regress/expected/interval.out @@ -241,3 +241,10 @@ SELECT justify_days(interval '6 months 36 days 5 hours 4 minutes 3 seconds') as @ 7 mons 6 days 5 hours 4 mins 3 secs (1 row) +-- test justify_interval() +SELECT justify_interval(interval '1 month -1 hour') as "1 month -1 hour"; + 1 month -1 hour +-------------------- + @ 29 days 23 hours +(1 row) + diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql index 25e58659e7dd5f6f0b43f7db5ff5346c30495f7f..bc384d012120441a155b6b8c4363e89849a3f50d 100644 --- a/src/test/regress/sql/interval.sql +++ b/src/test/regress/sql/interval.sql @@ -76,3 +76,6 @@ select '4 millenniums 5 centuries 4 decades 1 year 4 months 4 days 17 minutes 31 SELECT justify_hours(interval '6 months 3 days 52 hours 3 minutes 2 seconds') as "6 mons 5 days 4 hours 3 mins 2 seconds"; SELECT justify_days(interval '6 months 36 days 5 hours 4 minutes 3 seconds') as "7 mons 6 days 5 hours 4 mins 3 seconds"; +-- test justify_interval() + +SELECT justify_interval(interval '1 month -1 hour') as "1 month -1 hour";