]> granicus.if.org Git - postgresql/commitdiff
Fix leap year and month boundary arithmetic for datetime +/- timespan;
authorThomas G. Lockhart <lockhart@fourpalms.org>
Fri, 20 Jun 1997 17:12:54 +0000 (17:12 +0000)
committerThomas G. Lockhart <lockhart@fourpalms.org>
Fri, 20 Jun 1997 17:12:54 +0000 (17:12 +0000)
 the problem only manifests itself when adding years/months and hours
 when the hours:minutes:seconds pushes over midnight.
Fix interpretation of times with explicit timezone when the timezone is
 in daylight savings time and is not the default timezone.
Allow interpretation of explicit timezone when it is specified as two words:
 <standard time> DST". For example, "MET DST" (Middle European Time Daylight
 Savings Time). This syntax is found in the zic package on Linux boxes at least.

src/backend/utils/adt/dt.c

index 542246a52b9d9ac88d9a9c03326b93e289a7a741..79e83266563f57f13e995fabf63232253d5a83a2 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *    $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/dt.c,v 1.24 1997/06/03 06:32:56 vadim Exp $
+ *    $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/dt.c,v 1.25 1997/06/20 17:12:54 thomas Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,7 +48,23 @@ char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
  * Macro to replace modf(), which is broken on some platforms.
  */
 #define TMODULO(t,q,u) {q = ((t < 0)? ceil(t / u): floor(t / u)); \
-                      if (q != 0) t -= rint(q * u);}
+                       if (q != 0) t -= rint(q * u);}
+
+void GetEpochTime( struct tm *tm);
+
+#define UTIME_MINYEAR (1901)
+#define UTIME_MINMONTH (12)
+#define UTIME_MINDAY (14)
+#define UTIME_MAXYEAR (2038)
+#define UTIME_MAXMONTH (01)
+#define UTIME_MAXDAY (18)
+
+#define IS_VALID_UTIME(y,m,d) (((y > UTIME_MINYEAR) \
+ || ((y == UTIME_MINYEAR) && ((m > UTIME_MINMONTH) \
+  || ((m == UTIME_MINMONTH) && (d >= UTIME_MINDAY))))) \
+ && ((y < UTIME_MAXYEAR) \
+ || ((y == UTIME_MAXYEAR) && ((m < UTIME_MAXMONTH) \
+  || ((m == UTIME_MAXMONTH) && (d <= UTIME_MAXDAY))))))
 
 
 /***************************************************************************** 
@@ -79,8 +95,7 @@ datetime_in(char *str)
       || (DecodeDateTime( field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
        elog(WARN,"Bad datetime external representation %s",str);
 
-    if (!PointerIsValid(result = PALLOCTYPE(DateTime)))
-       elog(WARN,"Memory allocation failed, can't input datetime '%s'",str);
+    result = PALLOCTYPE(DateTime);
 
     switch (dtype) {
     case DTK_DATE:
@@ -146,8 +161,7 @@ datetime_out(DateTime *dt)
        EncodeSpecialDateTime(DT_INVALID, buf);
     };
 
-    if (!PointerIsValid(result = PALLOC(strlen(buf)+1)))
-       elog(WARN,"Memory allocation failed, can't output datetime",NULL);
+    result = PALLOC(strlen(buf)+1);
 
     strcpy( result, buf);
 
@@ -189,8 +203,7 @@ timespan_in(char *str)
       || (DecodeDateDelta( field, ftype, nf, &dtype, tm, &fsec) != 0))
        elog(WARN,"Bad timespan external representation '%s'",str);
 
-    if (!PointerIsValid(span = PALLOCTYPE(TimeSpan)))
-       elog(WARN,"Memory allocation failed, can't input timespan '%s'",str);
+    span = PALLOCTYPE(TimeSpan);
 
     switch (dtype) {
     case DTK_DELTA:
@@ -230,8 +243,7 @@ timespan_out(TimeSpan *span)
     if (EncodeTimeSpan(tm, fsec, DateStyle, buf) != 0)
          elog(WARN,"Unable to format timespan",NULL);
 
-    if (!PointerIsValid(result = PALLOC(strlen(buf)+1)))
-       elog(WARN,"Memory allocation failed, can't output timespan",NULL);
+    result = PALLOC(strlen(buf)+1);
 
     strcpy( result, buf);
     return( result);
@@ -267,8 +279,6 @@ timespan_finite(TimeSpan *timespan)
  *  Relational operators for datetime.
  *---------------------------------------------------------*/
 
