I have applied this patch with significant adjustments.  I changed your
"simplify" function into two new functions, justify_hours() and
justify_days(), to handle the adjustment of interval values to hours <
24 and days < 30.  Do we want to separate functions?

I used date2j and j2date to add days to the interval value (you used a
comment as a place-holder).  I also went through all the Interval
mentions and made sure everything was handling the new 'day' field
properly.
        
        SELECT '2005-04-03 00:00:00'::timestamp WITH TIME ZONE + '1 day';
                ?column?
        ------------------------
         2005-04-04 00:00:00-04
        
        SELECT '2005-04-03 00:00:00'::timestamp WITH TIME ZONE + '24 hours';
                ?column?
        ------------------------
         2005-04-04 01:00:00-04

This looks a little strange:

        SELECT '2005-04-04 00:00:00'::timestamp with time zone - '2005-04-03 
00:00:00'::timestamp with time zone;
        ----------
         23:00:00
        (1 row)
        
        SELECT '2005-04-04 01:00:00'::timestamp with time zone - '2005-04-03 
00:00:00'::timestamp with time zone;
         ?column?
        ----------
         1 day

When you subtract two timestamps, do we return the hours or days of
difference?  What happens now is the difference is in hours/time, and
hours are rolled up into days.  Is this what we want?

We have this TODO item:

        o Allow TIMESTAMP WITH TIME ZONE to store the original timezone
          information, either zone name or offset from UTC [timezone]

          If the TIMESTAMP value is stored with a time zone name, interval
          computations should adjust based on the time zone rules.

It was originally added so we could distinguish 24 hours from 1 day.  Do
we still need this TODO?

---------------------------------------------------------------------------

Michael Glaesemann wrote:
> Please find attached a patch which adds a day field to the interval  
> struct so that we can treat INTERVAL '1 day' differently from  
> INTERVAL '24 hours' in DST-aware situations. It also includes a  
> function called interval_simplify() which takes an interval argument  
> and returns an interval where hours over 24 are promoted to days, e.g.,
> 
> template1=# select interval_simplify('3 months -11 days 79 hours 2  
> minutes'::interval);
>      interval_simplify
> --------------------------
> 3 mons -7 days -16:58:00
> (1 row)
> 
> If anyone has better ideas for the name of this function, please let  
> me know.
> 
> I've modified the regression tests, but still need to add additional  
> tests for the interval_simplify function, and I want to add a few  
> more tests for the new interval behavior. Also, the docs will need to  
> be updated to mention the new behavior. I plan on doing this in over  
> the next couple of days.
> 
> This is some of the first C I've hacked, and the first patch I've  
> submitted that's more than a documentation or a simple one-liner (and  
> even that one got worked over pretty good :) ), so I fully expect  
> some mistakes to be found. Please let me know and I'll do my best to  
> fix them.
> 
> In timestamp.c, I suspect that AdjustIntervalForTypmod,  
> interval_scale will need some modifications, though I'm not quite  
> sure what this code is doing. I've left them as-is. I've made some  
> changes to interval2tm, but believe that the changes I've made may  
> not be adequate. Given sufficient instruction, I'll be happy to make  
> the necessary changes to these functions.
> 
> A few things I noticed while I was working:
> 
> In interval_mul and interval_div, I'm wondering whether 30.0 and 24.0  
> shouldn't be substituted for 30 and 24 in the non-integer-timestamp  
> code path, as these are floats. Perhaps it doesn't make a difference  
> for multiplication, but I see similar usage in interval_cmp_interval.  
> I've left the code as-is.
> 
> In the deconstruct_array calls in interval_accum and interval_avg,  
> the size of interval is passed as a magic number (16). I think this  
> could be abstracted out, such as #define SIZEOF_INTERVAL 16 to make  
> the code a bit more robust (albeit just a little). Is this a  
> reasonable change?
> 
> Michael Glaesemann
> grzm myrealbox com
> 

[ Attachment, skipping... ]

> 

> 
> ---------------------------(end of broadcast)---------------------------
> TIP 3: if posting/reading through Usenet, please send an appropriate
>        subscribe-nomail command to [EMAIL PROTECTED] so that your
>        message can get through to the mailing list cleanly

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: doc/src/sgml/func.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/func.sgml,v
retrieving revision 1.267
diff -c -c -r1.267 func.sgml
*** doc/src/sgml/func.sgml      18 Jul 2005 22:34:14 -0000      1.267
--- doc/src/sgml/func.sgml      20 Jul 2005 03:56:10 -0000
***************
*** 5145,5150 ****
--- 5145,5166 ----
         </row>
  
         <row>
+         
<entry><literal><function>justify_hours</function>(<type>interval</type>)</literal></entry>
+         <entry><type>interval</type></entry>
+         <entry>Adjust interval so 24-hour time periods are represented as 
days</entry>
+         <entry><literal>justify_hours(interval '24 hours')</literal></entry>
+         <entry><literal>1 day</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>localtime</function></literal></entry>
          <entry><type>time</type></entry>
          <entry>Time of day; see <xref linkend="functions-datetime-current">
Index: src/backend/commands/variable.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/variable.c,v
retrieving revision 1.109
diff -c -c -r1.109 variable.c
*** src/backend/commands/variable.c     28 Jun 2005 05:08:55 -0000      1.109
--- src/backend/commands/variable.c     20 Jul 2005 03:56:11 -0000
***************
*** 292,297 ****
--- 292,306 ----
                        pfree(interval);
                        return NULL;
                }
