diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index eff243d73aa..4574105b05e 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 ce73f714f8b..0524bf12394 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 f99eba80ba3..978e35af8e0 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 b1839606026..5121991c58a 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 7469f40723a..9747b300b6c 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 d07dd3013dc..7a43e90d5db 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 25e58659e7d..bc384d01212 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";