Re: Interval->day patch - Mailing list pgsql-patches

From Bruce Momjian
Subject Re: Interval->day patch
Date
Msg-id 200507201723.j6KHNm515081@candle.pha.pa.us
Whole thread Raw
In response to Interval->day patch  (Michael Glaesemann <grzm@myrealbox.com>)
Responses Re: Interval->day patch
List pgsql-patches
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 majordomo@postgresql.org 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, ×tamp) !=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, ×tamp) !=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_
"selectcast(($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 -
dx 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 -
dx 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 -
dx 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 -
dx 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);

pgsql-patches by date:

Previous
From: Tom Lane
Date:
Subject: Re: Writing Commit Status hint bits (was Re: [HACKERS] Constant WAL replay)
Next
From: Tom Lane
Date:
Subject: Re: Interval->day patch