+               if (interval->day != 0)
+               {
+                       if (source >= PGC_S_INTERACTIVE)
+                               ereport(ERROR,
+                                               
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                errmsg("invalid interval value 
for time zone: day not allowed")));
+                       pfree(interval);
+                       return NULL;
+               }
                if (doit)
                {
                        /* Here we change from SQL to Unix sign convention */
***************
*** 414,419 ****
--- 423,429 ----
                Interval interval;
  
                interval.month = 0;
+               interval.day = 0;
  #ifdef HAVE_INT64_TIMESTAMP
                interval.time = -(CTimeZone * USECS_PER_SEC);
  #else
Index: src/backend/utils/adt/date.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/date.c,v
retrieving revision 1.112
diff -c -c -r1.112 date.c
*** src/backend/utils/adt/date.c        12 Jul 2005 15:17:44 -0000      1.112
--- src/backend/utils/adt/date.c        20 Jul 2005 03:56:12 -0000
***************
*** 1423,1428 ****
--- 1423,1429 ----
        result = (Interval *) palloc(sizeof(Interval));
  
        result->time = time;
+       result->day = 0;
        result->month = 0;
  
        PG_RETURN_INTERVAL_P(result);
***************
*** 1477,1484 ****
  
        result = (Interval *) palloc(sizeof(Interval));
  
-       result->time = (time1 - time2);
        result->month = 0;
  
        PG_RETURN_INTERVAL_P(result);
  }
--- 1478,1486 ----
  
        result = (Interval *) palloc(sizeof(Interval));
  
        result->month = 0;
+       result->day = 0;
+       result->time = time1 - time2;
  
        PG_RETURN_INTERVAL_P(result);
  }
Index: src/backend/utils/adt/formatting.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/formatting.c,v
retrieving revision 1.90
diff -c -c -r1.90 formatting.c
*** src/backend/utils/adt/formatting.c  24 Jun 2005 01:10:11 -0000      1.90
--- src/backend/utils/adt/formatting.c  20 Jul 2005 03:56:16 -0000
***************
*** 899,905 ****
  /* static int is_acdc(char *str, int *len); */
  static int    seq_search(char *name, char **array, int type, int max, int 
*len);
  static void do_to_timestamp(text *date_txt, text *fmt,
!                               struct pg_tm * tm, fsec_t *fsec);
  static char *fill_str(char *str, int c, int max);
  static FormatNode *NUM_cache(int len, NUMDesc *Num, char *pars_str, bool 
*shouldFree);
  static char *int_to_roman(int number);
--- 899,905 ----
  /* static int is_acdc(char *str, int *len); */
  static int    seq_search(char *name, char **array, int type, int max, int 
*len);
  static void do_to_timestamp(text *date_txt, text *fmt,
!                               struct pg_tm *tm, fsec_t *fsec);
  static char *fill_str(char *str, int c, int max);
  static FormatNode *NUM_cache(int len, NUMDesc *Num, char *pars_str, bool 
*shouldFree);
  static char *int_to_roman(int number);
***************
*** 3028,3034 ****
   */
  static void
  do_to_timestamp(text *date_txt, text *fmt,
!                               struct pg_tm * tm, fsec_t *fsec)
  {
        FormatNode *format;
        TmFromChar      tmfc;
--- 3028,3034 ----
   */
  static void
  do_to_timestamp(text *date_txt, text *fmt,
!                               struct pg_tm *tm, fsec_t *fsec)
  {
        FormatNode *format;
        TmFromChar      tmfc;
Index: src/backend/utils/adt/nabstime.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v
retrieving revision 1.135
diff -c -c -r1.135 nabstime.c
*** src/backend/utils/adt/nabstime.c    12 Jul 2005 16:04:56 -0000      1.135
--- src/backend/utils/adt/nabstime.c    20 Jul 2005 03:56:17 -0000
***************
*** 829,835 ****
        Interval   *interval = PG_GETARG_INTERVAL_P(0);
        RelativeTime time;
        int                     year,
!                               month;
  
  #ifdef HAVE_INT64_TIMESTAMP
        int64           span;
--- 829,836 ----
        Interval   *interval = PG_GETARG_INTERVAL_P(0);
        RelativeTime time;
        int                     year,
!                               month,
!                               day;
  
  #ifdef HAVE_INT64_TIMESTAMP
        int64           span;
***************
*** 837,864 ****
        double          span;
  #endif
  
!       if (interval->month == 0)
!       {
!               year = 0;
!               month = 0;
!       }
!       else if (abs(interval->month) >=12)
!       {
!               year = (interval->month / 12);
!               month = (interval->month % 12);
!       }
!       else
!       {
!               year = 0;
!               month = interval->month;
!       }
  
  #ifdef HAVE_INT64_TIMESTAMP
!       span = ((INT64CONST(365250000) * year + INT64CONST(30000000) * month) *
!                       INT64CONST(86400)) + interval->time;
        span /= USECS_PER_SEC;
  #else
!       span = (365.25 * year + 30.0 * month) * SECS_PER_DAY + interval->time;
  #endif
  
        if (span < INT_MIN || span > INT_MAX)
--- 838,854 ----
        double          span;
  #endif
  
!       year = interval->month / 12;
!       month = interval->month % 12;
!       day = interval->day;
  
  #ifdef HAVE_INT64_TIMESTAMP
!       span = ((INT64CONST(365250000) * year + INT64CONST(30000000) * month +
!                       INT64CONST(1000000) * day) * INT64CONST(86400)) +
!                       interval->time;
        span /= USECS_PER_SEC;
  #else
!       span = (365.25 * year + 30.0 * month + day) * SECS_PER_DAY + 
interval->time;
  #endif
  
        if (span < INT_MIN || span > INT_MAX)
***************
*** 876,882 ****
        RelativeTime reltime = PG_GETARG_RELATIVETIME(0);
        Interval   *result;
        int                     year,
!                               month;
  
        result = (Interval *) palloc(sizeof(Interval));
  
--- 866,873 ----
        RelativeTime reltime = PG_GETARG_RELATIVETIME(0);
        Interval   *result;
        int                     year,
!                               month,
!                               day;
  
        result = (Interval *) palloc(sizeof(Interval));
  
***************
*** 887,892 ****
--- 878,884 ----
                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                          errmsg("cannot convert reltime \"invalid\" to 
interval")));
                        result->time = 0;
+                       result->day = 0;
                        result->month = 0;
                        break;
  
***************
*** 896,910 ****
--- 888,906 ----
                        reltime -= (year * (36525 * 864));
                        month = (reltime / (30 * SECS_PER_DAY));
                        reltime -= (month * (30 * SECS_PER_DAY));
+                       day = reltime / SECS_PER_DAY;
+                       reltime -= day * SECS_PER_DAY;
  
                        result->time = (reltime * USECS_PER_SEC);
  #else
                        TMODULO(reltime, year, 36525 * 864);
                        TMODULO(reltime, month, 30 * SECS_PER_DAY);
+                       TMODULO(reltime, day, SECS_PER_DAY);
  
                        result->time = reltime;
  #endif
                        result->month = 12 * year + month;
+                       result->day = day;
                        break;
        }
  
Index: src/backend/utils/adt/selfuncs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v
retrieving revision 1.184
diff -c -c -r1.184 selfuncs.c
*** src/backend/utils/adt/selfuncs.c    12 Jul 2005 16:04:57 -0000      1.184
--- src/backend/utils/adt/selfuncs.c    20 Jul 2005 03:56:20 -0000
***************
*** 2784,2793 ****
                                 * too accurate, but plenty good enough for our 
purposes.
                                 */
  #ifdef HAVE_INT64_TIMESTAMP
!                               return (interval->time + (interval->month * 
((365.25 / 12.0) * 86400000000.0)));
  #else
!                               return interval->time +
!                               interval  ->month * (365.25 / 12.0 * 24.0 * 
60.0 * 60.0);
  #endif
                        }
                case RELTIMEOID:
--- 2784,2794 ----
                                 * too accurate, but plenty good enough for our 
purposes.
                                 */
  #ifdef HAVE_INT64_TIMESTAMP
!                               return interval->time + interval->day * 
(double)USECS_PER_DAY +
!                                          interval->month * ((365.25 / 12.0) * 
USECS_PER_DAY);
  #else
!                               return interval->time + interval->day * 
SECS_PER_DAY +
!                                               interval->month * ((365.25 / 
12.0) * (double)SECS_PER_DAY);
  #endif
                        }
                case RELTIMEOID:
Index: src/backend/utils/adt/timestamp.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v
retrieving revision 1.133
diff -c -c -r1.133 timestamp.c
*** src/backend/utils/adt/timestamp.c   20 Jul 2005 03:50:24 -0000      1.133
--- src/backend/utils/adt/timestamp.c   20 Jul 2005 03:56:22 -0000
***************
*** 596,601 ****
--- 596,602 ----
  #else
        interval->time = pq_getmsgfloat8(buf);
  #endif
+       interval->day = pq_getmsgint(buf, sizeof(interval->day));
        interval->month = pq_getmsgint(buf, sizeof(interval->month));
  
        AdjustIntervalForTypmod(interval, typmod);
***************
*** 618,623 ****
--- 619,625 ----
  #else
        pq_sendfloat8(&buf, interval->time);
  #endif
+       pq_sendint(&buf, interval->day, sizeof(interval->day));
        pq_sendint(&buf, interval->month, sizeof(interval->month));
        PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
  }
