]> granicus.if.org Git - postgresql/commitdiff
Attached is the new patch. To summarize:
authorBruce Momjian <bruce@momjian.us>
Mon, 6 Mar 2006 22:49:17 +0000 (22:49 +0000)
committerBruce Momjian <bruce@momjian.us>
Mon, 6 Mar 2006 22:49:17 +0000 (22:49 +0000)
   - new function justify_interval(interval)
   - modified function justify_hours(interval)
   - modified function justify_days(interval)

These functions are defined to meet the requirements as discussed in
this thread.  Specifically:

   - justify_hours makes certain the sign bit on the hours
     matches the sign bit on the days.  It only checks the
     sign bit on the days, and not the months, when
     determining if the hours should be positive or negative.
     After the call, -24 < hours < 24.

   - justify_days makes certain the sign bit on the days
     matches the sign bit on the months.  It's behavior does
     not depend on the hours, nor does it modify the hours.
     After the call, -30 < days < 30.

   - justify_interval makes sure the sign bits on all three
     fields months, days, and hours are all the same.  After
     the call, -24 < hours < 24 AND -30 < days < 30.

Mark Dilger

doc/src/sgml/func.sgml
src/backend/utils/adt/timestamp.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/utils/timestamp.h
src/test/regress/expected/interval.out
src/test/regress/sql/interval.sql

index eff243d73aa70493b5c58e8f132ef3136b4ca445..4574105b05eb18aa6a3bab2cfa1de50c3ec10669 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.309 2006/03/06 04:53:50 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.310 2006/03/06 22:49:15 momjian Exp $
 PostgreSQL documentation
 -->
 
@@ -5287,11 +5287,14 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
    <indexterm>
     <primary>isfinite</primary>
    </indexterm>
+   <indexterm>
+    <primary>justify_days</primary>
+   </indexterm>
    <indexterm>
     <primary>justify_hours</primary>
    </indexterm>
    <indexterm>
-    <primary>justify_days</primary>
+    <primary>justify_interval</primary>
    </indexterm>
    <indexterm>
     <primary>localtime</primary>
@@ -5429,6 +5432,14 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
         <entry><literal>true</literal></entry>
        </row>
 
+       <row>
+        <entry><literal><function>justify_days</function>(<type>interval</type>)</literal></entry>
+        <entry><type>interval</type></entry>
+        <entry>Adjust interval so 30-day time periods are represented as months</entry>
+        <entry><literal>justify_days(interval '30 days')</literal></entry>
+        <entry><literal>1 month</literal></entry>
+       </row>
+
        <row>
         <entry><literal><function>justify_hours</function>(<type>interval</type>)</literal></entry>
         <entry><type>interval</type></entry>
@@ -5438,11 +5449,11 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
        </row>
 
        <row>
-        <entry><literal><function>justify_days</function>(<type>interval</type>)</literal></entry>
+        <entry><literal><function>justify_interval</function>(<type>interval</type>)</literal></entry>
         <entry><type>interval</type></entry>
-        <entry>Adjust interval so 30-day time periods are represented as months</entry>
-        <entry><literal>justify_days(interval '30 days')</literal></entry>
-        <entry><literal>1 month</literal></entry>
+        <entry>Adjust interval using <function>justify_days</> and <function>justify_hours</>, with additional sign adjustments</></entry>
+        <entry><literal>justify_interval(interval '1 mon -1 hour')</literal></entry>
+        <entry><literal>29 days 23:00:00</literal></entry>
        </row>
 
        <row>
@@ -5486,13 +5497,6 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
      </tgroup>
     </table>
 
-   <para>
-    If you are using both <function>justify_hours</> and
-    <function>justify_days</>, it is best to use <function>justify_hours</>
-    first so any additional days will be included in the
-    <function>justify_days</> calculation.
-   </para>
-
    <para>
     In addition to these functions, the SQL <literal>OVERLAPS</> operator is
     supported:
index ce73f714f8bbfa49645c0fd776ec3d28f4e8fcb4..0524bf123944b0e947cc4c84e07d880a521c93c5 100644 (file)
@@ -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);
 }
 
index f99eba80ba3a1387578724d6f43e9ee54086d2cc..978e35af8e0f647d5b291b50ccb7094ea4b3e6e4 100644 (file)
@@ -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
index b183960602699068393936a3a7b2925f5a3969b0..5121991c58a5f7936d719378a6777d27219da260 100644 (file)
@@ -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_ ));
index 7469f40723a1ce3cebc036e64fa76cef8cfdbbf9..9747b300b6c27105b16c529cb8327a2328c85b46 100644 (file)
@@ -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);
 
index d07dd3013dc4ffa746e58ea4695a0033c1d39b26..7a43e90d5dbc34329623b6796b182b3a8506c0c9 100644 (file)
@@ -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)
+
index 25e58659e7dd5f6f0b43f7db5ff5346c30495f7f..bc384d012120441a155b6b8c4363e89849a3f50d 100644 (file)
@@ -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";