From: Thomas Lockhart <Thomas.G.Lockhart@jpl.nasa.gov>
Subject: Re: [HACKERS] abstime "now" broken Yes, I broke 'now' :( with an attempt at a bug fix involving servers running in the UTC/GMT timezone. These patches fix the problem, and have been tested in GMT (+00 hours), PST (-08), and NZT (+12) timezones which exercized the code for various cases including across day boundaries. btw, this code fixes the same type of problem for 'today', 'yesterday', 'tomorrow', for DATETIME, ABSTIME, DATE and TIME types. The bugfix itself is quite small, but I have accumulated other changes in the datetime data type and include them here also. One set of changes involves printing ISO-formatted dates and is in response to the helpful information from Kurt Lidl regarding ANSI SQL dates. I'll send another e-mail sometime soon discussing more issues he has raised...
This commit is contained in:
parent
159f8c63ad
commit
28454c216b
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/dt.c,v 1.11 1997/03/28 06:53:50 scrappy Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/dt.c,v 1.12 1997/03/28 07:12:46 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -33,57 +33,12 @@
|
||||
|
||||
#define USE_DATE_CACHE 1
|
||||
|
||||
#if FALSE
|
||||
#ifdef NAN
|
||||
#define DT_INVALID (NAN)
|
||||
#else
|
||||
#define DT_INVALID (DBL_MIN+DBL_MIN)
|
||||
#endif
|
||||
#ifdef HUGE_VAL
|
||||
#define DT_NOBEGIN (-HUGE_VAL)
|
||||
#define DT_NOEND (HUGE_VAL)
|
||||
#else
|
||||
#define DT_NOBEGIN (-DBL_MAX)
|
||||
#define DT_NOEND (DBL_MAX)
|
||||
#endif
|
||||
#define DT_CURRENT (DBL_MIN)
|
||||
#define DT_EPOCH (-DBL_MIN)
|
||||
|
||||
#define DATETIME_INVALID(j) {j = DT_INVALID;}
|
||||
#ifdef NAN
|
||||
#define DATETIME_IS_INVALID(j) (isnan(j))
|
||||
#else
|
||||
#define DATETIME_IS_INVALID(j) (j == DT_INVALID)
|
||||
#endif
|
||||
char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
|
||||
|
||||
#define DATETIME_NOBEGIN(j) {j = DT_NOBEGIN;}
|
||||
#define DATETIME_IS_NOBEGIN(j) (j == DT_NOBEGIN)
|
||||
|
||||
#define DATETIME_NOEND(j) {j = DT_NOEND;}
|
||||
#define DATETIME_IS_NOEND(j) (j == DT_NOEND)
|
||||
|
||||
#define DATETIME_CURRENT(j) {j = DT_CURRENT;}
|
||||
#define DATETIME_IS_CURRENT(j) (j == DT_CURRENT)
|
||||
|
||||
#define DATETIME_EPOCH(j) {j = DT_EPOCH;}
|
||||
#define DATETIME_IS_EPOCH(j) (j == DT_EPOCH)
|
||||
|
||||
#define DATETIME_IS_RELATIVE(j) (DATETIME_IS_CURRENT(j) || DATETIME_IS_EPOCH(j))
|
||||
#define DATETIME_NOT_FINITE(j) (DATETIME_IS_INVALID(j) \
|
||||
|| DATETIME_IS_NOBEGIN(j) || DATETIME_IS_NOEND(j))
|
||||
#define DATETIME_IS_RESERVED(j) (DATETIME_IS_RELATIVE(j) || DATETIME_NOT_FINITE(j))
|
||||
|
||||
|
||||
#define TIMESPAN_INVALID(j) {j->time = DT_INVALID;}
|
||||
#ifdef NAN
|
||||
#define TIMESPAN_IS_INVALID(j) (isnan((j).time))
|
||||
#else
|
||||
#define TIMESPAN_IS_INVALID(j) ((j).time == DT_INVALID)
|
||||
#endif
|
||||
|
||||
#define TIME_PREC 1e-6
|
||||
#define JROUND(j) (rint(((double) j)/TIME_PREC)*TIME_PREC)
|
||||
#endif
|
||||
char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
|
||||
"Thursday", "Friday", "Saturday", NULL};
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
@ -98,9 +53,6 @@ datetime_in(char *str)
|
||||
{
|
||||
DateTime *result;
|
||||
|
||||
#if FALSE
|
||||
double date, time;
|
||||
#endif
|
||||
double fsec;
|
||||
struct tm tt, *tm = &tt;
|
||||
int tzp;
|
||||
@ -122,13 +74,6 @@ datetime_in(char *str)
|
||||
|
||||
switch (dtype) {
|
||||
case DTK_DATE:
|
||||
#if FALSE
|
||||
if (tzp != 0) {
|
||||
*result = tm2datetime( tm, fsec, tzp);
|
||||
} else {
|
||||
*result = tm2datetime( tm, fsec, CTimeZone);
|
||||
};
|
||||
#endif
|
||||
*result = tm2datetime( tm, fsec, tzp);
|
||||
|
||||
#ifdef DATEDEBUG
|
||||
@ -182,7 +127,7 @@ datetime_out(DateTime *dt)
|
||||
EncodeSpecialDateTime(*dt, buf);
|
||||
|
||||
} else if (datetime2tm( *dt, tm, &fsec) == 0) {
|
||||
EncodePostgresDate(tm, fsec, buf);
|
||||
EncodeDateTime(tm, fsec, DateStyle, buf);
|
||||
|
||||
} else {
|
||||
EncodeSpecialDateTime(DT_INVALID, buf);
|
||||
@ -268,7 +213,7 @@ timespan_out(TimeSpan *span)
|
||||
if (timespan2tm(*span, tm, &fsec) != 0)
|
||||
return(NULL);
|
||||
|
||||
if (EncodePostgresSpan(tm, fsec, buf) != 0)
|
||||
if (EncodeTimeSpan(tm, fsec, DateStyle, buf) != 0)
|
||||
elog(WARN,"Unable to format timespan",NULL);
|
||||
|
||||
if (!PointerIsValid(result = PALLOC(strlen(buf)+1)))
|
||||
@ -306,6 +251,7 @@ GetEpochTime( struct tm *tm)
|
||||
tm->tm_sec = t0->tm_sec;
|
||||
|
||||
if (tm->tm_year < 1900) tm->tm_year += 1900;
|
||||
tm->tm_mon++;
|
||||
|
||||
#ifdef DATEDEBUG
|
||||
printf( "GetEpochTime- %04d-%02d-%02d %02d:%02d:%02d\n",
|
||||
@ -350,10 +296,6 @@ datetime_eq(DateTime *datetime1, DateTime *datetime2)
|
||||
dt1 = *datetime1;
|
||||
dt2 = *datetime2;
|
||||
|
||||
#if FALSE
|
||||
if (DATETIME_NOT_FINITE(dt1) || DATETIME_NOT_FINITE(dt2))
|
||||
return FALSE;
|
||||
#endif
|
||||
if (DATETIME_IS_INVALID(dt1) || DATETIME_IS_INVALID(dt2))
|
||||
return FALSE;
|
||||
|
||||
@ -374,10 +316,6 @@ datetime_ne(DateTime *datetime1, DateTime *datetime2)
|
||||
dt1 = *datetime1;
|
||||
dt2 = *datetime2;
|
||||
|
||||
#if FALSE
|
||||
if (DATETIME_NOT_FINITE(dt1) || DATETIME_NOT_FINITE(dt2))
|
||||
return FALSE;
|
||||
#endif
|
||||
if (DATETIME_IS_INVALID(dt1) || DATETIME_IS_INVALID(dt2))
|
||||
return FALSE;
|
||||
|
||||
@ -398,10 +336,6 @@ datetime_lt(DateTime *datetime1, DateTime *datetime2)
|
||||
dt1 = *datetime1;
|
||||
dt2 = *datetime2;
|
||||
|
||||
#if FALSE
|
||||
if (DATETIME_NOT_FINITE(dt1) || DATETIME_NOT_FINITE(dt2))
|
||||
return FALSE;
|
||||
#endif
|
||||
if (DATETIME_IS_INVALID(dt1) || DATETIME_IS_INVALID(dt2))
|
||||
return FALSE;
|
||||
|
||||
@ -422,10 +356,6 @@ datetime_gt(DateTime *datetime1, DateTime *datetime2)
|
||||
dt1 = *datetime1;
|
||||
dt2 = *datetime2;
|
||||
|
||||
#if FALSE
|
||||
if (DATETIME_NOT_FINITE(dt1) || DATETIME_NOT_FINITE(dt2))
|
||||
return FALSE;
|
||||
#endif
|
||||
if (DATETIME_IS_INVALID(dt1) || DATETIME_IS_INVALID(dt2))
|
||||
return FALSE;
|
||||
|
||||
@ -449,10 +379,6 @@ datetime_le(DateTime *datetime1, DateTime *datetime2)
|
||||
dt1 = *datetime1;
|
||||
dt2 = *datetime2;
|
||||
|
||||
#if FALSE
|
||||
if (DATETIME_NOT_FINITE(dt1) || DATETIME_NOT_FINITE(dt2))
|
||||
return FALSE;
|
||||
#endif
|
||||
if (DATETIME_IS_INVALID(dt1) || DATETIME_IS_INVALID(dt2))
|
||||
return FALSE;
|
||||
|
||||
@ -473,10 +399,6 @@ datetime_ge(DateTime *datetime1, DateTime *datetime2)
|
||||
dt1 = *datetime1;
|
||||
dt2 = *datetime2;
|
||||
|
||||
#if FALSE
|
||||
if (DATETIME_NOT_FINITE(dt1) || DATETIME_NOT_FINITE(dt2))
|
||||
return FALSE;
|
||||
#endif
|
||||
if (DATETIME_IS_INVALID(dt1) || DATETIME_IS_INVALID(dt2))
|
||||
return FALSE;
|
||||
|
||||
@ -608,9 +530,6 @@ TimeSpan *datetime_sub(DateTime *datetime1, DateTime *datetime2)
|
||||
DATETIME_INVALID( result->time);
|
||||
|
||||
} else {
|
||||
#if FALSE
|
||||
result->time = JROUND(dt1 - dt2);
|
||||
#endif
|
||||
result->time = (dt1 - dt2);
|
||||
};
|
||||
result->month = 0;
|
||||
@ -1292,6 +1211,12 @@ int j2day( int date)
|
||||
return(day);
|
||||
} /* j2day() */
|
||||
|
||||
|
||||
/* datetime2tm()
|
||||
* Convert datetime data type to POSIX time structure.
|
||||
* Note that year is _not_ 1900-based, but is an explicit full value.
|
||||
* Also, month is one-based, _not_ zero-based.
|
||||
*/
|
||||
int
|
||||
datetime2tm( DateTime dt, struct tm *tm, double *fsec)
|
||||
{
|
||||
@ -1336,8 +1261,11 @@ printf( "datetime2tm- timezone is %s; offset is %d; daylight is %d\n",
|
||||
return 0;
|
||||
} /* datetime2tm() */
|
||||
|
||||
|
||||
/* tm2datetime()
|
||||
* Convert a tm structure to a datetime data type.
|
||||
* Note that year is _not_ 1900-based, but is an explicit full value.
|
||||
* Also, month is one-based, _not_ zero-based.
|
||||
*/
|
||||
DateTime
|
||||
tm2datetime( struct tm *tm, double fsec, int tzp) {
|
||||
@ -1639,7 +1567,7 @@ printf( "DecodeDateTime- RESERV field %s value is %d\n", field[i], val);
|
||||
#endif
|
||||
switch (val) {
|
||||
case DTK_NOW:
|
||||
tmask = (DTK_DATE_M | DTK_TIME_M);
|
||||
tmask = (DTK_DATE_M | DTK_TIME_M | DTK_M(TZ));
|
||||
*dtype = DTK_DATE;
|
||||
GetCurrentTime(tm);
|
||||
break;
|
||||
@ -1681,7 +1609,7 @@ printf( "DecodeDateTime- RESERV field %s value is %d\n", field[i], val);
|
||||
tm->tm_hour = 0;
|
||||
tm->tm_min = 0;
|
||||
tm->tm_sec = 0;
|
||||
*tzp = 0;
|
||||
if (tzp != NULL) *tzp = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -2472,7 +2400,7 @@ DecodeUnits(int field, char *lowtoken, int *val)
|
||||
} /* DecodeUnits() */
|
||||
|
||||
|
||||
/*
|
||||
/* datebsearch()
|
||||
* Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this
|
||||
* is WAY faster than the generic bsearch().
|
||||
*/
|
||||
@ -2499,30 +2427,9 @@ datebsearch(char *key, datetkn *base, unsigned int nel)
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
/***************************************************************************/
|
||||
/***************************************************************************/
|
||||
|
||||
char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};
|
||||
|
||||
char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
|
||||
"Thursday", "Friday", "Saturday", NULL};
|
||||
|
||||
#if FALSE
|
||||
int EncodeMonth(int mon, char *str);
|
||||
|
||||
int EncodeMonth(int mon, char *str)
|
||||
{
|
||||
strcpy( str, months[mon-1]);
|
||||
|
||||
return(TRUE);
|
||||
} /* EncodeMonth() */
|
||||
#endif
|
||||
|
||||
#define EncodeMonth(m,s) strcpy(s,months[m-1])
|
||||
|
||||
|
||||
/* EncodeSpecialDateTime()
|
||||
* Convert reserved datetime data type to string.
|
||||
*/
|
||||
int EncodeSpecialDateTime(DateTime dt, char *str)
|
||||
{
|
||||
if (DATETIME_IS_RESERVED(dt)) {
|
||||
@ -2554,10 +2461,10 @@ printf( "EncodeSpecialDateTime- unrecognized date\n");
|
||||
} /* EncodeSpecialDateTime() */
|
||||
|
||||
|
||||
int EncodePostgresDate(struct tm *tm, double fsec, char *str)
|
||||
int EncodeDateTime(struct tm *tm, double fsec, int style, char *str)
|
||||
{
|
||||
char mabbrev[4], dabbrev[4];
|
||||
int day;
|
||||
int day, hour, min;
|
||||
double sec;
|
||||
|
||||
sec = (tm->tm_sec + fsec);
|
||||
@ -2565,13 +2472,13 @@ int EncodePostgresDate(struct tm *tm, double fsec, char *str)
|
||||
tm->tm_isdst = -1;
|
||||
|
||||
#ifdef DATEDEBUG
|
||||
printf( "EncodePostgresDate- timezone is %s; offset is %d; daylight is %d\n",
|
||||
printf( "EncodeDateTime- timezone is %s; offset is %d; daylight is %d\n",
|
||||
CTZName, CTimeZone, CDayLight);
|
||||
#endif
|
||||
|
||||
day = date2j( tm->tm_year, tm->tm_mon, tm->tm_mday);
|
||||
#ifdef DATEDEBUG
|
||||
printf( "EncodePostgresDate- day is %d\n", day);
|
||||
printf( "EncodeDateTime- day is %d\n", day);
|
||||
#endif
|
||||
tm->tm_wday = j2day( day);
|
||||
|
||||
@ -2583,19 +2490,37 @@ printf( "EncodePostgresDate- day is %d\n", day);
|
||||
|
||||
strcpy( mabbrev, months[tm->tm_mon-1]);
|
||||
|
||||
if (DateStyle == USE_ISO_DATES) {
|
||||
/* compatible with ISO date formats */
|
||||
if (style == USE_ISO_DATES) {
|
||||
if (tm->tm_year > 0) {
|
||||
sprintf( str, "%04d-%02d-%02d %02d:%02d:%5.2f %s",
|
||||
#if FALSE
|
||||
sprintf( str, "%04d-%02d-%02d %02d:%02d:%05.2f %s",
|
||||
tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, sec, CTZName);
|
||||
/* XXX brute-force fill in leading zero on seconds */
|
||||
if (*(str+17) == ' ') *(str+17) = '0';
|
||||
#endif
|
||||
sprintf( str, "%04d-%02d-%02d %02d:%02d:",
|
||||
tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min);
|
||||
sprintf( (str+17), ((fsec != 0)? "%05.2f": "%02.0f"), sec);
|
||||
hour = -(CTimeZone / 3600);
|
||||
min = ((abs(CTimeZone) / 60) % 60);
|
||||
sprintf( (str+strlen(str)), ((min != 0)? "%+03d:%02d": "%+03d"), hour, min);
|
||||
#if FALSE
|
||||
sprintf( str, "%04d-%02d-%02d %02d:%02d:%05.2f%+03d:%02d",
|
||||
tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, sec,
|
||||
hour, min);
|
||||
#endif
|
||||
|
||||
} else {
|
||||
sprintf( str, "%04d-%02d-%02d %02d:%02d %s",
|
||||
-(tm->tm_year-1), tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, "BC");
|
||||
if (tm->tm_hour || tm->tm_min) {
|
||||
sprintf( str, "%04d-%02d-%02d %02d:%02d %s",
|
||||
-(tm->tm_year-1), tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, "BC");
|
||||
} else {
|
||||
sprintf( str, "%04d-%02d-%02d %s",
|
||||
-(tm->tm_year-1), tm->tm_mon, tm->tm_mday, "BC");
|
||||
};
|
||||
};
|
||||
|
||||
} else if (DateStyle == USE_SQL_DATES) {
|
||||
/* compatible with Oracle/Ingres date formats */
|
||||
} else if (style == USE_SQL_DATES) {
|
||||
if (EuroDates) {
|
||||
sprintf( str, "%02d/%02d", tm->tm_mday, tm->tm_mon);
|
||||
} else {
|
||||
@ -2612,7 +2537,8 @@ printf( "EncodePostgresDate- day is %d\n", day);
|
||||
-(tm->tm_year-1), tm->tm_hour, tm->tm_min, "BC");
|
||||
};
|
||||
|
||||
} else { /* if (DateStyle == USE_POSTGRES_DATES) */
|
||||
/* backward-compatible with traditional Postgres abstime dates */
|
||||
} else { /* if (style == USE_POSTGRES_DATES) */
|
||||
sprintf( str, "%3s ", dabbrev);
|
||||
if (EuroDates) {
|
||||
sprintf( (str+4), "%02d %3s", tm->tm_mday, mabbrev);
|
||||
@ -2632,21 +2558,27 @@ printf( "EncodePostgresDate- day is %d\n", day);
|
||||
};
|
||||
|
||||
#ifdef DATEDEBUG
|
||||
printf( "EncodePostgresDate- date result is %s\n", str);
|
||||
printf( "EncodeDateTime- date result is %s\n", str);
|
||||
#endif
|
||||
|
||||
#ifdef DATEDEBUG
|
||||
if (tm->tm_year >= 1000) tm->tm_year -= 1900;
|
||||
tm->tm_mon -= 1;
|
||||
strftime( buf, sizeof(buf), "%y.%m.%d %H:%M:%S %Z", tm);
|
||||
printf( "EncodePostgresDate- strftime result is %s\n", buf);
|
||||
printf( "EncodeDateTime- strftime result is %s\n", buf);
|
||||
#endif
|
||||
|
||||
return(TRUE);
|
||||
} /* EncodePostgresDate() */
|
||||
} /* EncodeDateTime() */
|
||||
|
||||
|
||||
int EncodePostgresSpan(struct tm *tm, double fsec, char *str)
|
||||
/* EncodeTimeSpan()
|
||||
* Interpret time structure as a delta time and convert to string.
|
||||
*
|
||||
* Pass a flag to specify the style of string, but only implement
|
||||
* the traditional Postgres style for now. - tgl 97/03/27
|
||||
*/
|
||||
int EncodeTimeSpan(struct tm *tm, double fsec, int style, char *str)
|
||||
{
|
||||
int is_before = FALSE;
|
||||
int is_nonzero = FALSE;
|
||||
@ -2702,8 +2634,8 @@ int EncodePostgresSpan(struct tm *tm, double fsec, char *str)
|
||||
};
|
||||
|
||||
#ifdef DATEDEBUG
|
||||
printf( "EncodePostgresSpan- result is %s\n", str);
|
||||
printf( "EncodeTimeSpan- result is %s\n", str);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
} /* EncodePostgresSpan() */
|
||||
} /* EncodeTimeSpan() */
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.20 1997/03/28 06:54:51 scrappy Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.21 1997/03/28 07:12:53 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -93,7 +93,7 @@ GetCurrentTime(struct tm *tm)
|
||||
time_t now;
|
||||
struct tm *tt;
|
||||
|
||||
now = GetCurrentTransactionStartTime();
|
||||
now = GetCurrentTransactionStartTime()-CTimeZone;
|
||||
tt = gmtime( &now);
|
||||
|
||||
tm->tm_year = tt->tm_year+1900;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: dt.h,v 1.3 1997/03/25 08:11:18 scrappy Exp $
|
||||
* $Id: dt.h,v 1.4 1997/03/28 07:13:21 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -306,8 +306,8 @@ extern int DecodeDateDelta( char *field[], int ftype[],
|
||||
extern int DecodeUnits(int field, char *lowtoken, int *val);
|
||||
|
||||
extern int EncodeSpecialDateTime(DateTime dt, char *str);
|
||||
extern int EncodePostgresDate(struct tm *tm, double fsec, char *str);
|
||||
extern int EncodePostgresSpan(struct tm *tm, double fsec, char *str);
|
||||
extern int EncodeDateTime(struct tm *tm, double fsec, int style, char *str);
|
||||
extern int EncodeTimeSpan(struct tm *tm, double fsec, int style, char *str);
|
||||
|
||||
extern datetkn *datebsearch(char *key, datetkn *base, unsigned int nel);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user