***************
*** 697,744 ****
                else if (range == INTERVAL_MASK(YEAR))
                {
                        interval->month = (interval->month / 12) * 12;
                        interval->time = 0;
                }
                else if (range == INTERVAL_MASK(MONTH))
                {
                        interval->month %= 12;
                        interval->time = 0;
                }
                /* YEAR TO MONTH */
                else if (range == (INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH)))
                        interval->time = 0;
! 
                else if (range == INTERVAL_MASK(DAY))
                {
                        interval->month = 0;
! 
! #ifdef HAVE_INT64_TIMESTAMP
!                       interval->time = ((int) (interval->time / 
USECS_PER_DAY)) *
!                                                               USECS_PER_DAY;
! 
! #else
!                       interval->time = ((int) (interval->time / 
SECS_PER_DAY)) * SECS_PER_DAY;
! #endif
                }
                else if (range == INTERVAL_MASK(HOUR))
                {
- #ifdef HAVE_INT64_TIMESTAMP
-                       int64           day;
- #else
-                       double          day;
- #endif
- 
                        interval->month = 0;
  
  #ifdef HAVE_INT64_TIMESTAMP
-                       day = interval->time / USECS_PER_DAY;
-                       interval->time -= day * USECS_PER_DAY;
                        interval->time = (interval->time / USECS_PER_HOUR) *
                                                                USECS_PER_HOUR;
- 
  #else
!                       TMODULO(interval->time, day, (double)SECS_PER_DAY);
!                       interval->time = ((int) (interval->time / 3600)) * 
3600.0;
  #endif
                }
                else if (range == INTERVAL_MASK(MINUTE))
--- 699,735 ----
                else if (range == INTERVAL_MASK(YEAR))
                {
                        interval->month = (interval->month / 12) * 12;
+                       interval->day = 0;
                        interval->time = 0;
                }
                else if (range == INTERVAL_MASK(MONTH))
                {
                        interval->month %= 12;
+                       interval->day = 0;
                        interval->time = 0;
                }
                /* YEAR TO MONTH */
                else if (range == (INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH)))
+               {
+                       /* month is already year to month */
+                       interval->day = 0;
                        interval->time = 0;
!               }
                else if (range == INTERVAL_MASK(DAY))
                {
                        interval->month = 0;
!                       interval->time = 0;
                }
                else if (range == INTERVAL_MASK(HOUR))
                {
                        interval->month = 0;
+                       interval->day = 0;
  
  #ifdef HAVE_INT64_TIMESTAMP
                        interval->time = (interval->time / USECS_PER_HOUR) *
                                                                USECS_PER_HOUR;
  #else
!                       interval->time = ((int)(interval->time / 3600)) * 
3600.0;
  #endif
                }
                else if (range == INTERVAL_MASK(MINUTE))
***************
*** 750,755 ****
--- 741,747 ----
  #endif
  
                        interval->month = 0;
+                       interval->day = 0;
  
  #ifdef HAVE_INT64_TIMESTAMP
                        hour = interval->time / USECS_PER_HOUR;
***************
*** 759,785 ****
  
  #else
                        TMODULO(interval->time, hour, 3600.0);
!                       interval->time = ((int) (interval->time / 60)) * 60;
  #endif
                }
                else if (range == INTERVAL_MASK(SECOND))
                {
  #ifdef HAVE_INT64_TIMESTAMP
                        int64           minute;
- 
  #else
                        double          minute;
  #endif
  
                        interval->month = 0;
  
  #ifdef HAVE_INT64_TIMESTAMP
                        minute = interval->time / USECS_PER_MINUTE;
                        interval->time -= minute * USECS_PER_MINUTE;
- 
  #else
                        TMODULO(interval->time, minute, 60.0);
! /*                    interval->time = (int)(interval->time); */
  #endif
                }
                /* DAY TO HOUR */
--- 751,776 ----
  
  #else
                        TMODULO(interval->time, hour, 3600.0);
!                       interval->time = ((int)(interval->time / 60)) * 60.0;
  #endif
                }
                else if (range == INTERVAL_MASK(SECOND))
                {
  #ifdef HAVE_INT64_TIMESTAMP
                        int64           minute;
  #else
                        double          minute;
  #endif
  
                        interval->month = 0;
+                       interval->day = 0;
  
  #ifdef HAVE_INT64_TIMESTAMP
                        minute = interval->time / USECS_PER_MINUTE;
                        interval->time -= minute * USECS_PER_MINUTE;
  #else
                        TMODULO(interval->time, minute, 60.0);
!                       /* return subseconds too */
  #endif
                }
                /* DAY TO HOUR */
***************
*** 791,799 ****
  #ifdef HAVE_INT64_TIMESTAMP
                        interval->time = (interval->time / USECS_PER_HOUR) *
                                                                USECS_PER_HOUR;
- 
  #else
!                       interval->time = ((int) (interval->time / 3600)) * 3600;
  #endif
                }
                /* DAY TO MINUTE */
--- 782,789 ----
  #ifdef HAVE_INT64_TIMESTAMP
                        interval->time = (interval->time / USECS_PER_HOUR) *
                                                                USECS_PER_HOUR;
  #else
!                       interval->time = ((int) (interval->time / 3600)) * 
3600.0;
  #endif
                }
                /* DAY TO MINUTE */
***************
*** 806,814 ****
  #ifdef HAVE_INT64_TIMESTAMP
                        interval->time = (interval->time / USECS_PER_MINUTE) *
                                                                
USECS_PER_MINUTE;
- 
  #else
!                       interval->time = ((int) (interval->time / 60)) * 60;
  #endif
                }
                /* DAY TO SECOND */
--- 796,803 ----
  #ifdef HAVE_INT64_TIMESTAMP
                        interval->time = (interval->time / USECS_PER_MINUTE) *
                                                                
USECS_PER_MINUTE;
  #else
!                       interval->time = ((int)(interval->time / 60)) * 60.0;
  #endif
                }
                /* DAY TO SECOND */
***************
*** 822,845 ****
                else if (range == (INTERVAL_MASK(HOUR) |
                                                   INTERVAL_MASK(MINUTE)))
                {
- #ifdef HAVE_INT64_TIMESTAMP
-                       int64           day;
- 
- #else
-                       double          day;
- #endif
- 
                        interval->month = 0;
  
  #ifdef HAVE_INT64_TIMESTAMP
-                       day = (interval->time / USECS_PER_DAY);
-                       interval->time -= day * USECS_PER_DAY;
                        interval->time = (interval->time / USECS_PER_MINUTE) *
                                                                
USECS_PER_MINUTE;
- 
  #else
!                       TMODULO(interval->time, day, (double)SECS_PER_DAY);
!                       interval->time = ((int) (interval->time / 60)) * 60;
  #endif
                }
                /* HOUR TO SECOND */
