diff options
| author | Kevin B Kenny <kennykb@acm.org> | 2004-05-14 21:43:28 (GMT) | 
|---|---|---|
| committer | Kevin B Kenny <kennykb@acm.org> | 2004-05-14 21:43:28 (GMT) | 
| commit | d3dd7920cd97f2c402d6fdc5c4acdb3e60c8f9cb (patch) | |
| tree | 1bd25818dd481cc051c619d9993b650758201a89 /unix/tclUnixTime.c | |
| parent | a87aa8a1be2c89d9174660ca97ea533474e73901 (diff) | |
| download | tcl-d3dd7920cd97f2c402d6fdc5c4acdb3e60c8f9cb.zip tcl-d3dd7920cd97f2c402d6fdc5c4acdb3e60c8f9cb.tar.gz tcl-d3dd7920cd97f2c402d6fdc5c4acdb3e60c8f9cb.tar.bz2 | |
2004-05-14  Kevin B. Kenny  <kennykb@acm.org>
	* generic/tclInt.decls:      Promoted TclpLocaltime and TclpGmtime
	* generic/tclIntDecls.h:     from Unix-specific stubs to the generic
	* generic/tclIntPlatDecls.h: internal Stubs table.  Reran 'genstubs'
	* generic/tclStubInit.c:
	* unix/tclUnixPort.h:
	* generic/tclClock.c: Changed a buggy 'GMT' timezone specification
	                      to the correct 'GMT0'. [Bug #922848]
	
	* unix/tclUnixThrd.c: Moved TclpGmtime and TclpLocaltime to
	                      unix/tclUnixTime.c where they belong.
	* unix/tclUnixTime.c (TclpGmtime, TclpLocaltime, TclpGetTimeZone,
	                      ThreadSafeGMTime [removed],
	                      ThreadSafeLocalTime [removed],
	                      SetTZIfNecessary, CleanupMemory):
	    Restructured to make sure that the same mutex protects
	    all calls to localtime, gmtime, and tzset.  Added a check
	    in front of those calls to make sure that the TZ env var
	    hasn't changed since the last call to tzset, and repeat
	    tzset if necessary. [Bug #942078]  Removed a buggy test
	    of the Daylight Saving Time information in 'gettimeofday'
	    in favor of applying 'localtime' to a known value.
	    [Bug #922848]
	* tests/clock.test (clock-3.14): Added test to make sure that
	    changes to $env(TZ) take effect immediately.
	
	* win/tclWinTime.c (TclpLocaltime, TclpGmtime):
	    Added porting layer for 'localtime' and 'gmtime' calls.
Diffstat (limited to 'unix/tclUnixTime.c')
| -rw-r--r-- | unix/tclUnixTime.c | 251 | 
1 files changed, 162 insertions, 89 deletions
| diff --git a/unix/tclUnixTime.c b/unix/tclUnixTime.c index 37c16bc..6def210 100644 --- a/unix/tclUnixTime.c +++ b/unix/tclUnixTime.c @@ -9,7 +9,7 @@   * See the file "license.terms" for information on usage and redistribution   * of this file, and for a DISCLAIMER OF ALL WARRANTIES.   * - * RCS: @(#) $Id: tclUnixTime.c,v 1.20 2004/04/06 22:25:57 dgp Exp $ + * RCS: @(#) $Id: tclUnixTime.c,v 1.21 2004/05/14 21:43:29 kennykb Exp $   */  #include "tclInt.h" @@ -24,22 +24,27 @@   */  static Tcl_ThreadDataKey tmKey; +typedef struct ThreadSpecificData { +    struct tm gmtime_buf; +    struct tm localtime_buf; +} ThreadSpecificData;  /*   * If we fall back on the thread-unsafe versions of gmtime and localtime,   * use this mutex to try to protect them.   */ -#if !defined(HAVE_GMTIME_R) || !defined(HAVE_LOCALTIME_R)  TCL_DECLARE_MUTEX(tmMutex) -#endif -/* - * Forward declarations for procedures defined later in this file. - */ +static char* lastTZ = NULL;	/* Holds the last setting of the +				 * TZ environment variable, or an +				 * empty string if the variable was +				 * not set. */ + +/* Static functions declared in this file */ -static struct tm *ThreadSafeGMTime _ANSI_ARGS_(( CONST time_t* )); -static struct tm *ThreadSafeLocalTime _ANSI_ARGS_(( CONST time_t* )); +static void SetTZIfNecessary _ANSI_ARGS_((void)); +static void CleanupMemory _ANSI_ARGS_((ClientData));  /*   *----------------------------------------------------------------------------- @@ -128,18 +133,22 @@ TclpGetTimeZone (currentTime)      unsigned long  currentTime;  {      /* -     * Determine how a timezone is obtained from "struct tm".  If there is no -     * time zone in this struct (very lame) then use the timezone variable. -     * This is done in a way to make the timezone variable the method of last -     * resort, as some systems have it in addition to a field in "struct tm". -     * The gettimeofday system call can also be used to determine the time -     * zone. +     * We prefer first to use the time zone in "struct tm" if the +     * structure contains such a member.  Following that, we try +     * to locate the external 'timezone' variable and use its value. +     * If both of those methods fail, we attempt to convert a known +     * time to local time and use the difference from UTC as the local +     * time zone.  In all cases, we need to undo any Daylight Saving Time +     * adjustment.       */  #if defined(HAVE_TM_TZADJ)  #   define TCL_GOT_TIMEZONE + +    /* Struct tm contains tm_tzadj - that value may be used. */ +      time_t      curTime = (time_t) currentTime; -    struct tm  *timeDataPtr = ThreadSafeLocalTime(&curTime); +    struct tm  *timeDataPtr = TclpLocaltime(&curTime);      int         timeZone;      timeZone = timeDataPtr->tm_tzadj  / 60; @@ -148,12 +157,16 @@ TclpGetTimeZone (currentTime)      }      return timeZone; +  #endif  #if defined(HAVE_TM_GMTOFF) && !defined (TCL_GOT_TIMEZONE)  #   define TCL_GOT_TIMEZONE + +    /* Struct tm contains tm_gmtoff - that value may be used. */ +      time_t     curTime = (time_t) currentTime; -    struct tm *timeDataPtr = ThreadSafeLocalTime(&curTime); +    struct tm *timeDataPtr = TclpLocaltime(&curTime);      int        timeZone;      timeZone = -(timeDataPtr->tm_gmtoff / 60); @@ -162,46 +175,17 @@ TclpGetTimeZone (currentTime)      }      return timeZone; -#endif - -#if defined(USE_DELTA_FOR_TZ) -#define TCL_GOT_TIMEZONE 1 -    /* -     * This hack replaces using global var timezone or gettimeofday -     * in situations where they are buggy such as on AIX when libbsd.a -     * is linked in. -     */ -    int timeZone; -    time_t tt; -    struct tm *stm; -    tt = 849268800L;      /*    1996-11-29 12:00:00  GMT */ -    stm = ThreadSafeLocalTime(&tt); /* eg 1996-11-29  6:00:00  CST6CDT */ -    /* The calculation below assumes a max of +12 or -12 hours from GMT */ -    timeZone = (12 - stm->tm_hour)*60 + (0 - stm->tm_min); -    return timeZone;  /* eg +360 for CST6CDT */  #endif -    /* -     * Must prefer timezone variable over gettimeofday, as gettimeofday does -     * not return timezone information on many systems that have moved this -     * information outside of the kernel. -     */ -     -#if defined(HAVE_TIMEZONE_VAR) && !defined (TCL_GOT_TIMEZONE) +#if defined(HAVE_TIMEZONE_VAR) && !defined(TCL_GOT_TIMEZONE) && !defined(USE_DELTA_FOR_TZ)  #   define TCL_GOT_TIMEZONE -    static int setTZ = 0; -#ifdef TCL_THREADS -    static Tcl_Mutex tzMutex; -#endif -    int        timeZone; -    Tcl_MutexLock(&tzMutex); -    if (!setTZ) { -        tzset(); -        setTZ = 1; -    } -    Tcl_MutexUnlock(&tzMutex); +    int         timeZone; + +    /* The 'timezone' external var is present and may be used. */ + +    SetTZIfNecessary();      /*       * Note: this is not a typo in "timezone" below!  See tzset @@ -209,30 +193,36 @@ TclpGetTimeZone (currentTime)       */      timeZone = timezone / 60; -      return timeZone; +  #endif -#if !defined(NO_GETTOD) && !defined (TCL_GOT_TIMEZONE) -#   define TCL_GOT_TIMEZONE -    struct timeval  tv; -    struct timezone tz; -    int timeZone; +#if !defined(TCL_GOT_TIMEZONE)  +#define TCL_GOT_TIMEZONE 1 +    /* +     * Fallback - determine time zone with a known reference time. +     */ -    gettimeofday(&tv, &tz); -    timeZone = tz.tz_minuteswest; -    if (tz.tz_dsttime) { -        timeZone += 60; +    int timeZone; +    time_t tt; +    struct tm *stm; +    tt = 849268800L;      /*    1996-11-29 12:00:00  GMT */ +    stm = TclpLocaltime(&tt); /* eg 1996-11-29  6:00:00  CST6CDT */ +    /* The calculation below assumes a max of +12 or -12 hours from GMT */ +    timeZone = (12 - stm->tm_hour)*60 + (0 - stm->tm_min); +    if ( stm -> tm_isdst ) { +	timeZone += 60;      } -     -    return timeZone; +    return timeZone;  /* eg +360 for CST6CDT */  #endif  #ifndef TCL_GOT_TIMEZONE      /*       * Cause compile error, we don't know how to get timezone.       */ -    error: autoconf did not figure out how to determine the timezone.  + +#error autoconf did not figure out how to determine the timezone.  +  #endif  } @@ -290,9 +280,9 @@ TclpGetDate(time, useGMT)      int useGMT;  {      if (useGMT) { -	return ThreadSafeGMTime(time); +	return TclpGmtime(time);      } else { -	return ThreadSafeLocalTime(time); +	return TclpLocaltime(time);      }  } @@ -354,7 +344,7 @@ TclpStrftime(s, maxsize, format, t, useGMT)  /*   *----------------------------------------------------------------------   * - * ThreadSafeGMTime -- + * TclpGmtime --   *   *	Wrapper around the 'gmtime' library function to make it thread   *	safe. @@ -368,33 +358,42 @@ TclpStrftime(s, maxsize, format, t, useGMT)   *----------------------------------------------------------------------   */ -static struct tm * -ThreadSafeGMTime(timePtr) +struct tm * +TclpGmtime( timePtr )      CONST time_t *timePtr;	/* Pointer to the number of seconds -				 * since the local system's epoch -				 */ +				 * since the local system's epoch */  {      /*       * Get a thread-local buffer to hold the returned time.       */ -    struct tm *tmPtr = (struct tm *) -	    Tcl_GetThreadData(&tmKey, sizeof(struct tm)); +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT( &tmKey );  #ifdef HAVE_GMTIME_R -    gmtime_r(timePtr, tmPtr); +    gmtime_r(timePtr, &( tsdPtr->gmtime_buf ));  #else -    Tcl_MutexLock(&tmMutex); -    memcpy((VOID *) tmPtr, (VOID *) gmtime(timePtr), sizeof(struct tm)); -    Tcl_MutexUnlock(&tmMutex); +    Tcl_MutexLock( &tmMutex ); +    memcpy( (VOID *) &( tsdPtr->gmtime_buf ), +	    (VOID *) gmtime( timePtr ), +	    sizeof( struct tm ) ); +    Tcl_MutexUnlock( &tmMutex );  #endif     -    return tmPtr; +    return &( tsdPtr->gmtime_buf ); +} +/* + * Forwarder for obsolete item in Stubs + */ +struct tm* +TclpGmtime_unix( timePtr ) +    CONST time_t* timePtr; +{ +    return TclpGmtime( timePtr );  }  /*   *----------------------------------------------------------------------   * - * ThreadSafeLocalTime -- + * TclpLocaltime --   *   *	Wrapper around the 'localtime' library function to make it thread   *	safe. @@ -408,25 +407,99 @@ ThreadSafeGMTime(timePtr)   *----------------------------------------------------------------------   */ -static struct tm * -ThreadSafeLocalTime(timePtr) +struct tm * +TclpLocaltime(timePtr)      CONST time_t *timePtr;	/* Pointer to the number of seconds -				 * since the local system's epoch -				 */ +				 * since the local system's epoch */  {      /*       * Get a thread-local buffer to hold the returned time.       */ -    struct tm *tmPtr = (struct tm *) -	    Tcl_GetThreadData(&tmKey, sizeof(struct tm)); +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT( &tmKey ); +    SetTZIfNecessary();  #ifdef HAVE_LOCALTIME_R -    localtime_r(timePtr, tmPtr); +    localtime_r( timePtr, &( tsdPtr->localtime_buf ) );  #else +    Tcl_MutexLock( &tmMutex ); +    memcpy( (VOID *) &( tsdPtr -> localtime_buf ), +	    (VOID *) localtime( timePtr ), +	    sizeof( struct tm ) ); +    Tcl_MutexUnlock( &tmMutex ); +#endif     +    return &( tsdPtr->localtime_buf ); +} +/* + * Forwarder for obsolete item in Stubs + */ +struct tm* +TclpLocaltime_unix( timePtr ) +    CONST time_t* timePtr; +{ +    return TclpLocaltime( timePtr ); +} + +/* + *---------------------------------------------------------------------- + * + * SetTZIfNecessary -- + * + *	Determines whether a call to 'tzset' is needed prior to the + *	next call to 'localtime' or examination of the 'timezone' variable. + * + * Results: + *	None. + * + * Side effects: + *	If 'tzset' has never been called in the current process, or if + *	the value of the environment variable TZ has changed since the + *	last call to 'tzset', then 'tzset' is called again. + * + *---------------------------------------------------------------------- + */ + +static void +SetTZIfNecessary() { + +    CONST char* newTZ = getenv( "TZ" );      Tcl_MutexLock(&tmMutex); -    memcpy((VOID *) tmPtr, (VOID *) localtime(timePtr), sizeof(struct tm)); +    if ( newTZ == NULL ) { +	newTZ = ""; +    } +    if ( lastTZ == NULL || strcmp( lastTZ, newTZ ) ) { +        tzset(); +	if ( lastTZ == NULL ) { +	    Tcl_CreateExitHandler( CleanupMemory, (ClientData) NULL ); +	} else { +	    Tcl_Free( lastTZ ); +	} +	lastTZ = Tcl_Alloc( strlen( newTZ ) + 1 ); +	strcpy( lastTZ, newTZ ); +    }      Tcl_MutexUnlock(&tmMutex); -#endif     -    return tmPtr; + +} + +/* + *---------------------------------------------------------------------- + * + * CleanupMemory -- + * + *	Releases the private copy of the TZ environment variable + *	upon exit from Tcl. + * + * Results: + *	None. + * + * Side effects: + *	Frees allocated memory. + * + *---------------------------------------------------------------------- + */ + +static void +CleanupMemory( ClientData ignored ) +{ +    Tcl_Free( lastTZ );  } | 
