mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-09 02:49:24 +02:00
Here are patches which should help fix timezone problems in the datetime and abstime code. Also, I repatched varlena.c to add in some comments and a little error checking on top of Vadim's earlier repairs. There are slight mods to the circle data type to have the distance operator between circles measure the distance between closest points rather than between centers.
575 lines
13 KiB
C
575 lines
13 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* nabstime.c--
|
|
* parse almost any absolute date getdate(3) can (& some it can't)
|
|
*
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.25 1997/04/25 18:40:33 scrappy Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "postgres.h"
|
|
#include <miscadmin.h>
|
|
#ifdef HAVE_FLOAT_H
|
|
# include <float.h>
|
|
#endif
|
|
#ifdef HAVE_LIMITS_H
|
|
# include <limits.h>
|
|
#endif
|
|
#ifndef USE_POSIX_TIME
|
|
#include <sys/timeb.h>
|
|
#endif
|
|
#include "utils/builtins.h"
|
|
#include "access/xact.h"
|
|
|
|
|
|
#define MIN_DAYNUM -24856 /* December 13, 1901 */
|
|
#define MAX_DAYNUM 24854 /* January 18, 2038 */
|
|
|
|
|
|
/* GetCurrentAbsoluteTime()
|
|
* Get the current system time. Set timezone parameters if not specified elsewhere.
|
|
* Define HasTZSet to allow clients to specify the default timezone.
|
|
*
|
|
* Returns the number of seconds since epoch (January 1 1970 GMT)
|
|
*/
|
|
AbsoluteTime
|
|
GetCurrentAbsoluteTime(void)
|
|
{
|
|
time_t now;
|
|
|
|
#ifdef USE_POSIX_TIME
|
|
struct tm *tm;
|
|
|
|
now = time(NULL);
|
|
#else /* ! USE_POSIX_TIME */
|
|
struct timeb tb; /* the old V7-ism */
|
|
|
|
(void) ftime(&tb);
|
|
now = tb.time;
|
|
#endif
|
|
|
|
if (! HasCTZSet) {
|
|
#ifdef USE_POSIX_TIME
|
|
#if defined(HAVE_TZSET) && defined(HAVE_INT_TIMEZONE)
|
|
tm = localtime(&now);
|
|
|
|
CDayLight = tm->tm_isdst;
|
|
CTimeZone = (tm->tm_isdst? (timezone - 3600): timezone);
|
|
strcpy( CTZName, tzname[tm->tm_isdst]);
|
|
#else /* !HAVE_TZSET */
|
|
tm = localtime(&now);
|
|
|
|
CTimeZone = - tm->tm_gmtoff; /* tm_gmtoff is Sun/DEC-ism */
|
|
CDayLight = (tm->tm_isdst > 0);
|
|
/* XXX is there a better way to get local timezone string w/o tzname? - tgl 97/03/18 */
|
|
strftime( CTZName, MAXTZLEN, "%Z", tm);
|
|
/* XXX FreeBSD man pages indicate that this should work - tgl 97/04/23 */
|
|
strcpy(CTZName, tm->tm_zone);
|
|
#endif
|
|
#else /* ! USE_POSIX_TIME */
|
|
CTimeZone = tb.timezone * 60;
|
|
CDayLight = (tb.dstflag != 0);
|
|
/* XXX does this work to get the local timezone string in V7? - tgl 97/03/18 */
|
|
strftime( CTZName, MAXTZLEN, "%Z", localtime(&now));
|
|
#endif
|
|
};
|
|
|
|
#ifdef DATEDEBUG
|
|
printf( "GetCurrentAbsoluteTime- timezone is %s -> %d seconds from UTC\n",
|
|
CTZName, CTimeZone);
|
|
#endif
|
|
|
|
return((AbsoluteTime) now);
|
|
} /* GetCurrentAbsoluteTime() */
|
|
|
|
|
|
void
|
|
GetCurrentTime(struct tm *tm)
|
|
{
|
|
int tz;
|
|
|
|
abstime2tm( GetCurrentTransactionStartTime(), &tz, tm);
|
|
|
|
return;
|
|
} /* GetCurrentTime() */
|
|
|
|
|
|
void
|
|
abstime2tm(AbsoluteTime time, int *tzp, struct tm *tm)
|
|
{
|
|
struct tm *tt;
|
|
|
|
#if FALSE
|
|
if (tzp != NULL) time -= *tzp;
|
|
tt = gmtime((time_t *) &time);
|
|
#endif
|
|
/* XXX HACK to get time behavior compatible with Postgres v6.0 - tgl 97/04/07 */
|
|
if (tzp != NULL) {
|
|
tt = localtime((time_t *) &time);
|
|
} else {
|
|
tt = gmtime((time_t *) &time);
|
|
};
|
|
|
|
tm->tm_year = tt->tm_year+1900;
|
|
tm->tm_mon = tt->tm_mon+1;
|
|
tm->tm_mday = tt->tm_mday;
|
|
tm->tm_hour = tt->tm_hour;
|
|
tm->tm_min = tt->tm_min;
|
|
tm->tm_sec = tt->tm_sec;
|
|
tm->tm_isdst = tt->tm_isdst;
|
|
|
|
return;
|
|
} /* abstime2tm() */
|
|
|
|
|
|
/* tm2abstime()
|
|
* Convert a tm structure to abstime.
|
|
* Note that tm has full year (not 1900-based) and 1-based month.
|
|
*/
|
|
AbsoluteTime
|
|
tm2abstime( struct tm *tm, int tz)
|
|
{
|
|
int day, sec;
|
|
|
|
/* validate, before going out of range on some members */
|
|
if (tm->tm_year < 1901 || tm->tm_year > 2038
|
|
|| tm->tm_mon < 1 || tm->tm_mon > 12
|
|
|| tm->tm_mday < 1 || tm->tm_mday > 31
|
|
|| tm->tm_hour < 0 || tm->tm_hour >= 24
|
|
|| tm->tm_min < 0 || tm->tm_min > 59
|
|
|| tm->tm_sec < 0 || tm->tm_sec > 59)
|
|
return(INVALID_ABSTIME);
|
|
|
|
day = (date2j( tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j( 1970, 1, 1));
|
|
|
|
/* check for time out of range */
|
|
if ((day < MIN_DAYNUM) || (day > MAX_DAYNUM))
|
|
return(INVALID_ABSTIME);
|
|
|
|
/* convert to seconds */
|
|
sec = tm->tm_sec + tz + (tm->tm_min +(day*24 + tm->tm_hour)*60)*60;
|
|
|
|
/* check for overflow */
|
|
if ((day == MAX_DAYNUM && sec < 0) ||
|
|
(day == MIN_DAYNUM && sec > 0))
|
|
return(INVALID_ABSTIME);
|
|
|
|
#if FALSE
|
|
/* daylight correction */
|
|
if (tm->tm_isdst < 0) { /* unknown; find out */
|
|
tm->tm_isdst = (CDayLight > 0);
|
|
};
|
|
if (tm->tm_isdst > 0)
|
|
sec -= 60*60;
|
|
#endif
|
|
|
|
/* check for reserved values (e.g. "current" on edge of usual range */
|
|
if (!AbsoluteTimeIsReal(sec))
|
|
return(INVALID_ABSTIME);
|
|
|
|
return(sec);
|
|
} /* tm2abstime() */
|
|
|
|
|
|
/* nabstimein()
|
|
* Decode date/time string and return abstime.
|
|
*/
|
|
AbsoluteTime
|
|
nabstimein(char* str)
|
|
{
|
|
AbsoluteTime result;
|
|
|
|
double fsec;
|
|
int tz = 0;
|
|
struct tm date, *tm = &date;
|
|
|
|
char *field[MAXDATEFIELDS];
|
|
char lowstr[MAXDATELEN+1];
|
|
int dtype;
|
|
int nf, ftype[MAXDATEFIELDS];
|
|
|
|
if (!PointerIsValid(str))
|
|
elog(WARN,"Bad (null) abstime external representation",NULL);
|
|
|
|
if (strlen(str) > MAXDATELEN)
|
|
elog( WARN, "Bad (length) abstime external representation '%s'",str);
|
|
|
|
if ((ParseDateTime( str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|
|
|| (DecodeDateTime( field, ftype, nf, &dtype, tm, &fsec, &tz) != 0))
|
|
elog( WARN, "Bad abstime external representation '%s'",str);
|
|
|
|
#ifdef DATEDEBUG
|
|
printf( "nabstimein- %d fields are type %d (DTK_DATE=%d)\n", nf, dtype, DTK_DATE);
|
|
#endif
|
|
|
|
switch (dtype) {
|
|
case DTK_DATE:
|
|
result = tm2abstime(tm, tz);
|
|
break;
|
|
|
|
case DTK_EPOCH:
|
|
result = EPOCH_ABSTIME;
|
|
break;
|
|
|
|
case DTK_CURRENT:
|
|
result = CURRENT_ABSTIME;
|
|
break;
|
|
|
|
case DTK_LATE:
|
|
result = NOEND_ABSTIME;
|
|
break;
|
|
|
|
case DTK_EARLY:
|
|
result = NOSTART_ABSTIME;
|
|
break;
|
|
|
|
case DTK_INVALID:
|
|
result = INVALID_ABSTIME;
|
|
break;
|
|
|
|
default:
|
|
elog(WARN,"Bad abstime (internal coding error) '%s'",str);
|
|
result = INVALID_ABSTIME;
|
|
break;
|
|
};
|
|
|
|
return result;
|
|
} /* nabstimein() */
|
|
|
|
|
|
/*
|
|
* Given an AbsoluteTime return the English text version of the date
|
|
*/
|
|
char *
|
|
nabstimeout(AbsoluteTime time)
|
|
{
|
|
/*
|
|
* Fri Jan 28 23:05:29 1994 PST
|
|
* 0 1 2
|
|
* 12345678901234567890123456789
|
|
*
|
|
* we allocate some extra -- timezones are usually 3 characters but
|
|
* this is not in the POSIX standard...
|
|
*/
|
|
char buf[40];
|
|
char* result;
|
|
|
|
switch (time) {
|
|
case EPOCH_ABSTIME: (void) strcpy(buf, EPOCH); break;
|
|
case INVALID_ABSTIME: (void) strcpy(buf, INVALID); break;
|
|
case CURRENT_ABSTIME: (void) strcpy(buf, DCURRENT); break;
|
|
case NOEND_ABSTIME: (void) strcpy(buf, LATE); break;
|
|
case NOSTART_ABSTIME: (void) strcpy(buf, EARLY); break;
|
|
default:
|
|
/* hack -- localtime happens to work for negative times */
|
|
(void) strftime(buf, sizeof(buf), "%a %b %d %H:%M:%S %Y %Z",
|
|
localtime((time_t *) &time));
|
|
break;
|
|
}
|
|
result = (char*)palloc(strlen(buf) + 1);
|
|
strcpy(result, buf);
|
|
return result;
|
|
}
|
|
|
|
|
|
/* turn a (struct tm) and a few variables into a time_t, with range checking */
|
|
AbsoluteTime
|
|
dateconv(register struct tm *tm, int zone)
|
|
{
|
|
tm->tm_wday = tm->tm_yday = 0;
|
|
|
|
#if FALSE
|
|
if (tm->tm_year < 70) {
|
|
tm->tm_year += 2000;
|
|
} else if (tm->tm_year < 1000) {
|
|
tm->tm_year += 1900;
|
|
};
|
|
#endif
|
|
|
|
/* validate, before going out of range on some members */
|
|
if (tm->tm_year < 1901 || tm->tm_year > 2038
|
|
|| tm->tm_mon < 1 || tm->tm_mon > 12
|
|
|| tm->tm_mday < 1 || tm->tm_mday > 31
|
|
|| tm->tm_hour < 0 || tm->tm_hour >= 24
|
|
|| tm->tm_min < 0 || tm->tm_min > 59
|
|
|| tm->tm_sec < 0 || tm->tm_sec > 59)
|
|
return INVALID_ABSTIME;
|
|
|
|
/*
|
|
* zone should really be -zone, and tz should be set to tp->value, not
|
|
* -tp->value. Or the table could be fixed.
|
|
*/
|
|
tm->tm_sec += zone; /* mktime lets it be out of range */
|
|
|
|
/* convert to seconds */
|
|
return qmktime(tm);
|
|
}
|
|
|
|
|
|
/*
|
|
* near-ANSI qmktime suitable for use by dateconv; not necessarily as paranoid
|
|
* as ANSI requires, and it may not canonicalise the struct tm. Ignores tm_wday
|
|
* and tm_yday.
|
|
*/
|
|
time_t
|
|
qmktime(struct tm *tm)
|
|
{
|
|
time_t sec;
|
|
|
|
int day;
|
|
|
|
#if FALSE
|
|
/* If it was a 2 digit year */
|
|
if (tm->tm_year < 100)
|
|
tm->tm_year += 1900;
|
|
#endif
|
|
|
|
day = (date2j( tm->tm_year, tm->tm_mon, tm->tm_mday) - date2j( 1970, 1, 1));
|
|
|
|
/* check for time out of range */
|
|
if ((day < MIN_DAYNUM) || (day > MAX_DAYNUM))
|
|
return INVALID_ABSTIME;
|
|
|
|
/* convert to seconds */
|
|
sec = tm->tm_sec + (tm->tm_min +(day*24 + tm->tm_hour)*60)*60;
|
|
|
|
/* check for overflow */
|
|
if ((day == MAX_DAYNUM && sec < 0) ||
|
|
(day == MIN_DAYNUM && sec > 0))
|
|
return INVALID_ABSTIME;
|
|
|
|
/* check for reserved values (e.g. "current" on edge of usual range */
|
|
if (!AbsoluteTimeIsReal(sec))
|
|
return INVALID_ABSTIME;
|
|
|
|
/* daylight correction */
|
|
if (tm->tm_isdst < 0) { /* unknown; find out */
|
|
tm->tm_isdst = (CDayLight > 0);
|
|
};
|
|
if (tm->tm_isdst > 0)
|
|
sec -= 60*60;
|
|
|
|
return sec;
|
|
} /* qmktime() */
|
|
|
|
|
|
/*
|
|
* AbsoluteTimeIsBefore -- true iff time1 is before time2.
|
|
* AbsoluteTimeIsBefore -- true iff time1 is after time2.
|
|
*/
|
|
bool
|
|
AbsoluteTimeIsBefore(AbsoluteTime time1, AbsoluteTime time2)
|
|
{
|
|
Assert(AbsoluteTimeIsValid(time1));
|
|
Assert(AbsoluteTimeIsValid(time2));
|
|
|
|
if (time1 == CURRENT_ABSTIME)
|
|
time1 = GetCurrentTransactionStartTime();
|
|
|
|
if (time2 == CURRENT_ABSTIME)
|
|
time2 = GetCurrentTransactionStartTime();
|
|
|
|
return (time1 < time2);
|
|
}
|
|
|
|
bool
|
|
AbsoluteTimeIsAfter(AbsoluteTime time1, AbsoluteTime time2)
|
|
{
|
|
Assert(AbsoluteTimeIsValid(time1));
|
|
Assert(AbsoluteTimeIsValid(time2));
|
|
|
|
if (time1 == CURRENT_ABSTIME)
|
|
time1 = GetCurrentTransactionStartTime();
|
|
|
|
if (time2 == CURRENT_ABSTIME)
|
|
time2 = GetCurrentTransactionStartTime();
|
|
|
|
return (time1 > time2);
|
|
}
|
|
|
|
|
|
/* abstime_finite()
|
|
*/
|
|
bool
|
|
abstime_finite(AbsoluteTime abstime)
|
|
{
|
|
return((abstime != INVALID_ABSTIME)
|
|
&& (abstime != NOSTART_ABSTIME) && (abstime != NOEND_ABSTIME));
|
|
} /* abstime_datetime() */
|
|
|
|
|
|
/*
|
|
* abstimeeq - returns 1, iff arguments are equal
|
|
* abstimene - returns 1, iff arguments are not equal
|
|
* abstimelt - returns 1, iff t1 less than t2
|
|
* abstimegt - returns 1, iff t1 greater than t2
|
|
* abstimele - returns 1, iff t1 less than or equal to t2
|
|
* abstimege - returns 1, iff t1 greater than or equal to t2
|
|
*/
|
|
bool
|
|
abstimeeq(AbsoluteTime t1, AbsoluteTime t2)
|
|
{
|
|
if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
|
|
return(FALSE);
|
|
if (t1 == CURRENT_ABSTIME)
|
|
t1 = GetCurrentTransactionStartTime();
|
|
if (t2 == CURRENT_ABSTIME)
|
|
t2 = GetCurrentTransactionStartTime();
|
|
|
|
return(t1 == t2);
|
|
}
|
|
|
|
bool
|
|
abstimene(AbsoluteTime t1, AbsoluteTime t2)
|
|
{
|
|
if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
|
|
return(FALSE);
|
|
if (t1 == CURRENT_ABSTIME)
|
|
t1 = GetCurrentTransactionStartTime();
|
|
if (t2 == CURRENT_ABSTIME)
|
|
t2 = GetCurrentTransactionStartTime();
|
|
|
|
return(t1 != t2);
|
|
}
|
|
|
|
bool
|
|
abstimelt(AbsoluteTime t1, AbsoluteTime t2)
|
|
{
|
|
if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
|
|
return(FALSE);
|
|
if (t1 == CURRENT_ABSTIME)
|
|
t1 = GetCurrentTransactionStartTime();
|
|
if (t2 == CURRENT_ABSTIME)
|
|
t2 = GetCurrentTransactionStartTime();
|
|
|
|
return(t1 < t2);
|
|
}
|
|
|
|
bool
|
|
abstimegt(AbsoluteTime t1, AbsoluteTime t2)
|
|
{
|
|
if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
|
|
return(FALSE);
|
|
if (t1 == CURRENT_ABSTIME)
|
|
t1 = GetCurrentTransactionStartTime();
|
|
if (t2 == CURRENT_ABSTIME)
|
|
t2 = GetCurrentTransactionStartTime();
|
|
|
|
return(t1 > t2);
|
|
}
|
|
|
|
bool
|
|
abstimele(AbsoluteTime t1, AbsoluteTime t2)
|
|
{
|
|
if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
|
|
return(FALSE);
|
|
if (t1 == CURRENT_ABSTIME)
|
|
t1 = GetCurrentTransactionStartTime();
|
|
if (t2 == CURRENT_ABSTIME)
|
|
t2 = GetCurrentTransactionStartTime();
|
|
|
|
return(t1 <= t2);
|
|
}
|
|
|
|
bool
|
|
abstimege(AbsoluteTime t1, AbsoluteTime t2)
|
|
{
|
|
if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME)
|
|
return(FALSE);
|
|
if (t1 == CURRENT_ABSTIME)
|
|
t1 = GetCurrentTransactionStartTime();
|
|
if (t2 == CURRENT_ABSTIME)
|
|
t2 = GetCurrentTransactionStartTime();
|
|
|
|
return(t1 >= t2);
|
|
}
|
|
|
|
|
|
/* datetime_abstime()
|
|
* Convert datetime to abstime.
|
|
*/
|
|
AbsoluteTime
|
|
datetime_abstime(DateTime *datetime)
|
|
{
|
|
AbsoluteTime result;
|
|
|
|
double fsec;
|
|
struct tm tt, *tm = &tt;
|
|
|
|
if (!PointerIsValid(datetime)) {
|
|
result = INVALID_ABSTIME;
|
|
|
|
} else if (DATETIME_IS_INVALID(*datetime)) {
|
|
result = INVALID_ABSTIME;
|
|
|
|
} else if (DATETIME_IS_NOBEGIN(*datetime)) {
|
|
result = NOSTART_ABSTIME;
|
|
|
|
} else if (DATETIME_IS_NOEND(*datetime)) {
|
|
result = NOEND_ABSTIME;
|
|
|
|
} else {
|
|
if (DATETIME_IS_RELATIVE(*datetime)) {
|
|
datetime2tm( SetDateTime(*datetime), NULL, tm, &fsec);
|
|
result = tm2abstime( tm, 0);
|
|
|
|
} else if (datetime2tm( *datetime, NULL, tm, &fsec) == 0) {
|
|
result = tm2abstime( tm, 0);
|
|
|
|
} else {
|
|
result = INVALID_ABSTIME;
|
|
};
|
|
};
|
|
|
|
return(result);
|
|
} /* datetime_abstime() */
|
|
|
|
/* abstime_datetime()
|
|
* Convert datetime to abstime.
|
|
*/
|
|
DateTime *
|
|
abstime_datetime(AbsoluteTime abstime)
|
|
{
|
|
DateTime *result;
|
|
|
|
if (!PointerIsValid(result = PALLOCTYPE(DateTime)))
|
|
elog(WARN,"Unable to allocate space to convert abstime to datetime",NULL);
|
|
|
|
switch (abstime) {
|
|
case INVALID_ABSTIME:
|
|
DATETIME_INVALID(*result);
|
|
break;
|
|
|
|
case NOSTART_ABSTIME:
|
|
DATETIME_NOBEGIN(*result);
|
|
break;
|
|
|
|
case NOEND_ABSTIME:
|
|
DATETIME_NOEND(*result);
|
|
break;
|
|
|
|
case EPOCH_ABSTIME:
|
|
DATETIME_EPOCH(*result);
|
|
break;
|
|
|
|
case CURRENT_ABSTIME:
|
|
DATETIME_CURRENT(*result);
|
|
break;
|
|
|
|
default:
|
|
*result = abstime + ((date2j( 1970, 1, 1) - date2j( 2000, 1, 1))*86400);
|
|
break;
|
|
};
|
|
|
|
return(result);
|
|
} /* abstime_datetime() */
|