Subject: bug in adt date

From: [email-masked] (Juergen Schoenwaelder)
Date: Wed, 8 Jan 92 10:09:30 MET

Hi!

We are using postgres v3r1 on a sparc under sunos 4.1 and we found
two bugs in the code for the adt date which is responsible for the
correct access of time varying data. Both bugs are in the file
$POSTGRESHOME/src/utils/adt/date.c

The first one is in function isabstime(). The CORRECT define gives
the right code.

        /* search for hour:minute:sec option */
        hour=0;
        min=0;
        sec=0;
#ifdef CORRECT
        hour = -1;
#endif
        for (i=0; i<=1; i++) {
            c = *p;
#ifndef CORRECT
            hour = -1;
#endif
            if (isdigit(c)) {
                if (hour < 0)
                    hour = (c - '0');
                else
                    hour = hour * 10 + (c - '0');
                p++;
            } else if ( c == ':' && hour >= 0 ) {
                /* one digit hours */
                break;
            } else {
                return(0);      /*syntax error*/
            }
        }

Perhaps it would be better to replace this funny loop with
something more obvious.

The second bug is in abstimein() and abstimeout(). In both functions a call
to ftime() is used to determine the timezone and the daylight saving time
flag (dstflag). The man page for ftime() says:

	The structure contains ... and a flag that, if nonzero, indicates 
	that Daylight Saving time applies locally during the appropriate
	part of the year.

This flag tells nothing about whether Daylight Saving time applies 
on the current time. To get this information we call time() and localtime()
to get a tm structure which includes a tm_isdst flag which works correct.
Since we do not know if these calls are supported on systems running
postgres we placed #ifdef sun around our fix.

To avoid a clash with the function time() we renamed the variable time
to abstime in function abstimein().

/*
 *	abstimein	- converts a time string to an internal format
 *			  checks syntax and range of datetime,
 *			  returns time as long integer in respect of GMT
 */

AbsoluteTime	
abstimein(datetime)
	char	*datetime;
{
	int		which;
	AbsoluteTime	abstime;
	struct tm	brokentime;

	if ( TimeDiffIsInited == false ) {
	    TimeDifferenceFromGMT = (struct timeb *)
	      malloc( sizeof(struct timeb));
	    ftime(TimeDifferenceFromGMT);
#ifdef sun
	    { 
		struct tm *tt;
		time_t aa = time((time_t *) 0);
		tt = localtime(&aa);
		TimeDifferenceFromGMT->dstflag = tt->tm_isdst;
	    }
#endif
	    timezonename = (char *) timezone ( TimeDifferenceFromGMT->timezone,
			        TimeDifferenceFromGMT->dstflag ) ;
	    TimeDiffIsInited = true;
	}

	which = isabstime(datetime, &brokentime);

	switch (which) {

	case 3:							/* epoch */
		abstime = MIN_ABSTIME;
		break;

	case 2:							/* now */
		abstime = GetCurrentTransactionStartTime();
		break;

	case 1:							/* from user */
		(void) timeinsec(&brokentime, &abstime);

		/* user inputs in local time, we need to store things
		 * in GMT so that moving databases across timezones
		 * (amongst other things) behave predictably
		 */

		abstime = abstime + 
		  (TimeDifferenceFromGMT->timezone * 60 ) -
		    ( TimeDifferenceFromGMT->dstflag * 3600 ) ;
		
		break;

	case 0:							/* error */
		abstime = INVALID_ABSTIME;
		break;
	}

	return(abstime);
}

/*
 *	abstimeout	- converts the internal time format to a string
 */
char	*
abstimeout(datetime)
	AbsoluteTime	datetime;
	/* seconds in respect of Greenwich Mean Time GMT!! */
{
	extern	struct tm	*gmtime();
	extern	char		*asctime();
	extern	char		*index();
	extern	char		*strcpy();
	struct tm		*brokentime;
	/*points to static structure in gmtime*/
	char			*datestring, *p;
	int			i;

	if ( TimeDiffIsInited == false ) {
	    TimeDifferenceFromGMT = (struct timeb *)
	      malloc( sizeof(struct timeb));
	    ftime(TimeDifferenceFromGMT);
#ifdef sun
            { 
		struct tm *tt;
                time_t aa = time((time_t *) 0);
                tt = localtime(&aa);
                TimeDifferenceFromGMT->dstflag = tt->tm_isdst;
            }
#endif
	    timezonename = (char *) timezone ( TimeDifferenceFromGMT->timezone,
			        TimeDifferenceFromGMT->dstflag ) ;
	    TimeDiffIsInited = true;
	}

	if (datetime == INVALID_ABSTIME) {
	    datestring = (char *) palloc ( INVALID_ABSTIME_STR_LEN +1 );
	    (void) strcpy(datestring,INVALID_ABSTIME_STR);
	} else if ( datetime == 0 ) {
	    datestring = (char *)palloc ( TIME_EPOCH_STR_LEN + 1 );
	    (void) strcpy(datestring, TIME_EPOCH_STR );
	} else   {
	    /* Using localtime instead of gmtime
	     * does the correct conversion !!!
	     */
	    char *temp_date_string = NULL;

	    brokentime = localtime((long *) &datetime);
	    temp_date_string = asctime(brokentime);

	    /* add ten to the length because we want to append
	     * the local timezone spec which might be up to GMT+xx:xx
	     */

	    datestring = (char *)palloc ( strlen ( temp_date_string ) + 10 );

	    (void) strcpy(datestring, 
			  temp_date_string + 4 );    /* skip name of day */

	    /* change new-line character "\n" at the end to timezone 
	       followed by a null */
	    p = index(datestring,'\n');
	    *p = ' ';

	    strcpy ( p+1 , timezonename );
	}

	return(datestring);
}

-----
Juergen Schoenwaelder   ([email-masked])
Technische Universitaet Braunschweig, Inst. f. Betriebssysteme & Rechnerverbund
Bueltenweg 74/75,  3300 Braunschweig, Germany, Tel. +49 531 / 391-3249