-void GetEpochTime( struct tm *tm);
-
 void
 GetEpochTime( struct tm *tm)
 {
@@ -556,7 +566,8 @@ timespan_ge(TimeSpan *timespan1, TimeSpan *timespan2)
  *                     actual value.
  *---------------------------------------------------------*/
 
-DateTime *datetime_smaller(DateTime *datetime1, DateTime *datetime2)
+DateTime *
+datetime_smaller(DateTime *datetime1, DateTime *datetime2)
 {
     DateTime *result;
 
@@ -568,8 +579,7 @@ DateTime *datetime_smaller(DateTime *datetime1, DateTime *datetime2)
     dt1 = *datetime1;
     dt2 = *datetime2;
 
-    if (!PointerIsValid(result = PALLOCTYPE(DateTime)))
-       elog(WARN,"Memory allocation failed, can't find smaller date",NULL);
+    result = PALLOCTYPE(DateTime);
 
     if (DATETIME_IS_RELATIVE(dt1)) dt1 = SetDateTime(dt1);
     if (DATETIME_IS_RELATIVE(dt2)) dt2 = SetDateTime(dt2);
@@ -585,7 +595,8 @@ DateTime *datetime_smaller(DateTime *datetime1, DateTime *datetime2)
     return(result);
 } /* datetime_smaller() */
 
-DateTime *datetime_larger(DateTime *datetime1, DateTime *datetime2)
+DateTime *
+datetime_larger(DateTime *datetime1, DateTime *datetime2)
 {
     DateTime *result;
 
@@ -597,8 +608,7 @@ DateTime *datetime_larger(DateTime *datetime1, DateTime *datetime2)
     dt1 = *datetime1;
     dt2 = *datetime2;
 
-    if (!PointerIsValid(result = PALLOCTYPE(DateTime)))
-       elog(WARN,"Memory allocation failed, can't find larger date",NULL);
+    result = PALLOCTYPE(DateTime);
 
     if (DATETIME_IS_RELATIVE(dt1)) dt1 = SetDateTime(dt1);
     if (DATETIME_IS_RELATIVE(dt2)) dt2 = SetDateTime(dt2);
@@ -615,7 +625,8 @@ DateTime *datetime_larger(DateTime *datetime1, DateTime *datetime2)
 } /* datetime_larger() */
 
 
-TimeSpan *datetime_sub(DateTime *datetime1, DateTime *datetime2)
+TimeSpan *
+datetime_sub(DateTime *datetime1, DateTime *datetime2)
 {
     TimeSpan *result;
 
@@ -627,8 +638,7 @@ TimeSpan *datetime_sub(DateTime *datetime1, DateTime *datetime2)
     dt1 = *datetime1;
     dt2 = *datetime2;
 
-    if (!PointerIsValid(result = PALLOCTYPE(TimeSpan)))
-       elog(WARN,"Memory allocation failed, can't subtract dates",NULL);
+    result = PALLOCTYPE(TimeSpan);
 
     if (DATETIME_IS_RELATIVE(dt1)) dt1 = SetDateTime(dt1);
     if (DATETIME_IS_RELATIVE(dt2)) dt2 = SetDateTime(dt2);
@@ -654,15 +664,18 @@ TimeSpan *datetime_sub(DateTime *datetime1, DateTime *datetime2)
  * Then, if the next month has fewer days, set the day of month
  *  to the last day of month.
  */
-DateTime *datetime_add_span(DateTime *datetime, TimeSpan *span)
+DateTime *
+datetime_add_span(DateTime *datetime, TimeSpan *span)
 {
     DateTime *result;
+    DateTime dt;
+    int tz;
+    char *tzn;
 
     if ((!PointerIsValid(datetime)) || (!PointerIsValid(span)))
        return NULL;
 
-    if (!PointerIsValid(result = PALLOCTYPE(DateTime)))
-       elog(WARN,"Memory allocation failed, can't add dates",NULL);
+    result = PALLOCTYPE(DateTime);
 
 #ifdef DATEDEBUG
 printf( "datetime_add_span- add %f to %d %f\n", *datetime, span->month, span->time);
@@ -675,13 +688,19 @@ printf( "datetime_add_span- add %f to %d %f\n", *datetime, span->month, span->ti
        DATETIME_INVALID(*result);
 
     } else {
-       *result = (DATETIME_IS_RELATIVE(*datetime)? SetDateTime(*datetime): *datetime);
+       dt = (DATETIME_IS_RELATIVE(*datetime)? SetDateTime(*datetime): *datetime);
+
+#ifdef ROUND_ALL
+       dt = JROUND(dt + span->time);
+#else
+       dt = span->time;
+#endif
 
        if (span->month != 0) {
            struct tm tt, *tm = &tt;
            double fsec;
 
-           if (datetime2tm( *result, NULL, tm, &fsec, NULL) == 0) {
+           if (datetime2tm( dt, &tz, tm, &fsec, &tzn) == 0) {
 #ifdef DATEDEBUG
 printf( "datetime_add_span- date was %d.%02d.%02d\n", tm->tm_year, tm->tm_mon, tm->tm_mday);
 #endif
@@ -706,26 +725,22 @@ printf( "datetime_add_span- date was %d.%02d.%02d\n", tm->tm_year, tm->tm_mon, t
 #ifdef DATEDEBUG
 printf( "datetime_add_span- date becomes %d.%02d.%02d\n", tm->tm_year, tm->tm_mon, tm->tm_mday);
 #endif
-               if (tm2datetime( tm, fsec, NULL, result) != 0)
+               if (tm2datetime( tm, fsec, &tz, &dt) != 0)
                    elog(WARN,"Unable to add datetime and timespan",NULL);
 
-
            } else {
                DATETIME_INVALID(*result);
            };
        };
 
-#ifdef ROUND_ALL
-       *result = JROUND(*result + span->time);
-#else
-       *result += span->time;
-#endif
+       *result = dt;
     };
 
     return(result);
 } /* datetime_add_span() */
 
-DateTime *datetime_sub_span(DateTime *datetime, TimeSpan *span)
+DateTime *
+datetime_sub_span(DateTime *datetime, TimeSpan *span)
 {
     DateTime *result;
     TimeSpan tspan;
@@ -742,15 +757,15 @@ DateTime *datetime_sub_span(DateTime *datetime, TimeSpan *span)
 } /* datetime_sub_span() */
 
 
-TimeSpan *timespan_um(TimeSpan *timespan)
+TimeSpan *
+timespan_um(TimeSpan *timespan)
 {
     TimeSpan *result;
 
     if (!PointerIsValid(timespan))
        return NULL;
 
-    if (!PointerIsValid(result = PALLOCTYPE(TimeSpan)))
-       elog(WARN,"Memory allocation failed, can't subtract dates",NULL);
+    result = PALLOCTYPE(TimeSpan);
 
     result->time = -(timespan->time);
     result->month = -(timespan->month);
@@ -759,7 +774,8 @@ TimeSpan *timespan_um(TimeSpan *timespan)
 } /* timespan_um() */
 
 
-TimeSpan *timespan_smaller(TimeSpan *timespan1, TimeSpan *timespan2)
+TimeSpan *
+timespan_smaller(TimeSpan *timespan1, TimeSpan *timespan2)
 {
     TimeSpan *result;
 
@@ -768,8 +784,7 @@ TimeSpan *timespan_smaller(TimeSpan *timespan1, TimeSpan *timespan2)
     if (!PointerIsValid(timespan1) || !PointerIsValid(timespan2))
        return NULL;
 
-    if (!PointerIsValid(result = PALLOCTYPE(TimeSpan)))
-       elog(WARN,"Memory allocation failed, can't find smaller timespan",NULL);
+    result = PALLOCTYPE(TimeSpan);
 
     if (TIMESPAN_IS_INVALID(*timespan1)) {
        result->time = timespan2->time;
@@ -803,7 +818,8 @@ printf( "timespan_smaller- months %d %d times %f %f spans %f %f\n",
     return(result);
 } /* timespan_smaller() */
 
-TimeSpan *timespan_larger(TimeSpan *timespan1, TimeSpan *timespan2)
+TimeSpan *
+timespan_larger(TimeSpan *timespan1, TimeSpan *timespan2)
 {
     TimeSpan *result;
 
@@ -812,8 +828,7 @@ TimeSpan *timespan_larger(TimeSpan *timespan1, TimeSpan *timespan2)
     if (!PointerIsValid(timespan1) || !PointerIsValid(timespan2))
        return NULL;
 
-    if (!PointerIsValid(result = PALLOCTYPE(TimeSpan)))
-       elog(WARN,"Memory allocation failed, can't find larger timespan",NULL);
+    result = PALLOCTYPE(TimeSpan);
 
     if (TIMESPAN_IS_INVALID(*timespan1)) {
        result->time = timespan2->time;
@@ -848,15 +863,15 @@ printf( "timespan_larger- months %d %d times %f %f spans %f %f\n",
 } /* timespan_larger() */
 
 
-TimeSpan *timespan_add(TimeSpan *span1, TimeSpan *span2)
+TimeSpan *
+timespan_add(TimeSpan *span1, TimeSpan *span2)
 {
     TimeSpan *result;
 
     if ((!PointerIsValid(span1)) || (!PointerIsValid(span2)))
        return NULL;
 
-    if (!PointerIsValid(result = PALLOCTYPE(TimeSpan)))
-       elog(WARN,"Memory allocation failed, can't add timespans",NULL);
+    result = PALLOCTYPE(TimeSpan);
 
     result->month = (span1->month + span2->month);
     result->time = JROUND(span1->time + span2->time);
@@ -864,15 +879,15 @@ TimeSpan *timespan_add(TimeSpan *span1, TimeSpan *span2)
     return(result);
 } /* timespan_add() */
 
-TimeSpan *timespan_sub(TimeSpan *span1, TimeSpan *span2)
+TimeSpan *
+timespan_sub(TimeSpan *span1, TimeSpan *span2)
 {
     TimeSpan *result;
 
     if ((!PointerIsValid(span1)) || (!PointerIsValid(span2)))
        return NULL;
 
-    if (!PointerIsValid(result = PALLOCTYPE(TimeSpan)))
-       elog(WARN,"Memory allocation failed, can't subtract timespans",NULL);
+    result = PALLOCTYPE(TimeSpan);
 
     result->month = (span1->month - span2->month);
     result->time = JROUND(span1->time - span2->time);
@@ -906,8 +921,7 @@ datetime_text(DateTime *datetime)
 
     len = (strlen(str)+VARHDRSZ);
 
-    if (!PointerIsValid(result = PALLOC(len)))
-       elog(WARN,"Memory allocation failed, can't convert datetime to text",NULL);
+    result = PALLOC(len);
 
     VARSIZE(result) = len;
     memmove(VARDATA(result), str, (len-VARHDRSZ));
@@ -964,8 +978,7 @@ timespan_text(TimeSpan *timespan)
 
     len = (strlen(str)+VARHDRSZ);
 
-    if (!PointerIsValid(result = PALLOC(len)))
-       elog(WARN,"Memory allocation failed, can't convert timespan to text",NULL);
+    result = PALLOC(len);
 
     VARSIZE(result) = len;
     memmove(VARDATA(result), str, (len-VARHDRSZ));
@@ -1022,8 +1035,7 @@ datetime_part(text *units, DateTime *datetime)
     if ((!PointerIsValid(units)) || (!PointerIsValid(datetime)))
        return NULL;
 
-    if (!PointerIsValid(result = PALLOCTYPE(float64data)))
-       elog(WARN,"Memory allocation failed, can't get date part",NULL);
+    result = PALLOCTYPE(float64data);
 
     up = VARDATA(units);
     lp = lowunits;
@@ -1031,6 +1043,9 @@ datetime_part(text *units, DateTime *datetime)
     *lp = '\0';
 
     type = DecodeUnits( 0, lowunits, &val);
+    if (type == IGNORE) {
+       type = DecodeSpecial( 0, lowunits, &val);
+    };
 
 #ifdef DATEDEBUG
 if (type == IGNORE) strcpy(lowunits, "(unknown)");
@@ -1039,15 +1054,15 @@ printf( "datetime_part- units %s type=%d value=%d\n", lowunits, type, val);
 
     if (DATETIME_NOT_FINITE(*datetime)) {
 #if FALSE
+/* should return null but Postgres doesn't like that currently. - tgl 97/06/12 */
        elog(WARN,"Datetime is not finite",NULL);
 #endif
        *result = 0;
 
-    } else if (type == UNITS) {
-
+    } else {
        dt = (DATETIME_IS_RELATIVE(*datetime)? SetDateTime(*datetime): *datetime); 
 
-       if (datetime2tm( dt, &tz, tm, &fsec, &tzn) == 0) {
+       if ((type == UNITS) && (datetime2tm( dt, &tz, tm, &fsec, &tzn) == 0)) {
            switch (val) {
            case DTK_TZ:
                *result = tz;
@@ -1102,19 +1117,18 @@ printf( "datetime_part- units %s type=%d value=%d\n", lowunits, type, val);
                break;
 
            default:
-               elog(WARN,"Datetime units %s not supported",units);
+               elog(WARN,"Datetime units %s not supported",lowunits);
                *result = 0;
            };
 
+       } else if ((type == RESERV) && (val == DTK_EPOCH)) {
+           DATETIME_EPOCH(*result);
+           *result = dt - SetDateTime(*result);
+
        } else {
-           elog(NOTICE,"Datetime out of range",NULL);
+           elog(WARN,"Datetime units %s not recognized",lowunits);
            *result = 0;
        };
-
-
-    } else {
-       elog(WARN,"Datetime units %s not recognized",units);
-       *result = 0;
     };
 
     return(result);
@@ -1138,8 +1152,7 @@ timespan_part(text *units, TimeSpan *timespan)
     if ((!PointerIsValid(units)) || (!PointerIsValid(timespan)))
        return NULL;
 
-    if (!PointerIsValid(result = PALLOCTYPE(float64data)))
-       elog(WARN,"Memory allocation failed, can't get date part",NULL);
+    result = PALLOCTYPE(float64data);
 
     up = VARDATA(units);
     lp = lowunits;
@@ -1147,6 +1160,9 @@ timespan_part(text *units, TimeSpan *timespan)
     *lp = '\0';
 
     type = DecodeUnits( 0, lowunits, &val);
+    if (type == IGNORE) {
+       type = DecodeSpecial( 0, lowunits, &val);
+    };
 
 #ifdef DATEDEBUG
 if (type == IGNORE) strcpy(lowunits, "(unknown)");
@@ -1221,6 +1237,12 @@ printf( "timespan_part- units %s type=%d value=%d\n", lowunits, type, val);
            *result = 0;
        };
 
+    } else if ((type == RESERV) && (val == DTK_EPOCH)) {
+       *result = timespan->time;
+       if (timespan->month != 0) {
+           *result += ((365.25*86400)*(timespan->month / 12));
+           *result += ((30*86400)*(timespan->month % 12));
+       };
 
     } else {
        elog(WARN,"Timespan units %s not recognized",units);
@@ -1284,7 +1306,7 @@ static datetkn datetktbl[] = {
 {      "dec",          MONTH,  12},
 {      "december",     MONTH,  12},
 {      "dnt",          TZ,     6},             /* Dansk Normal Tid */
-{      "dst",          IGNORE, 0},
+{      "dst",          DTZMOD, 6},
 {      "east",         TZ,     NEG(60)},       /* East Australian Std Time */
 {      "edt",          DTZ,    NEG(24)},       /* Eastern Daylight Time */
 {      "eet",          TZ,     12},            /* East. Europe, USSR Zone 1 */
@@ -1491,20 +1513,6 @@ datetkn *deltacache[MAXDATEFIELDS] = {NULL};
  *  to avoid having to check year and day both. - tgl 97/05/08
  */
 
-#define UTIME_MINYEAR (1901)
-#define UTIME_MINMONTH (12)
-#define UTIME_MINDAY (14)
-#define UTIME_MAXYEAR (2038)
-#define UTIME_MAXMONTH (01)
-#define UTIME_MAXDAY (18)
-
-#define IS_VALID_UTIME(y,m,d) (((y > UTIME_MINYEAR) \
- || ((y == UTIME_MINYEAR) && ((m > UTIME_MINMONTH) \
-  || ((m == UTIME_MINMONTH) && (d >= UTIME_MINDAY))))) \
- && ((y < UTIME_MAXYEAR) \
- || ((y == UTIME_MAXYEAR) && ((m < UTIME_MAXMONTH) \
-  || ((m == UTIME_MAXMONTH) && (d <= UTIME_MAXDAY))))))
-
 #define JULIAN_MINYEAR (-4713)
 #define JULIAN_MINMONTH (11)
 #define JULIAN_MINDAY (23)
@@ -1522,7 +1530,8 @@ date2j(int y, int m, int d)
      - (3*((y+4900+m12)/100))/4 + d - 32075);
 } /* date2j() */
 
-void j2date( int jd, int *year, int *month, int *day)
+void
+j2date( int jd, int *year, int *month, int *day)
 {
     int j, y, m, d;
 
@@ -1545,7 +1554,8 @@ void j2date( int jd, int *year, int *month, int *day)
     return;
 } /* j2date() */
 
-int j2day( int date)
+int
+j2day( int date)
 {
     int day;
 
@@ -1766,19 +1776,22 @@ printf( "tm2timespan- %d %f = %04d-%02d-%02d %02d:%02d:%02d %.2f\n", span->month
 } /* tm2timespan() */
 
 
-DateTime dt2local(DateTime dt, int tz)
+DateTime
+dt2local(DateTime dt, int tz)
 {
     dt -= tz;
     dt = JROUND(dt);
     return(dt);
 } /* dt2local() */
 
-double time2t(const int hour, const int min, const double sec)
+double
+time2t(const int hour, const int min, const double sec)
 {
     return((((hour*60)+min)*60)+sec);
 } /* time2t() */
 
-void dt2time(DateTime jd, int *hour, int *min, double *sec)
+void
+dt2time(DateTime jd, int *hour, int *min, double *sec)
 {
     double time;
 
@@ -1940,7 +1953,7 @@ DecodeDateTime( char *field[], int ftype[], int nf,
     tm->tm_sec = 0;
     *fsec = 0;
     tm->tm_isdst = -1; /* don't know daylight savings time status apriori */
-    if (tzp != NULL) *tzp = CTimeZone;
+    if (tzp != NULL) *tzp = 0;
 
     for (i = 0; i < nf; i++) {
 #ifdef DATEDEBUG
@@ -1995,6 +2008,7 @@ printf( "DecodeDateTime- RESERV field %s value is %d\n", field[i], val);
                    tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
                    *dtype = DTK_DATE;
                    GetCurrentTime(tm);
+                   if (tzp != NULL) *tzp = CTimeZone;
                    break;
 
                case DTK_YESTERDAY:
@@ -2050,7 +2064,17 @@ printf( "DecodeDateTime- month field %s value is %d\n", field[i], val);
                tm->tm_mon = val;
                break;
 
+           /* daylight savings time modifier (solves "MET DST" syntax) */
+           case DTZMOD:
+               tmask |= DTK_M(DTZ);
+               tm->tm_isdst = 1;
+               if (tzp == NULL) return -1;
+               *tzp += val * 60;
+               break;
+
            case DTZ:
+               /* set mask for TZ here _or_ check for DTZ later when getting default timezone */
+               tmask |= DTK_M(TZ);
                tm->tm_isdst = 1;
                if (tzp == NULL) return -1;
                *tzp = val * 60;
@@ -2628,7 +2652,7 @@ DecodeSpecial(int field, char *lowtoken, int *val)
        *val = 0;
     } else {
        type = tp->type;
-       if ((type == TZ) || (type == DTZ)) {
+       if ((type == TZ) || (type == DTZ) || (type == DTZMOD)) {
            *val = FROMVAL(tp);
        } else {
            *val = tp->value;