--- 811,824 ----
                else if (range == (INTERVAL_MASK(HOUR) |
                                                   INTERVAL_MASK(MINUTE)))
                {
                        interval->month = 0;
+                       interval->day = 0;
  
  #ifdef HAVE_INT64_TIMESTAMP
                        interval->time = (interval->time / USECS_PER_MINUTE) *
                                                                
USECS_PER_MINUTE;
  #else
!                       interval->time = ((int)(interval->time / 60)) * 60.0;
  #endif
                }
                /* HOUR TO SECOND */
***************
*** 847,868 ****
                                                   INTERVAL_MASK(MINUTE) |
                                                   INTERVAL_MASK(SECOND)))
                {
- #ifdef HAVE_INT64_TIMESTAMP
-                       int64           day;
- 
- #else
-                       double          day;
- #endif
- 
                        interval->month = 0;
! 
! #ifdef HAVE_INT64_TIMESTAMP
!                       day = interval->time / USECS_PER_DAY;
!                       interval->time -= day * USECS_PER_DAY;
! 
! #else
!                       TMODULO(interval->time, day, (double)SECS_PER_DAY);
! #endif
                }
                /* MINUTE TO SECOND */
                else if (range == (INTERVAL_MASK(MINUTE) |
--- 826,834 ----
                                                   INTERVAL_MASK(MINUTE) |
                                                   INTERVAL_MASK(SECOND)))
                {
                        interval->month = 0;
!                       interval->day = 0;
!                       /* return subseconds too */
                }
                /* MINUTE TO SECOND */
                else if (range == (INTERVAL_MASK(MINUTE) |
***************
*** 876,881 ****
--- 842,848 ----
  #endif
  
                        interval->month = 0;
+                       interval->day = 0;
  
  #ifdef HAVE_INT64_TIMESTAMP
                        hour = interval->time / USECS_PER_HOUR;
***************
*** 1029,1035 ****
   * timezone) will be used.
   */
  int
! timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm, fsec_t *fsec, char 
**tzn, pg_tz *attimezone)
  {
        Timestamp date;
        Timestamp       time;
--- 996,1002 ----
   * timezone) will be used.
   */
  int
! timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm, fsec_t *fsec, char 
**tzn, pg_tz *attimezone)
  {
        Timestamp date;
        Timestamp       time;
***************
*** 1165,1171 ****
   * Returns -1 on failure (value out of range).
   */
  int
! tm2timestamp(struct pg_tm * tm, fsec_t fsec, int *tzp, Timestamp *result)
  {
  #ifdef HAVE_INT64_TIMESTAMP
        int date;
--- 1132,1138 ----
   * Returns -1 on failure (value out of range).
   */
  int
! tm2timestamp(struct pg_tm *tm, fsec_t fsec, int *tzp, Timestamp *result)
  {
  #ifdef HAVE_INT64_TIMESTAMP
        int date;
***************
*** 1205,1211 ****
   * Convert a interval data type to a tm structure.
   */
  int
! interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec)
  {
  #ifdef HAVE_INT64_TIMESTAMP
        int64           time;
--- 1172,1178 ----
   * Convert a interval data type to a tm structure.
   */
  int
! interval2tm(Interval span, struct pg_tm *tm, fsec_t *fsec)
  {
  #ifdef HAVE_INT64_TIMESTAMP
        int64           time;
***************
*** 1213,1235 ****
        double          time;
  #endif
  
!       if (span.month != 0)
!       {
!               tm->tm_year = span.month / 12;
!               tm->tm_mon = span.month % 12;
! 
!       }
!       else
!       {
!               tm->tm_year = 0;
!               tm->tm_mon = 0;
!       }
! 
        time = span.time;
  
  #ifdef HAVE_INT64_TIMESTAMP
-       tm->tm_mday = (time / USECS_PER_DAY);
-       time -= (tm->tm_mday * USECS_PER_DAY);
        tm->tm_hour = (time / USECS_PER_HOUR);
        time -= (tm->tm_hour * USECS_PER_HOUR);
        tm->tm_min = (time / USECS_PER_MINUTE);
--- 1180,1191 ----
        double          time;
  #endif
  
!       tm->tm_year = span.month / 12;
!       tm->tm_mon = span.month % 12;
!       tm->tm_mday = span.day;
        time = span.time;
  
  #ifdef HAVE_INT64_TIMESTAMP
        tm->tm_hour = (time / USECS_PER_HOUR);
        time -= (tm->tm_hour * USECS_PER_HOUR);
        tm->tm_min = (time / USECS_PER_MINUTE);
***************
*** 1237,1243 ****
        tm->tm_sec = (time / USECS_PER_SEC);
        *fsec = (time - (tm->tm_sec * USECS_PER_SEC));
  #else
-       TMODULO(time, tm->tm_mday, (double)SECS_PER_DAY);
        TMODULO(time, tm->tm_hour, 3600.0);
        TMODULO(time, tm->tm_min, 60.0);
        TMODULO(time, tm->tm_sec, 1.0);
--- 1193,1198 ----
***************
*** 1248,1264 ****
  }
  
  int
! tm2interval(struct pg_tm * tm, fsec_t fsec, Interval *span)
  {
        span->month = tm->tm_year * 12 + tm->tm_mon;
  #ifdef HAVE_INT64_TIMESTAMP
!       span->time = (((((((tm->tm_mday * INT64CONST(24)) +
!                                               tm->tm_hour) * INT64CONST(60)) +
                                                tm->tm_min) * INT64CONST(60)) +
                                                tm->tm_sec) * USECS_PER_SEC) + 
fsec;
  #else
!       span->time = (((((tm->tm_mday * 24.0) +
!                                               tm->tm_hour) * 60.0) +
                                                tm->tm_min) * 60.0) +
                                                tm->tm_sec;
        span->time = JROUND(span->time + fsec);
--- 1203,1218 ----
  }
  
  int
! tm2interval(struct pg_tm *tm, fsec_t fsec, Interval *span)
  {
        span->month = tm->tm_year * 12 + tm->tm_mon;
+       span->day   = tm->tm_mday;
  #ifdef HAVE_INT64_TIMESTAMP
!       span->time = (((((tm->tm_hour * INT64CONST(60)) +
                                                tm->tm_min) * INT64CONST(60)) +
                                                tm->tm_sec) * USECS_PER_SEC) + 
fsec;
  #else
!       span->time = (((tm->tm_hour * 60.0) +
                                                tm->tm_min) * 60.0) +
                                                tm->tm_sec;
        span->time = JROUND(span->time + fsec);
***************
*** 1320,1326 ****
   *---------------------------------------------------------*/
  
  void
! GetEpochTime(struct pg_tm * tm)
  {
        struct pg_tm *t0;
        pg_time_t       epoch = 0;
--- 1274,1280 ----
   *---------------------------------------------------------*/
  
  void
! GetEpochTime(struct pg_tm *tm)
  {
        struct pg_tm *t0;
        pg_time_t       epoch = 0;
***************
*** 1654,1668 ****
        span2 = interval2->time;
  
  #ifdef HAVE_INT64_TIMESTAMP
!       if (interval1->month != 0)
!               span1 += interval1->month * INT64CONST(30) * USECS_PER_DAY;
!       if (interval2->month != 0)
!               span2 += interval2->month * INT64CONST(30) * USECS_PER_DAY;
! #else
!       if (interval1->month != 0)
!               span1 += interval1->month * (30.0 * SECS_PER_DAY);
!       if (interval2->month != 0)
!               span2 += interval2->month * (30.0 * SECS_PER_DAY);
  #endif
  
        return ((span1 < span2) ? -1 : (span1 > span2) ? 1 : 0);
--- 1608,1622 ----
        span2 = interval2->time;
  
  #ifdef HAVE_INT64_TIMESTAMP
!       span1 += interval1->month * INT64CONST(30) * USECS_PER_DAY;
!       span1 += interval1->day * INT64CONST(24) * USECS_PER_HOUR;
!       span2 += interval2->month * INT64CONST(30) * USECS_PER_DAY;
!       span2 += interval2->day * INT64CONST(24) * USECS_PER_HOUR;
! #else
!       span1 += interval1->month * (30.0 * SECS_PER_DAY);
!       span1 += interval1->day * (24.0 * SECS_PER_HOUR);
!       span2 += interval2->month * (30.0 * SECS_PER_DAY);
!       span2 += interval2->day * (24.0 * SECS_PER_HOUR);
  #endif
  
        return ((span1 < span2) ? -1 : (span1 > span2) ? 1 : 0);
***************
*** 1744,1750 ****
         * sizeof(Interval), so that any garbage pad bytes in the structure
         * won't be included in the hash!
         */
!       return hash_any((unsigned char *) key, sizeof(key->time) + 
sizeof(key->month));
  }
  
  /* overlaps_timestamp() --- implements the SQL92 OVERLAPS operator.
--- 1698,1705 ----
         * sizeof(Interval), so that any garbage pad bytes in the structure
         * won't be included in the hash!
         */
!       return hash_any((unsigned char *) key, 
!                       sizeof(key->time) + sizeof(key->day) + 
sizeof(key->month));
  }
  
  /* overlaps_timestamp() --- implements the SQL92 OVERLAPS operator.
***************
*** 1934,1951 ****
  #endif
  
        result->month = 0;
  
        PG_RETURN_INTERVAL_P(result);
  }
  
  
  /* timestamp_pl_interval()
   * Add a interval to a timestamp data type.
!  * Note that interval has provisions for qualitative year/month
   *    units, so try to do the right thing with them.
   * To add a month, increment the month, and use the same day of month.
   * Then, if the next month has fewer days, set the day of month
   *    to the last day of month.
   * Lastly, add in the "quantitative time".
   */
  Datum
--- 1889,1964 ----
  #endif
  
        result->month = 0;
+       result->day = 0;
+ 
+       result = DatumGetIntervalP(DirectFunctionCall1(interval_justify_hours,
+                                                                               
                IntervalPGetDatum(result)));
+       PG_RETURN_INTERVAL_P(result);
+ }
+ 
+ /*    interval_justify_hours()
+  *    Adjust interval so 'time' contains less than a whole day, and
+  *    'day' contains an integral number of days.  This is useful for
+  *    situations (such as non-TZ) where '1 day' = '24 hours' is valid,
+  *    e.g. interval subtraction and division.  The SQL standard requires
+  *    such conversion in these cases, but not the conversion of days to 
months.
+  */
+ Datum
+ interval_justify_hours(PG_FUNCTION_ARGS)
+ {
+       Interval  *span = PG_GETARG_INTERVAL_P(0);
+       Interval  *result;
+ 
+       result = (Interval *) palloc(sizeof(Interval));
+       result->month = span->month;
+       result->time = span->time;
+ 
+ #ifdef HAVE_INT64_TIMESTAMP
+       result->time += span->day * USECS_PER_DAY;
+       result->day = result->time / USECS_PER_DAY;
+       result->time -= result->day * USECS_PER_DAY;
+ #else
+       result->time += span->day * (double)SECS_PER_DAY;
+       TMODULO(result->time, result->day, (double)SECS_PER_DAY);
+ #endif
  
        PG_RETURN_INTERVAL_P(result);
  }
  
+ /*    interval_justify_days()
+  *    Adjust interval so 'time' contains less than 30 days, and
+  *    adds as months.
+  */
+ Datum
+ interval_justify_days(PG_FUNCTION_ARGS)
+ {
+       Interval  *span = PG_GETARG_INTERVAL_P(0);
+       Interval  *result;
+ 
+       result = (Interval *) palloc(sizeof(Interval));
+       result->day = span->day;
+       result->time = span->time;
+ 
+ #ifdef HAVE_INT64_TIMESTAMP
+       result->day += span->month * 30.0;
+       result->month = span->day / 30;
+       result->day -= result->month * 30;
+ #else
+       result->day += span->month * 30.0;
+       TMODULO(result->day, result->month, 30.0);
+ #endif
+ 
+       PG_RETURN_INTERVAL_P(result);
+ }
  
  /* timestamp_pl_interval()
   * Add a interval to a timestamp data type.
!  * Note that interval has provisions for qualitative year/month and day
   *    units, so try to do the right thing with them.
   * To add a month, increment the month, and use the same day of month.
   * Then, if the next month has fewer days, set the day of month
   *    to the last day of month.
+  * To add a day, increment the mday, and use the same time of day.
   * Lastly, add in the "quantitative time".
   */
  Datum
***************
*** 1957,1963 ****
  
        if (TIMESTAMP_NOT_FINITE(timestamp))
                result = timestamp;
- 
        else
        {
                if (span->month != 0)
--- 1970,1975 ----
***************
*** 1993,1999 ****
                                                 errmsg("timestamp out of 
range")));
                }
  
!               timestamp +=span->time;
                result = timestamp;
        }
  
--- 2005,2033 ----
                                                 errmsg("timestamp out of 
range")));
                }
  
!               if (span->day != 0)
!               {
!                       struct pg_tm tt,
!                                          *tm = &tt;
!                       fsec_t          fsec;
!                       int                     julian;
!                       
!                       if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, 
NULL) !=0)
!                               ereport(ERROR,
!                                               
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
!                                                errmsg("timestamp out of 
range")));
! 
!                       /* Add days by converting to and from julian */
!                       julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 
span->day;
!                       j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
! 
!                       if (tm2timestamp(tm, fsec, NULL, &timestamp) !=0)
!                               ereport(ERROR,
!                                               
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
!                                                errmsg("timestamp out of 
range")));
!               }
! 
!               timestamp += span->time;
                result = timestamp;
        }
  
***************
*** 2008,2013 ****
--- 2042,2048 ----
        Interval        tspan;
  
        tspan.month = -span->month;
+       tspan.day = -span->day;
        tspan.time = -span->time;
  
        return DirectFunctionCall2(timestamp_pl_interval,
***************
*** 2036,2042 ****
  
        if (TIMESTAMP_NOT_FINITE(timestamp))
                result = timestamp;
- 
        else
        {
                if (span->month != 0)
--- 2071,2076 ----
***************
*** 2074,2080 ****
                                                 errmsg("timestamp out of 
range")));
                }
  
!               timestamp +=span->time;
                result = timestamp;
        }
  
--- 2108,2138 ----
                                                 errmsg("timestamp out of 
range")));
                }
  
!               if (span->day != 0)
!               {
!                       struct pg_tm tt,
!                                          *tm = &tt;
!                       fsec_t          fsec;
!                       int                     julian;
! 
!                       if (timestamp2tm(timestamp, &tz, tm, &fsec, &tzn, NULL) 
!=0)
!                               ereport(ERROR,
!                                               
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
!                                                errmsg("timestamp out of 
range")));
! 
!                       /* Add days by converting to and from julian */
!                       julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + 
span->day;
!                       j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
! 
!                       tz = DetermineLocalTimeZone(tm);
! 
!                       if (tm2timestamp(tm, fsec, &tz, &timestamp) !=0)
!                               ereport(ERROR,
!                                               
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
!                                                errmsg("timestamp out of 
range")));
!               }
! 
!               timestamp += span->time;
                result = timestamp;
        }
  
***************
*** 2089,2094 ****
--- 2147,2153 ----
        Interval        tspan;
  
        tspan.month = -span->month;
+       tspan.day = -span->day;
        tspan.time = -span->time;
  
        return DirectFunctionCall2(timestamptz_pl_interval,
***************
*** 2106,2111 ****
--- 2165,2171 ----
        result = (Interval *) palloc(sizeof(Interval));
  
        result->time = -(interval->time);
+       result->day = -(interval->day);
        result->month = -(interval->month);
  
        PG_RETURN_INTERVAL_P(result);
***************
*** 2151,2156 ****
--- 2211,2217 ----
        result = (Interval *) palloc(sizeof(Interval));
  
        result->month = (span1->month + span2->month);
+       result->day = (span1->day + span2->day);
  #ifdef HAVE_INT64_TIMESTAMP
        result->time = (span1->time + span2->time);
  #else
***************
*** 2170,2175 ****
--- 2231,2237 ----
        result = (Interval *) palloc(sizeof(Interval));
  
        result->month = (span1->month - span2->month);
+       result->day = (span1->day - span2->day);
  #ifdef HAVE_INT64_TIMESTAMP
        result->time = (span1->time - span2->time);
  #else
***************
*** 2188,2210 ****
  
  #ifdef HAVE_INT64_TIMESTAMP
        int64           months;
  #else
        double          months;
  #endif
  
        result = (Interval *) palloc(sizeof(Interval));
  
        months = (span1->month * factor);
  #ifdef HAVE_INT64_TIMESTAMP
        result->month = months;
        result->time = (span1->time * factor);
!       result->time += (months - result->month) * INT64CONST(30) *
!                                       USECS_PER_DAY;
  #else
        result->month = (int)months;
        result->time = JROUND(span1->time * factor);
        /* evaluate fractional months as 30 days */
        result->time += JROUND((months - result->month) * 30 * SECS_PER_DAY);
  #endif
  
        PG_RETURN_INTERVAL_P(result);
--- 2250,2279 ----
  
  #ifdef HAVE_INT64_TIMESTAMP
        int64           months;
+       int64       days;
  #else
        double          months;
+       double      days;
  #endif
  
        result = (Interval *) palloc(sizeof(Interval));
  
        months = (span1->month * factor);
+       days = (span1->day * factor);
  #ifdef HAVE_INT64_TIMESTAMP
        result->month = months;
+       result->day = days;
        result->time = (span1->time * factor);
!       result->time += (months - result->month) * INT64CONST(30) * 
USECS_PER_DAY;
!       result->time += (days - result->day) * INT64CONST(24) * USECS_PER_HOUR;
  #else
        result->month = (int)months;
+       result->day = (int)days;
        result->time = JROUND(span1->time * factor);
        /* evaluate fractional months as 30 days */
        result->time += JROUND((months - result->month) * 30 * SECS_PER_DAY);
+       /* evaluate fractional days as 24 hours */
+       result->time += JROUND((days - result->day) * 24 * SECS_PER_HOUR);
  #endif
  
        PG_RETURN_INTERVAL_P(result);
***************
*** 2225,2236 ****
  {
        Interval   *span = PG_GETARG_INTERVAL_P(0);
        float8          factor = PG_GETARG_FLOAT8(1);
        Interval   *result;
  
- #ifndef HAVE_INT64_TIMESTAMP
-       double          months;
- #endif
- 
        result = (Interval *) palloc(sizeof(Interval));
  
        if (factor == 0.0)
--- 2294,2302 ----
  {
        Interval   *span = PG_GETARG_INTERVAL_P(0);
        float8          factor = PG_GETARG_FLOAT8(1);
+       double          month_remainder, day_remainder;
        Interval   *result;
  
        result = (Interval *) palloc(sizeof(Interval));
  
        if (factor == 0.0)
***************
*** 2238,2257 ****
                                (errcode(ERRCODE_DIVISION_BY_ZERO),
                                 errmsg("division by zero")));
  
  #ifdef HAVE_INT64_TIMESTAMP
!       result->month = (span->month / factor);
!       result->time = (span->time / factor);
!       /* evaluate fractional months as 30 days */
!       result->time += ((span->month - (result->month * factor)) *
!                                       INT64CONST(30) * USECS_PER_DAY) / 
factor;
  #else
!       months = span->month / factor;
!       result->month = (int)months;
!       result->time = JROUND(span->time / factor);
!       /* evaluate fractional months as 30 days */
!       result->time += JROUND((months - result->month) * 30 * SECS_PER_DAY);
  #endif
  
        PG_RETURN_INTERVAL_P(result);
  }
  
--- 2304,2332 ----
                                (errcode(ERRCODE_DIVISION_BY_ZERO),
                                 errmsg("division by zero")));
  
+       result->month = span->month / factor;
+       result->day = span->day / factor;
+       result->time = span->time / factor;
+ 
+       /* Computer remainders */
+       month_remainder = (span->month - result->month * factor) / factor;
+       day_remainder = (span->day - result->day * factor) / factor;
+ 
+       /* Cascade fractions to lower units */
+       /* fractional months full days into days */
+       result->day += month_remainder * 30;
+       /* fractional months partial days into time */
+       day_remainder += (month_remainder * 30) - (int)(month_remainder * 30);
+ 
  #ifdef HAVE_INT64_TIMESTAMP
!       result->time += day_remainder * USECS_PER_DAY;
  #else
!       result->time += day_remainder * SECS_PER_DAY;
!       result->time = JROUND(result->time);
  #endif
  
+       result = DatumGetIntervalP(DirectFunctionCall1(interval_justify_hours,
+                                                                               
                IntervalPGetDatum(result)));
        PG_RETURN_INTERVAL_P(result);
  }
  
***************
*** 2276,2284 ****
        Interval   *newsum;
        ArrayType  *result;
  
-       /* We assume the input is array of interval */
        deconstruct_array(transarray,
!                                         INTERVALOID, 12, false, 'd',
                                          &transdatums, &ndatums);
        if (ndatums != 2)
                elog(ERROR, "expected 2-element interval array");
--- 2351,2358 ----
        Interval   *newsum;
        ArrayType  *result;
  
        deconstruct_array(transarray,
!                                         INTERVALOID, sizeof(Interval), false, 
'd',
                                          &transdatums, &ndatums);
        if (ndatums != 2)
                elog(ERROR, "expected 2-element interval array");
***************
*** 2304,2310 ****
        transdatums[1] = IntervalPGetDatum(&N);
  
        result = construct_array(transdatums, 2,
!                                                        INTERVALOID, 12, 
false, 'd');
  
        PG_RETURN_ARRAYTYPE_P(result);
  }
--- 2378,2384 ----
        transdatums[1] = IntervalPGetDatum(&N);
  
        result = construct_array(transdatums, 2,
!                                                        INTERVALOID, 
sizeof(Interval), false, 'd');
  
        PG_RETURN_ARRAYTYPE_P(result);
  }
***************
*** 2318,2326 ****
        Interval        sumX,
                                N;
  
-       /* We assume the input is array of interval */
        deconstruct_array(transarray,
!                                         INTERVALOID, 12, false, 'd',
                                          &transdatums, &ndatums);
        if (ndatums != 2)
                elog(ERROR, "expected 2-element interval array");
--- 2392,2399 ----
        Interval        sumX,
                                N;
  
        deconstruct_array(transarray,
!                                         INTERVALOID, sizeof(Interval), false, 
'd',
                                          &transdatums, &ndatums);
        if (ndatums != 2)
                elog(ERROR, "expected 2-element interval array");
***************
*** 2721,2727 ****
        result = palloc(len);
  
        VARATT_SIZEP(result) = len;
!       memmove(VARDATA(result), str, (len - VARHDRSZ));
  
        pfree(str);
  
--- 2794,2800 ----
        result = palloc(len);
  
        VARATT_SIZEP(result) = len;
!       memmove(VARDATA(result), str, len - VARHDRSZ);
  
        pfree(str);
  
***************
*** 3080,3085 ****
--- 3153,3159 ----
                {
                        switch (val)
                        {
+                               /* fall through */
                                case DTK_MILLENNIUM:
                                        /* caution: C division may have 
negative remainder */
                                        tm->tm_year = (tm->tm_year / 1000) * 
1000;
***************
*** 3830,3840 ****
  #else
                result = interval->time;
  #endif
!               if (interval->month != 0)
!               {
!                       result += (365.25 * SECS_PER_DAY) * (interval->month / 
12);
!                       result += (30.0 * SECS_PER_DAY) * (interval->month % 
12);
!               }
        }
        else
        {
--- 3904,3912 ----
  #else
                result = interval->time;
  #endif
!               result += (365.25 * SECS_PER_DAY) * (interval->month / 12);
!               result += (30.0 * SECS_PER_DAY) * (interval->month % 12);
!               result += interval->day * SECS_PER_DAY;
        }
        else
        {
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/catalog/pg_proc.h,v
retrieving revision 1.376
diff -c -c -r1.376 pg_proc.h
*** src/include/catalog/pg_proc.h       10 Jul 2005 21:13:59 -0000      1.376
--- src/include/catalog/pg_proc.h       20 Jul 2005 03:56:27 -0000
***************
*** 1497,1502 ****
--- 1497,1506 ----
  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 = 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_ ));
+ DESCR("promote groups of 30 days to numbers of months");
  DATA(insert OID = 1176 (  timestamptz    PGNSP PGUID 14 f f t f s 2 1184 
"1082 1083" _null_ _null_ _null_     "select cast(($1 + $2) as timestamp with 
time zone)" - _null_ ));
  DESCR("convert date and time to timestamp with time zone");
  DATA(insert OID = 1177 (  interval               PGNSP PGUID 12 f f t f i 1 
1186 "703" _null_ _null_ _null_ reltime_interval - _null_ ));
Index: src/include/catalog/pg_type.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/catalog/pg_type.h,v
retrieving revision 1.163
diff -c -c -r1.163 pg_type.h
*** src/include/catalog/pg_type.h       7 Jul 2005 20:39:59 -0000       1.163
--- src/include/catalog/pg_type.h       20 Jul 2005 03:56:28 -0000
***************
*** 457,463 ****
  DESCR("date and time with time zone");
  #define TIMESTAMPTZOID        1184
  DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b t \054 0     1184 
array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1186 ( interval      PGNSP PGUID 12 f b t \054 0    0 
interval_in interval_out interval_recv interval_send - d p f 0 -1 0 _null_ 
_null_ ));
  DESCR("@ <number> <units>, time interval");
  #define INTERVALOID           1186
  DATA(insert OID = 1187 ( _interval     PGNSP PGUID    -1 f b t \054 0 1186 
array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
--- 457,463 ----
  DESCR("date and time with time zone");
  #define TIMESTAMPTZOID        1184
  DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b t \054 0     1184 
array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
! DATA(insert OID = 1186 ( interval      PGNSP PGUID 16 f b t \054 0    0 
interval_in interval_out interval_recv interval_send - d p f 0 -1 0 _null_ 
_null_ ));
  DESCR("@ <number> <units>, time interval");
  #define INTERVALOID           1186
  DATA(insert OID = 1187 ( _interval     PGNSP PGUID    -1 f b t \054 0 1186 
array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
Index: src/include/utils/timestamp.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/utils/timestamp.h,v
retrieving revision 1.46
diff -c -c -r1.46 timestamp.h
*** src/include/utils/timestamp.h       29 Jun 2005 22:51:57 -0000      1.46
--- src/include/utils/timestamp.h       20 Jul 2005 03:56:28 -0000
***************
*** 25,33 ****
  
  /*
   * Timestamp represents absolute time.
!  * Interval represents delta time. Keep track of months (and years)
!  *    separately since the elapsed time spanned is unknown until instantiated
!  *    relative to an absolute time.
   *
   * Note that Postgres uses "time interval" to mean a bounded interval,
   * consisting of a beginning and ending time, not a time span - thomas 
97/03/20
--- 25,33 ----
  
  /*
   * Timestamp represents absolute time.
!  * Interval represents delta time. Keep track of months (and years), days,
!  *    and time separately since the elapsed time spanned is unknown until
!  *    instantiated relative to an absolute time.
   *
   * Note that Postgres uses "time interval" to mean a bounded interval,
   * consisting of a beginning and ending time, not a time span - thomas 
97/03/20
***************
*** 45,56 ****
  typedef struct
  {
  #ifdef HAVE_INT64_TIMESTAMP
!       int64           time;                   /* all time units other than 
months and
!                                                                * years */
  #else
!       double          time;                   /* all time units other than 
months and
!                                                                * years */
  #endif
        int32           month;                  /* months and years, after time 
for
                                                                 * alignment */
  } Interval;
--- 45,57 ----
  typedef struct
  {
  #ifdef HAVE_INT64_TIMESTAMP
!       int64           time;                   /* all time units other than 
days, 
!                                                                * months and 
years */
  #else
!       double          time;                   /* all time units other than 
days,
!                                                                * months and 
years */
  #endif
+       int32           day;                /* days, after time for alignment */
        int32           month;                  /* months and years, after time 
for
                                                                 * alignment */
  } Interval;
***************
*** 60,65 ****
--- 61,67 ----
  #define MAX_INTERVAL_PRECISION 6
  
  #define SECS_PER_DAY  86400
+ #define SECS_PER_HOUR   3600
  #ifdef HAVE_INT64_TIMESTAMP
  #define USECS_PER_DAY INT64CONST(86400000000)
  #define USECS_PER_HOUR        INT64CONST(3600000000)
***************
*** 212,217 ****
--- 214,221 ----
  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_hours(PG_FUNCTION_ARGS);
+ extern Datum interval_justify_days(PG_FUNCTION_ARGS);
  
  extern Datum timestamp_text(PG_FUNCTION_ARGS);
  extern Datum text_timestamp(PG_FUNCTION_ARGS);
***************
*** 266,281 ****
  
  extern TimestampTz GetCurrentTimestamp(void);
  
! extern int    tm2timestamp(struct pg_tm * tm, fsec_t fsec, int *tzp, 
Timestamp *dt);
! extern int timestamp2tm(Timestamp dt, int *tzp, struct pg_tm * tm,
                         fsec_t *fsec, char **tzn, pg_tz *attimezone);
  extern void dt2time(Timestamp dt, int *hour, int *min, int *sec, fsec_t 
*fsec);
  
! extern int    interval2tm(Interval span, struct pg_tm * tm, fsec_t *fsec);
! extern int    tm2interval(struct pg_tm * tm, fsec_t fsec, Interval *span);
  
  extern Timestamp SetEpochTimestamp(void);
! extern void GetEpochTime(struct pg_tm * tm);
  
  extern int    timestamp_cmp_internal(Timestamp dt1, Timestamp dt2);
  
--- 270,285 ----
  
  extern TimestampTz GetCurrentTimestamp(void);
  
! extern int    tm2timestamp(struct pg_tm *tm, fsec_t fsec, int *tzp, Timestamp 
*dt);
! extern int timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm,
                         fsec_t *fsec, char **tzn, pg_tz *attimezone);
  extern void dt2time(Timestamp dt, int *hour, int *min, int *sec, fsec_t 
*fsec);
  
! extern int    interval2tm(Interval span, struct pg_tm *tm, fsec_t *fsec);
! extern int    tm2interval(struct pg_tm *tm, fsec_t fsec, Interval *span);
  
  extern Timestamp SetEpochTimestamp(void);
! extern void GetEpochTime(struct pg_tm *tm);
  
  extern int    timestamp_cmp_internal(Timestamp dt1, Timestamp dt2);
  
Index: src/interfaces/ecpg/pgtypeslib/interval.c
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/ecpg/pgtypeslib/interval.c,v
retrieving revision 1.23
diff -c -c -r1.23 interval.c
*** src/interfaces/ecpg/pgtypeslib/interval.c   12 Jul 2005 16:05:12 -0000      
1.23
--- src/interfaces/ecpg/pgtypeslib/interval.c   20 Jul 2005 03:56:29 -0000
***************
*** 33,39 ****
   *    can be used to represent time spans.
   */
  static int
! DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, fsec_t *fsec)
  {
        char       *cp;
  
--- 33,39 ----
   *    can be used to represent time spans.
   */
  static int
! DecodeTime(char *str, int fmask, int *tmask, struct tm *tm, fsec_t *fsec)
  {
        char       *cp;
  
***************
*** 107,113 ****
   *    preceding an hh:mm:ss field. - thomas 1998-04-30
   */
  int
! DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, 
fsec_t *fsec)
  {
        int                     is_before = FALSE;
  
--- 107,113 ----
   *    preceding an hh:mm:ss field. - thomas 1998-04-30
   */
  int
! DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm *tm, 
fsec_t *fsec)
  {
        int                     is_before = FALSE;
  
***************
*** 445,451 ****
   * - thomas 1998-04-30
   */
  int
! EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str)
  {
        int                     is_before = FALSE;
        int                     is_nonzero = FALSE;
--- 445,451 ----
   * - thomas 1998-04-30
   */
  int
! EncodeInterval(struct tm *tm, fsec_t fsec, int style, char *str)
  {
        int                     is_before = FALSE;
        int                     is_nonzero = FALSE;
***************
*** 670,676 ****
   * Convert a interval data type to a tm structure.
   */
  static int
! interval2tm(interval span, struct tm * tm, fsec_t *fsec)
  {
  #ifdef HAVE_INT64_TIMESTAMP
        int64           time;
--- 670,676 ----
   * Convert a interval data type to a tm structure.
   */
  static int
! interval2tm(interval span, struct tm *tm, fsec_t *fsec)
  {
  #ifdef HAVE_INT64_TIMESTAMP
        int64           time;
***************
*** 713,719 ****
  }     /* interval2tm() */
  
  static int
! tm2interval(struct tm * tm, fsec_t fsec, interval *span)
  {
        span->month = tm->tm_year * 12 + tm->tm_mon;
  #ifdef HAVE_INT64_TIMESTAMP
--- 713,719 ----
  }     /* interval2tm() */
  
  static int
! tm2interval(struct tm *tm, fsec_t fsec, interval *span)
  {
        span->month = tm->tm_year * 12 + tm->tm_mon;
  #ifdef HAVE_INT64_TIMESTAMP
Index: src/test/regress/expected/interval.out
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/expected/interval.out,v
retrieving revision 1.11
diff -c -c -r1.11 interval.out
*** src/test/regress/expected/interval.out      26 May 2005 02:04:14 -0000      
1.11
--- src/test/regress/expected/interval.out      20 Jul 2005 03:56:29 -0000
***************
*** 28,48 ****
  (1 row)
  
  SELECT INTERVAL '-1 +02:03' AS "22 hours ago...";
!  22 hours ago... 
! -----------------
!  -21:57:00
  (1 row)
  
  SELECT INTERVAL '-1 days +02:03' AS "22 hours ago...";
!  22 hours ago... 
! -----------------
!  -21:57:00
  (1 row)
  
  SELECT INTERVAL '10 years -11 month -12 days +13:14' AS "9 years...";
              9 years...            
  ----------------------------------
!  9 years 1 mon -11 days -10:46:00
  (1 row)
  
  CREATE TABLE INTERVAL_TBL (f1 interval);
--- 28,48 ----
  (1 row)
  
  SELECT INTERVAL '-1 +02:03' AS "22 hours ago...";
!   22 hours ago...  
! -------------------
!  -1 days +02:03:00
  (1 row)
  
  SELECT INTERVAL '-1 days +02:03' AS "22 hours ago...";
!   22 hours ago...  
! -------------------
!  -1 days +02:03:00
  (1 row)
  
  SELECT INTERVAL '10 years -11 month -12 days +13:14' AS "9 years...";
              9 years...            
  ----------------------------------
!  9 years 1 mon -12 days +13:14:00
  (1 row)
  
  CREATE TABLE INTERVAL_TBL (f1 interval);
---------------------------(end of broadcast)---------------------------
TIP 1: if posting/reading through Usenet, please send an appropriate
       subscribe-nomail command to [EMAIL PROTECTED] so that your
       message can get through to the mailing list cleanly

Reply via email to