os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinTime.c
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinTime.c	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,1147 @@
     1.4 +/* 
     1.5 + * tclWinTime.c --
     1.6 + *
     1.7 + *	Contains Windows specific versions of Tcl functions that
     1.8 + *	obtain time values from the operating system.
     1.9 + *
    1.10 + * Copyright 1995-1998 by Sun Microsystems, Inc.
    1.11 + *
    1.12 + * See the file "license.terms" for information on usage and redistribution
    1.13 + * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    1.14 + *
    1.15 + * RCS: @(#) $Id: tclWinTime.c,v 1.14.2.11 2007/04/21 19:52:15 kennykb Exp $
    1.16 + */
    1.17 +
    1.18 +#include "tclWinInt.h"
    1.19 +
    1.20 +#define SECSPERDAY (60L * 60L * 24L)
    1.21 +#define SECSPERYEAR (SECSPERDAY * 365L)
    1.22 +#define SECSPER4YEAR (SECSPERYEAR * 4L + SECSPERDAY)
    1.23 +
    1.24 +/*
    1.25 + * Number of samples over which to estimate the performance counter
    1.26 + */
    1.27 +#define SAMPLES 64
    1.28 +
    1.29 +/*
    1.30 + * The following arrays contain the day of year for the last day of
    1.31 + * each month, where index 1 is January.
    1.32 + */
    1.33 +
    1.34 +static int normalDays[] = {
    1.35 +    -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364
    1.36 +};
    1.37 +
    1.38 +static int leapDays[] = {
    1.39 +    -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
    1.40 +};
    1.41 +
    1.42 +typedef struct ThreadSpecificData {
    1.43 +    char tzName[64];		/* Time zone name */
    1.44 +    struct tm tm;		/* time information */
    1.45 +} ThreadSpecificData;
    1.46 +static Tcl_ThreadDataKey dataKey;
    1.47 +
    1.48 +/*
    1.49 + * Data for managing high-resolution timers.
    1.50 + */
    1.51 +
    1.52 +typedef struct TimeInfo {
    1.53 +
    1.54 +    CRITICAL_SECTION cs;	/* Mutex guarding this structure */
    1.55 +
    1.56 +    int initialized;		/* Flag == 1 if this structure is
    1.57 +				 * initialized. */
    1.58 +
    1.59 +    int perfCounterAvailable;	/* Flag == 1 if the hardware has a
    1.60 +				 * performance counter */
    1.61 +
    1.62 +    HANDLE calibrationThread;	/* Handle to the thread that keeps the
    1.63 +				 * virtual clock calibrated. */
    1.64 +
    1.65 +    HANDLE readyEvent;		/* System event used to
    1.66 +				 * trigger the requesting thread
    1.67 +				 * when the clock calibration procedure
    1.68 +				 * is initialized for the first time */
    1.69 +
    1.70 +    HANDLE exitEvent; 		/* Event to signal out of an exit handler
    1.71 +				 * to tell the calibration loop to
    1.72 +				 * terminate */
    1.73 +
    1.74 +    LARGE_INTEGER nominalFreq;	/* Nominal frequency of the system
    1.75 +				 * performance counter, that is, the value
    1.76 +				 * returned from QueryPerformanceFrequency. */
    1.77 +
    1.78 +    /*
    1.79 +     * The following values are used for calculating virtual time.
    1.80 +     * Virtual time is always equal to:
    1.81 +     *    lastFileTime + (current perf counter - lastCounter) 
    1.82 +     *				* 10000000 / curCounterFreq
    1.83 +     * and lastFileTime and lastCounter are updated any time that
    1.84 +     * virtual time is returned to a caller.
    1.85 +     */
    1.86 +
    1.87 +    ULARGE_INTEGER fileTimeLastCall;
    1.88 +    LARGE_INTEGER perfCounterLastCall;
    1.89 +    LARGE_INTEGER curCounterFreq;
    1.90 +
    1.91 +    /*
    1.92 +     * Data used in developing the estimate of performance counter
    1.93 +     * frequency
    1.94 +     */
    1.95 +    Tcl_WideUInt fileTimeSample[SAMPLES];
    1.96 +				/* Last 64 samples of system time */
    1.97 +    Tcl_WideInt perfCounterSample[SAMPLES];
    1.98 +				/* Last 64 samples of performance counter */
    1.99 +    int sampleNo;		/* Current sample number */
   1.100 +
   1.101 +
   1.102 +} TimeInfo;
   1.103 +
   1.104 +static TimeInfo timeInfo = {
   1.105 +    { NULL },
   1.106 +    0,
   1.107 +    0,
   1.108 +    (HANDLE) NULL,
   1.109 +    (HANDLE) NULL,
   1.110 +    (HANDLE) NULL,
   1.111 +#ifdef HAVE_CAST_TO_UNION
   1.112 +    (LARGE_INTEGER) (Tcl_WideInt) 0,
   1.113 +    (ULARGE_INTEGER) (DWORDLONG) 0,
   1.114 +    (LARGE_INTEGER) (Tcl_WideInt) 0,
   1.115 +    (LARGE_INTEGER) (Tcl_WideInt) 0,
   1.116 +#else
   1.117 +    0,
   1.118 +    0,
   1.119 +    0,
   1.120 +    0,
   1.121 +#endif
   1.122 +    { 0 },
   1.123 +    { 0 },
   1.124 +    0
   1.125 +};
   1.126 +
   1.127 +CONST static FILETIME posixEpoch = { 0xD53E8000, 0x019DB1DE };
   1.128 +    
   1.129 +/*
   1.130 + * Declarations for functions defined later in this file.
   1.131 + */
   1.132 +
   1.133 +static struct tm *	ComputeGMT _ANSI_ARGS_((const time_t *tp));
   1.134 +static void		StopCalibration _ANSI_ARGS_(( ClientData ));
   1.135 +static DWORD WINAPI     CalibrationThread _ANSI_ARGS_(( LPVOID arg ));
   1.136 +static void 		UpdateTimeEachSecond _ANSI_ARGS_(( void ));
   1.137 +static void		ResetCounterSamples _ANSI_ARGS_((
   1.138 +			    Tcl_WideUInt fileTime, 
   1.139 +                            Tcl_WideInt perfCounter,
   1.140 +			    Tcl_WideInt perfFreq
   1.141 +			));
   1.142 +static Tcl_WideInt		AccumulateSample _ANSI_ARGS_((
   1.143 +			    Tcl_WideInt perfCounter,
   1.144 +			    Tcl_WideUInt fileTime
   1.145 +			));
   1.146 +
   1.147 +/*
   1.148 + *----------------------------------------------------------------------
   1.149 + *
   1.150 + * TclpGetSeconds --
   1.151 + *
   1.152 + *	This procedure returns the number of seconds from the epoch.
   1.153 + *	On most Unix systems the epoch is Midnight Jan 1, 1970 GMT.
   1.154 + *
   1.155 + * Results:
   1.156 + *	Number of seconds from the epoch.
   1.157 + *
   1.158 + * Side effects:
   1.159 + *	None.
   1.160 + *
   1.161 + *----------------------------------------------------------------------
   1.162 + */
   1.163 +
   1.164 +unsigned long
   1.165 +TclpGetSeconds()
   1.166 +{
   1.167 +    Tcl_Time t;
   1.168 +    Tcl_GetTime( &t );
   1.169 +    return t.sec;
   1.170 +}
   1.171 +
   1.172 +/*
   1.173 + *----------------------------------------------------------------------
   1.174 + *
   1.175 + * TclpGetClicks --
   1.176 + *
   1.177 + *	This procedure returns a value that represents the highest
   1.178 + *	resolution clock available on the system.  There are no
   1.179 + *	guarantees on what the resolution will be.  In Tcl we will
   1.180 + *	call this value a "click".  The start time is also system
   1.181 + *	dependant.
   1.182 + *
   1.183 + * Results:
   1.184 + *	Number of clicks from some start time.
   1.185 + *
   1.186 + * Side effects:
   1.187 + *	None.
   1.188 + *
   1.189 + *----------------------------------------------------------------------
   1.190 + */
   1.191 +
   1.192 +unsigned long
   1.193 +TclpGetClicks()
   1.194 +{
   1.195 +    /*
   1.196 +     * Use the Tcl_GetTime abstraction to get the time in microseconds,
   1.197 +     * as nearly as we can, and return it.
   1.198 +     */
   1.199 +
   1.200 +    Tcl_Time now;		/* Current Tcl time */
   1.201 +    unsigned long retval;	/* Value to return */
   1.202 +
   1.203 +    Tcl_GetTime( &now );
   1.204 +    retval = ( now.sec * 1000000 ) + now.usec;
   1.205 +    return retval;
   1.206 +
   1.207 +}
   1.208 +
   1.209 +/*
   1.210 + *----------------------------------------------------------------------
   1.211 + *
   1.212 + * TclpGetTimeZone --
   1.213 + *
   1.214 + *	Determines the current timezone.  The method varies wildly
   1.215 + *	between different Platform implementations, so its hidden in
   1.216 + *	this function.
   1.217 + *
   1.218 + * Results:
   1.219 + *	Minutes west of GMT.
   1.220 + *
   1.221 + * Side effects:
   1.222 + *	None.
   1.223 + *
   1.224 + *----------------------------------------------------------------------
   1.225 + */
   1.226 +
   1.227 +int
   1.228 +TclpGetTimeZone (currentTime)
   1.229 +    Tcl_WideInt currentTime;
   1.230 +{
   1.231 +    int timeZone;
   1.232 +
   1.233 +    tzset();
   1.234 +    timeZone = _timezone / 60;
   1.235 +
   1.236 +    return timeZone;
   1.237 +}
   1.238 +
   1.239 +/*
   1.240 + *----------------------------------------------------------------------
   1.241 + *
   1.242 + * Tcl_GetTime --
   1.243 + *
   1.244 + *	Gets the current system time in seconds and microseconds
   1.245 + *	since the beginning of the epoch: 00:00 UCT, January 1, 1970.
   1.246 + *
   1.247 + * Results:
   1.248 + *	Returns the current time in timePtr.
   1.249 + *
   1.250 + * Side effects:
   1.251 + *	On the first call, initializes a set of static variables to
   1.252 + *	keep track of the base value of the performance counter, the
   1.253 + *	corresponding wall clock (obtained through ftime) and the
   1.254 + *	frequency of the performance counter.  Also spins a thread
   1.255 + *	whose function is to wake up periodically and monitor these
   1.256 + *	values, adjusting them as necessary to correct for drift
   1.257 + *	in the performance counter's oscillator.
   1.258 + *
   1.259 + *----------------------------------------------------------------------
   1.260 + */
   1.261 +
   1.262 +void
   1.263 +Tcl_GetTime(timePtr)
   1.264 +    Tcl_Time *timePtr;		/* Location to store time information. */
   1.265 +{
   1.266 +    struct timeb t;
   1.267 +
   1.268 +    int useFtime = 1;		/* Flag == TRUE if we need to fall back
   1.269 +				 * on ftime rather than using the perf
   1.270 +				 * counter */
   1.271 +
   1.272 +    /* Initialize static storage on the first trip through. */
   1.273 +
   1.274 +    /*
   1.275 +     * Note: Outer check for 'initialized' is a performance win
   1.276 +     * since it avoids an extra mutex lock in the common case.
   1.277 +     */
   1.278 +
   1.279 +    if ( !timeInfo.initialized ) { 
   1.280 +	TclpInitLock();
   1.281 +	if ( !timeInfo.initialized ) {
   1.282 +	    timeInfo.perfCounterAvailable
   1.283 +		= QueryPerformanceFrequency( &timeInfo.nominalFreq );
   1.284 +
   1.285 +	    /*
   1.286 +	     * Some hardware abstraction layers use the CPU clock
   1.287 +	     * in place of the real-time clock as a performance counter
   1.288 +	     * reference.  This results in:
   1.289 +	     *    - inconsistent results among the processors on
   1.290 +	     *      multi-processor systems.
   1.291 +	     *    - unpredictable changes in performance counter frequency
   1.292 +	     *      on "gearshift" processors such as Transmeta and
   1.293 +	     *      SpeedStep.
   1.294 +	     *
   1.295 +	     * There seems to be no way to test whether the performance
   1.296 +	     * counter is reliable, but a useful heuristic is that
   1.297 +	     * if its frequency is 1.193182 MHz or 3.579545 MHz, it's
   1.298 +	     * derived from a colorburst crystal and is therefore
   1.299 +	     * the RTC rather than the TSC.
   1.300 +	     *
   1.301 +	     * A sloppier but serviceable heuristic is that the RTC crystal
   1.302 +	     * is normally less than 15 MHz while the TSC crystal is
   1.303 +	     * virtually assured to be greater than 100 MHz.  Since Win98SE
   1.304 +	     * appears to fiddle with the definition of the perf counter
   1.305 +	     * frequency (perhaps in an attempt to calibrate the clock?)
   1.306 +	     * we use the latter rule rather than an exact match.
   1.307 +	     */
   1.308 +
   1.309 +	    if ( timeInfo.perfCounterAvailable
   1.310 +		 /* The following lines would do an exact match on
   1.311 +		  * crystal frequency:
   1.312 +		  * && timeInfo.nominalFreq.QuadPart != (Tcl_WideInt) 1193182
   1.313 +		  * && timeInfo.nominalFreq.QuadPart != (Tcl_WideInt) 3579545
   1.314 +		  */
   1.315 +		 && timeInfo.nominalFreq.QuadPart > (Tcl_WideInt) 15000000 ) {
   1.316 +
   1.317 +		/*
   1.318 +		 * As an exception, if every logical processor on the system
   1.319 +		 * is on the same chip, we use the performance counter anyway,
   1.320 +		 * presuming that everyone's TSC is locked to the same
   1.321 +		 * oscillator.
   1.322 +		 */
   1.323 +
   1.324 +		SYSTEM_INFO systemInfo;
   1.325 +		unsigned int regs[4];
   1.326 +		GetSystemInfo( &systemInfo );
   1.327 +		if ( TclWinCPUID( 0, regs ) == TCL_OK
   1.328 +
   1.329 +		     && regs[1] == 0x756e6547 /* "Genu" */
   1.330 +		     && regs[3] == 0x49656e69 /* "ineI" */
   1.331 +		     && regs[2] == 0x6c65746e /* "ntel" */
   1.332 +
   1.333 +		     && TclWinCPUID( 1, regs ) == TCL_OK 
   1.334 +
   1.335 +		     && ( (regs[0] & 0x00000F00) == 0x00000F00 /* Pentium 4 */
   1.336 +			  || ( (regs[0] & 0x00F00000)    /* Extended family */
   1.337 +			       && (regs[3] & 0x10000000) ) ) /* Hyperthread */
   1.338 +		     && ( ( ( regs[1] & 0x00FF0000 ) >> 16 ) /* CPU count */
   1.339 +			  == systemInfo.dwNumberOfProcessors ) 
   1.340 +
   1.341 +		    ) {
   1.342 +		    timeInfo.perfCounterAvailable = TRUE;
   1.343 +		} else {
   1.344 +		timeInfo.perfCounterAvailable = FALSE;
   1.345 +	    }
   1.346 +
   1.347 +	    }
   1.348 +
   1.349 +	    /*
   1.350 +	     * If the performance counter is available, start a thread to
   1.351 +	     * calibrate it.
   1.352 +	     */
   1.353 +
   1.354 +	    if ( timeInfo.perfCounterAvailable ) {
   1.355 +		DWORD id;
   1.356 +		InitializeCriticalSection( &timeInfo.cs );
   1.357 +		timeInfo.readyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
   1.358 +		timeInfo.exitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
   1.359 +		timeInfo.calibrationThread = CreateThread( NULL,
   1.360 +							   256,
   1.361 +							   CalibrationThread,
   1.362 +							   (LPVOID) NULL,
   1.363 +							   0,
   1.364 +							   &id );
   1.365 +		SetThreadPriority( timeInfo.calibrationThread,
   1.366 +				   THREAD_PRIORITY_HIGHEST );
   1.367 +
   1.368 +		/*
   1.369 +		 * Wait for the thread just launched to start running,
   1.370 +		 * and create an exit handler that kills it so that it
   1.371 +		 * doesn't outlive unloading tclXX.dll
   1.372 +		 */
   1.373 +
   1.374 +		WaitForSingleObject( timeInfo.readyEvent, INFINITE );
   1.375 +		CloseHandle( timeInfo.readyEvent );
   1.376 +		Tcl_CreateExitHandler( StopCalibration, (ClientData) NULL );
   1.377 +	    }
   1.378 +	    timeInfo.initialized = TRUE;
   1.379 +	}
   1.380 +	TclpInitUnlock();
   1.381 +    }
   1.382 +
   1.383 +    if ( timeInfo.perfCounterAvailable ) {
   1.384 +	/*
   1.385 +	 * Query the performance counter and use it to calculate the
   1.386 +	 * current time.
   1.387 +	 */
   1.388 +
   1.389 +	LARGE_INTEGER curCounter;
   1.390 +				/* Current performance counter */
   1.391 +
   1.392 +	Tcl_WideInt curFileTime;
   1.393 +				/* Current estimated time, expressed
   1.394 +				 * as 100-ns ticks since the Windows epoch */
   1.395 +
   1.396 +	static LARGE_INTEGER posixEpoch;
   1.397 +				/* Posix epoch expressed as 100-ns ticks
   1.398 +				 * since the windows epoch */
   1.399 +
   1.400 +	Tcl_WideInt usecSincePosixEpoch;
   1.401 +				/* Current microseconds since Posix epoch */
   1.402 +
   1.403 +	posixEpoch.LowPart = 0xD53E8000;
   1.404 +	posixEpoch.HighPart = 0x019DB1DE;
   1.405 +
   1.406 +	EnterCriticalSection( &timeInfo.cs );
   1.407 +
   1.408 +	QueryPerformanceCounter( &curCounter );
   1.409 +
   1.410 +	/* 
   1.411 +	 * If it appears to be more than 1.1 seconds since the last trip
   1.412 +	 * through the calibration loop, the performance counter may
   1.413 +	 * have jumped forward. (See MSDN Knowledge Base article
   1.414 +	 * Q274323 for a description of the hardware problem that makes
   1.415 +	 * this test necessary.) If the counter jumps, we don't want
   1.416 +	 * to use it directly. Instead, we must return system time.
   1.417 +	 * Eventually, the calibration loop should recover.
   1.418 +	 */
   1.419 +	if ( curCounter.QuadPart - timeInfo.perfCounterLastCall.QuadPart
   1.420 +	     < 11 * timeInfo.curCounterFreq.QuadPart / 10 ) {
   1.421 +
   1.422 +	    curFileTime = timeInfo.fileTimeLastCall.QuadPart
   1.423 +		+ ( ( curCounter.QuadPart - timeInfo.perfCounterLastCall.QuadPart )
   1.424 +		    * 10000000 / timeInfo.curCounterFreq.QuadPart );
   1.425 +	    timeInfo.fileTimeLastCall.QuadPart = curFileTime;
   1.426 +	    timeInfo.perfCounterLastCall.QuadPart = curCounter.QuadPart;
   1.427 +	    usecSincePosixEpoch = ( curFileTime - posixEpoch.QuadPart ) / 10;
   1.428 +	    timePtr->sec = (long) ( usecSincePosixEpoch / 1000000 );
   1.429 +	    timePtr->usec = (unsigned long ) ( usecSincePosixEpoch % 1000000 );
   1.430 +	    useFtime = 0;
   1.431 +	}
   1.432 +
   1.433 +	LeaveCriticalSection( &timeInfo.cs );
   1.434 +    }
   1.435 +
   1.436 +    if ( useFtime ) {
   1.437 +	/* High resolution timer is not available.  Just use ftime */
   1.438 +
   1.439 +	ftime(&t);
   1.440 +	timePtr->sec = (long)t.time;
   1.441 +	timePtr->usec = t.millitm * 1000;
   1.442 +    }
   1.443 +}
   1.444 +
   1.445 +/*
   1.446 + *----------------------------------------------------------------------
   1.447 + *
   1.448 + * StopCalibration --
   1.449 + *
   1.450 + *	Turns off the calibration thread in preparation for exiting the
   1.451 + *	process.
   1.452 + *
   1.453 + * Results:
   1.454 + *	None.
   1.455 + *
   1.456 + * Side effects:
   1.457 + *	Sets the 'exitEvent' event in the 'timeInfo' structure to ask
   1.458 + *	the thread in question to exit, and waits for it to do so.
   1.459 + *
   1.460 + *----------------------------------------------------------------------
   1.461 + */
   1.462 +
   1.463 +static void
   1.464 +StopCalibration( ClientData unused )
   1.465 +				/* Client data is unused */
   1.466 +{
   1.467 +    SetEvent( timeInfo.exitEvent );
   1.468 +    WaitForSingleObject( timeInfo.calibrationThread, INFINITE );
   1.469 +    CloseHandle( timeInfo.exitEvent );
   1.470 +    CloseHandle( timeInfo.calibrationThread );
   1.471 +}
   1.472 +
   1.473 +/*
   1.474 + *----------------------------------------------------------------------
   1.475 + *
   1.476 + * TclpGetTZName --
   1.477 + *
   1.478 + *	Gets the current timezone string.
   1.479 + *
   1.480 + * Results:
   1.481 + *	Returns a pointer to a static string, or NULL on failure.
   1.482 + *
   1.483 + * Side effects:
   1.484 + *	None.
   1.485 + *
   1.486 + *----------------------------------------------------------------------
   1.487 + */
   1.488 +
   1.489 +char *
   1.490 +TclpGetTZName(int dst)
   1.491 +{
   1.492 +    size_t len;
   1.493 +    char *zone, *p;
   1.494 +    TIME_ZONE_INFORMATION tz;
   1.495 +    Tcl_Encoding encoding;
   1.496 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   1.497 +    char *name = tsdPtr->tzName;
   1.498 +
   1.499 +    /*
   1.500 +     * tzset() under Borland doesn't seem to set up tzname[] at all.
   1.501 +     * tzset() under MSVC has the following weird observed behavior:
   1.502 +     *	 First time we call "clock format [clock seconds] -format %Z -gmt 1"
   1.503 +     *	 we get "GMT", but on all subsequent calls we get the current time 
   1.504 +     *	 zone string, even though env(TZ) is GMT and the variable _timezone 
   1.505 +     *   is 0.
   1.506 +     */
   1.507 +
   1.508 +    name[0] = '\0';
   1.509 +
   1.510 +    zone = getenv("TZ");
   1.511 +    if (zone != NULL) {
   1.512 +	/*
   1.513 +	 * TZ is of form "NST-4:30NDT", where "NST" would be the
   1.514 +	 * name of the standard time zone for this area, "-4:30" is
   1.515 +	 * the offset from GMT in hours, and "NDT is the name of 
   1.516 +	 * the daylight savings time zone in this area.  The offset 
   1.517 +	 * and DST strings are optional.
   1.518 +	 */
   1.519 +
   1.520 +	len = strlen(zone);
   1.521 +	if (len > 3) {
   1.522 +	    len = 3;
   1.523 +	}
   1.524 +	if (dst != 0) {
   1.525 +	    /*
   1.526 +	     * Skip the offset string and get the DST string.
   1.527 +	     */
   1.528 +
   1.529 +	    p = zone + len;
   1.530 +	    p += strspn(p, "+-:0123456789");
   1.531 +	    if (*p != '\0') {
   1.532 +		zone = p;
   1.533 +		len = strlen(zone);
   1.534 +		if (len > 3) {
   1.535 +		    len = 3;
   1.536 +		}
   1.537 +	    }
   1.538 +	}
   1.539 +	Tcl_ExternalToUtf(NULL, NULL, zone, (int)len, 0, NULL, name,
   1.540 +		sizeof(tsdPtr->tzName), NULL, NULL, NULL);
   1.541 +    }
   1.542 +    if (name[0] == '\0') {
   1.543 +	if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_UNKNOWN) {
   1.544 +	    /*
   1.545 +	     * MSDN: On NT this is returned if DST is not used in
   1.546 +	     * the current TZ
   1.547 +	     */
   1.548 +	    dst = 0;
   1.549 +	}
   1.550 +	encoding = Tcl_GetEncoding(NULL, "unicode");
   1.551 +	Tcl_ExternalToUtf(NULL, encoding, 
   1.552 +		(char *) ((dst) ? tz.DaylightName : tz.StandardName), -1, 
   1.553 +		0, NULL, name, sizeof(tsdPtr->tzName), NULL, NULL, NULL);
   1.554 +	Tcl_FreeEncoding(encoding);
   1.555 +    } 
   1.556 +    return name;
   1.557 +}
   1.558 +
   1.559 +/*
   1.560 + *----------------------------------------------------------------------
   1.561 + *
   1.562 + * TclpGetDate --
   1.563 + *
   1.564 + *	This function converts between seconds and struct tm.  If
   1.565 + *	useGMT is true, then the returned date will be in Greenwich
   1.566 + *	Mean Time (GMT).  Otherwise, it will be in the local time zone.
   1.567 + *
   1.568 + * Results:
   1.569 + *	Returns a static tm structure.
   1.570 + *
   1.571 + * Side effects:
   1.572 + *	None.
   1.573 + *
   1.574 + *----------------------------------------------------------------------
   1.575 + */
   1.576 +
   1.577 +struct tm *
   1.578 +TclpGetDate(t, useGMT)
   1.579 +    TclpTime_t t;
   1.580 +    int useGMT;
   1.581 +{
   1.582 +    const time_t *tp = (const time_t *) t;
   1.583 +    struct tm *tmPtr;
   1.584 +    time_t time;
   1.585 +
   1.586 +    if (!useGMT) {
   1.587 +	tzset();
   1.588 +
   1.589 +	/*
   1.590 +	 * If we are in the valid range, let the C run-time library
   1.591 +	 * handle it.  Otherwise we need to fake it.  Note that this
   1.592 +	 * algorithm ignores daylight savings time before the epoch.
   1.593 +	 */
   1.594 +
   1.595 +	if (*tp >= 0) {
   1.596 +	    return localtime(tp);
   1.597 +	}
   1.598 +
   1.599 +	time = *tp - _timezone;
   1.600 +
   1.601 +	/*
   1.602 +	 * If we aren't near to overflowing the long, just add the bias and
   1.603 +	 * use the normal calculation.  Otherwise we will need to adjust
   1.604 +	 * the result at the end.
   1.605 +	 */
   1.606 +
   1.607 +	if (*tp < (LONG_MAX - 2 * SECSPERDAY)
   1.608 +		&& *tp > (LONG_MIN + 2 * SECSPERDAY)) {
   1.609 +	    tmPtr = ComputeGMT(&time);
   1.610 +	} else {
   1.611 +	    tmPtr = ComputeGMT(tp);
   1.612 +
   1.613 +	    tzset();
   1.614 +
   1.615 +	    /*
   1.616 +	     * Add the bias directly to the tm structure to avoid overflow.
   1.617 +	     * Propagate seconds overflow into minutes, hours and days.
   1.618 +	     */
   1.619 +
   1.620 +	    time = tmPtr->tm_sec - _timezone;
   1.621 +	    tmPtr->tm_sec = (int)(time % 60);
   1.622 +	    if (tmPtr->tm_sec < 0) {
   1.623 +		tmPtr->tm_sec += 60;
   1.624 +		time -= 60;
   1.625 +	    }
   1.626 +
   1.627 +	    time = tmPtr->tm_min + time/60;
   1.628 +	    tmPtr->tm_min = (int)(time % 60);
   1.629 +	    if (tmPtr->tm_min < 0) {
   1.630 +		tmPtr->tm_min += 60;
   1.631 +		time -= 60;
   1.632 +	    }
   1.633 +
   1.634 +	    time = tmPtr->tm_hour + time/60;
   1.635 +	    tmPtr->tm_hour = (int)(time % 24);
   1.636 +	    if (tmPtr->tm_hour < 0) {
   1.637 +		tmPtr->tm_hour += 24;
   1.638 +		time -= 24;
   1.639 +	    }
   1.640 +
   1.641 +	    time /= 24;
   1.642 +	    tmPtr->tm_mday += (int)time;
   1.643 +	    tmPtr->tm_yday += (int)time;
   1.644 +	    tmPtr->tm_wday = (tmPtr->tm_wday + (int)time) % 7;
   1.645 +	}
   1.646 +    } else {
   1.647 +	tmPtr = ComputeGMT(tp);
   1.648 +    }
   1.649 +    return tmPtr;
   1.650 +}
   1.651 +
   1.652 +/*
   1.653 + *----------------------------------------------------------------------
   1.654 + *
   1.655 + * ComputeGMT --
   1.656 + *
   1.657 + *	This function computes GMT given the number of seconds since
   1.658 + *	the epoch (midnight Jan 1 1970).
   1.659 + *
   1.660 + * Results:
   1.661 + *	Returns a (per thread) statically allocated struct tm.
   1.662 + *
   1.663 + * Side effects:
   1.664 + *	Updates the values of the static struct tm.
   1.665 + *
   1.666 + *----------------------------------------------------------------------
   1.667 + */
   1.668 +
   1.669 +static struct tm *
   1.670 +ComputeGMT(tp)
   1.671 +    const time_t *tp;
   1.672 +{
   1.673 +    struct tm *tmPtr;
   1.674 +    long tmp, rem;
   1.675 +    int isLeap;
   1.676 +    int *days;
   1.677 +    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   1.678 +
   1.679 +    tmPtr = &tsdPtr->tm;
   1.680 +
   1.681 +    /*
   1.682 +     * Compute the 4 year span containing the specified time.
   1.683 +     */
   1.684 +
   1.685 +    tmp = (long)(*tp / SECSPER4YEAR);
   1.686 +    rem = (LONG)(*tp % SECSPER4YEAR);
   1.687 +
   1.688 +    /*
   1.689 +     * Correct for weird mod semantics so the remainder is always positive.
   1.690 +     */
   1.691 +
   1.692 +    if (rem < 0) {
   1.693 +	tmp--;
   1.694 +	rem += SECSPER4YEAR;
   1.695 +    }
   1.696 +
   1.697 +    /*
   1.698 +     * Compute the year after 1900 by taking the 4 year span and adjusting
   1.699 +     * for the remainder.  This works because 2000 is a leap year, and
   1.700 +     * 1900/2100 are out of the range.
   1.701 +     */
   1.702 +
   1.703 +    tmp = (tmp * 4) + 70;
   1.704 +    isLeap = 0;
   1.705 +    if (rem >= SECSPERYEAR) {			  /* 1971, etc. */
   1.706 +	tmp++;
   1.707 +	rem -= SECSPERYEAR;
   1.708 +	if (rem >= SECSPERYEAR) {		  /* 1972, etc. */
   1.709 +	    tmp++;
   1.710 +	    rem -= SECSPERYEAR;
   1.711 +	    if (rem >= SECSPERYEAR + SECSPERDAY) { /* 1973, etc. */
   1.712 +		tmp++;
   1.713 +		rem -= SECSPERYEAR + SECSPERDAY;
   1.714 +	    } else {
   1.715 +		isLeap = 1;
   1.716 +	    }
   1.717 +	}
   1.718 +    }
   1.719 +    tmPtr->tm_year = tmp;
   1.720 +
   1.721 +    /*
   1.722 +     * Compute the day of year and leave the seconds in the current day in
   1.723 +     * the remainder.
   1.724 +     */
   1.725 +
   1.726 +    tmPtr->tm_yday = rem / SECSPERDAY;
   1.727 +    rem %= SECSPERDAY;
   1.728 +    
   1.729 +    /*
   1.730 +     * Compute the time of day.
   1.731 +     */
   1.732 +
   1.733 +    tmPtr->tm_hour = rem / 3600;
   1.734 +    rem %= 3600;
   1.735 +    tmPtr->tm_min = rem / 60;
   1.736 +    tmPtr->tm_sec = rem % 60;
   1.737 +
   1.738 +    /*
   1.739 +     * Compute the month and day of month.
   1.740 +     */
   1.741 +
   1.742 +    days = (isLeap) ? leapDays : normalDays;
   1.743 +    for (tmp = 1; days[tmp] < tmPtr->tm_yday; tmp++) {
   1.744 +    }
   1.745 +    tmPtr->tm_mon = --tmp;
   1.746 +    tmPtr->tm_mday = tmPtr->tm_yday - days[tmp];
   1.747 +
   1.748 +    /*
   1.749 +     * Compute day of week.  Epoch started on a Thursday.
   1.750 +     */
   1.751 +
   1.752 +    tmPtr->tm_wday = (long)(*tp / SECSPERDAY) + 4;
   1.753 +    if ((*tp % SECSPERDAY) < 0) {
   1.754 +	tmPtr->tm_wday--;
   1.755 +    }
   1.756 +    tmPtr->tm_wday %= 7;
   1.757 +    if (tmPtr->tm_wday < 0) {
   1.758 +	tmPtr->tm_wday += 7;
   1.759 +    }
   1.760 +
   1.761 +    return tmPtr;
   1.762 +}
   1.763 +
   1.764 +/*
   1.765 + *----------------------------------------------------------------------
   1.766 + *
   1.767 + * CalibrationThread --
   1.768 + *
   1.769 + *	Thread that manages calibration of the hi-resolution time
   1.770 + *	derived from the performance counter, to keep it synchronized
   1.771 + *	with the system clock.
   1.772 + *
   1.773 + * Parameters:
   1.774 + *	arg -- Client data from the CreateThread call.  This parameter
   1.775 + *             points to the static TimeInfo structure.
   1.776 + *
   1.777 + * Return value:
   1.778 + *	None.  This thread embeds an infinite loop.
   1.779 + *
   1.780 + * Side effects:
   1.781 + *	At an interval of 1 s, this thread performs virtual time discipline.
   1.782 + *
   1.783 + * Note: When this thread is entered, TclpInitLock has been called
   1.784 + * to safeguard the static storage.  There is therefore no synchronization
   1.785 + * in the body of this procedure.
   1.786 + *
   1.787 + *----------------------------------------------------------------------
   1.788 + */
   1.789 +
   1.790 +static DWORD WINAPI
   1.791 +CalibrationThread( LPVOID arg )
   1.792 +{
   1.793 +    FILETIME curFileTime;
   1.794 +    DWORD waitResult;
   1.795 +
   1.796 +    /* Get initial system time and performance counter */
   1.797 +
   1.798 +    GetSystemTimeAsFileTime( &curFileTime );
   1.799 +    QueryPerformanceCounter( &timeInfo.perfCounterLastCall );
   1.800 +    QueryPerformanceFrequency( &timeInfo.curCounterFreq );
   1.801 +    timeInfo.fileTimeLastCall.LowPart = curFileTime.dwLowDateTime;
   1.802 +    timeInfo.fileTimeLastCall.HighPart = curFileTime.dwHighDateTime;
   1.803 +
   1.804 +    ResetCounterSamples( timeInfo.fileTimeLastCall.QuadPart,
   1.805 +			 timeInfo.perfCounterLastCall.QuadPart,
   1.806 +			 timeInfo.curCounterFreq.QuadPart );
   1.807 +
   1.808 +    /*
   1.809 +     * Wake up the calling thread.  When it wakes up, it will release the
   1.810 +     * initialization lock.
   1.811 +     */
   1.812 +
   1.813 +    SetEvent( timeInfo.readyEvent );
   1.814 +
   1.815 +    /* Run the calibration once a second */
   1.816 +
   1.817 +    for ( ; ; ) {
   1.818 +
   1.819 +	/* If the exitEvent is set, break out of the loop. */
   1.820 +
   1.821 +	waitResult = WaitForSingleObjectEx(timeInfo.exitEvent, 1000, FALSE);
   1.822 +	if ( waitResult == WAIT_OBJECT_0 ) {
   1.823 +	    break;
   1.824 +	}
   1.825 +	UpdateTimeEachSecond();
   1.826 +    }
   1.827 +
   1.828 +    /* lint */
   1.829 +    return (DWORD) 0;
   1.830 +}
   1.831 +
   1.832 +/*
   1.833 + *----------------------------------------------------------------------
   1.834 + *
   1.835 + * UpdateTimeEachSecond --
   1.836 + *
   1.837 + *	Callback from the waitable timer in the clock calibration thread
   1.838 + *	that updates system time.
   1.839 + *
   1.840 + * Parameters:
   1.841 + *	info -- Pointer to the static TimeInfo structure
   1.842 + *
   1.843 + * Results:
   1.844 + *	None.
   1.845 + *
   1.846 + * Side effects:
   1.847 + *	Performs virtual time calibration discipline.
   1.848 + *
   1.849 + *----------------------------------------------------------------------
   1.850 + */
   1.851 +
   1.852 +static void
   1.853 +UpdateTimeEachSecond()
   1.854 +{
   1.855 +
   1.856 +    LARGE_INTEGER curPerfCounter;
   1.857 +				/* Current value returned from
   1.858 +				 * QueryPerformanceCounter */
   1.859 +
   1.860 +    FILETIME curSysTime;	/* Current system time */
   1.861 +
   1.862 +    LARGE_INTEGER curFileTime;	/* File time at the time this callback
   1.863 +				 * was scheduled. */
   1.864 +
   1.865 +    Tcl_WideInt estFreq;	/* Estimated perf counter frequency */
   1.866 +
   1.867 +    Tcl_WideInt vt0;		/* Tcl time right now */
   1.868 +    Tcl_WideInt vt1;		/* Tcl time one second from now */
   1.869 +
   1.870 +    Tcl_WideInt tdiff;		/* Difference between system clock and
   1.871 +				 * Tcl time. */
   1.872 +
   1.873 +    Tcl_WideInt driftFreq;	/* Frequency needed to drift virtual time
   1.874 +				 * into step over 1 second */
   1.875 +
   1.876 +    /*
   1.877 +     * Sample performance counter and system time.
   1.878 +     */
   1.879 +
   1.880 +    QueryPerformanceCounter( &curPerfCounter );
   1.881 +    GetSystemTimeAsFileTime( &curSysTime );
   1.882 +    curFileTime.LowPart = curSysTime.dwLowDateTime;
   1.883 +    curFileTime.HighPart = curSysTime.dwHighDateTime;
   1.884 +
   1.885 +    EnterCriticalSection( &timeInfo.cs );
   1.886 +
   1.887 +    /*
   1.888 +     * Several things may have gone wrong here that have to
   1.889 +     * be checked for.
   1.890 +     * (1) The performance counter may have jumped.
   1.891 +     * (2) The system clock may have been reset.
   1.892 +     *
   1.893 +     * In either case, we'll need to reinitialize the circular buffer
   1.894 +     * with samples relative to the current system time and the NOMINAL
   1.895 +     * performance frequency (not the actual, because the actual has
   1.896 +     * probably run slow in the first case). Our estimated frequency
   1.897 +     * will be the nominal frequency.
   1.898 +     */
   1.899 +
   1.900 +    /*
   1.901 +     * Store the current sample into the circular buffer of samples,
   1.902 +     * and estimate the performance counter frequency.
   1.903 +     */
   1.904 +
   1.905 +    estFreq = AccumulateSample( curPerfCounter.QuadPart,
   1.906 +				(Tcl_WideUInt) curFileTime.QuadPart );
   1.907 +
   1.908 +    /*
   1.909 +     * We want to adjust things so that time appears to be continuous.
   1.910 +     * Virtual file time, right now, is 
   1.911 +     *
   1.912 +     * vt0 = 10000000 * ( curPerfCounter - perfCounterLastCall )
   1.913 +     *       / curCounterFreq
   1.914 +     *       + fileTimeLastCall
   1.915 +     *
   1.916 +     * Ideally, we would like to drift the clock into place over a
   1.917 +     * period of 2 sec, so that virtual time 2 sec from now will be
   1.918 +     *
   1.919 +     * vt1 = 20000000 + curFileTime
   1.920 +     * 
   1.921 +     * The frequency that we need to use to drift the counter back into
   1.922 +     * place is estFreq * 20000000 / ( vt1 - vt0 )
   1.923 +     */
   1.924 +    
   1.925 +    vt0 = 10000000 * ( curPerfCounter.QuadPart
   1.926 +		       - timeInfo.perfCounterLastCall.QuadPart )
   1.927 +	/ timeInfo.curCounterFreq.QuadPart
   1.928 +	+ timeInfo.fileTimeLastCall.QuadPart;
   1.929 +    vt1 = 20000000 + curFileTime.QuadPart;
   1.930 +
   1.931 +    /*
   1.932 +     * If we've gotten more than a second away from system time,
   1.933 +     * then drifting the clock is going to be pretty hopeless.
   1.934 +     * Just let it jump. Otherwise, compute the drift frequency and
   1.935 +     * fill in everything.
   1.936 +     */
   1.937 +
   1.938 +    tdiff = vt0 - curFileTime.QuadPart;
   1.939 +    if ( tdiff > 10000000 || tdiff < -10000000 ) {
   1.940 +	timeInfo.fileTimeLastCall.QuadPart = curFileTime.QuadPart;
   1.941 +	timeInfo.curCounterFreq.QuadPart = estFreq;
   1.942 +    } else {
   1.943 +	driftFreq = estFreq * 20000000 / ( vt1 - vt0 );
   1.944 +	if ( driftFreq > 1003 * estFreq / 1000 ) {
   1.945 +	    driftFreq = 1003 * estFreq / 1000;
   1.946 +	}
   1.947 +	if ( driftFreq < 997 * estFreq / 1000 ) {
   1.948 +	    driftFreq = 997 * estFreq / 1000;
   1.949 +	}
   1.950 +	timeInfo.fileTimeLastCall.QuadPart = vt0;
   1.951 +	timeInfo.curCounterFreq.QuadPart = driftFreq;
   1.952 +    }
   1.953 +
   1.954 +    timeInfo.perfCounterLastCall.QuadPart = curPerfCounter.QuadPart;
   1.955 +
   1.956 +    LeaveCriticalSection( &timeInfo.cs );
   1.957 +
   1.958 +}
   1.959 +
   1.960 +/*
   1.961 + *----------------------------------------------------------------------
   1.962 + *
   1.963 + * ResetCounterSamples --
   1.964 + *
   1.965 + *	Fills the sample arrays in 'timeInfo' with dummy values that will
   1.966 + *	yield the current performance counter and frequency.
   1.967 + *
   1.968 + * Results:
   1.969 + *	None.
   1.970 + *
   1.971 + * Side effects:
   1.972 + *	The array of samples is filled in so that it appears that there
   1.973 + *	are SAMPLES samples at one-second intervals, separated by precisely
   1.974 + *	the given frequency.
   1.975 + *
   1.976 + *----------------------------------------------------------------------
   1.977 + */
   1.978 +
   1.979 +static void
   1.980 +ResetCounterSamples( Tcl_WideUInt fileTime,
   1.981 +				/* Current file time */
   1.982 +		     Tcl_WideInt perfCounter,
   1.983 +				/* Current performance counter */
   1.984 +		     Tcl_WideInt perfFreq )
   1.985 +				/* Target performance frequency */
   1.986 +{
   1.987 +    int i;
   1.988 +    for ( i = SAMPLES-1; i >= 0; --i ) {
   1.989 +	timeInfo.perfCounterSample[i] = perfCounter;
   1.990 +	timeInfo.fileTimeSample[i] = fileTime;
   1.991 +	perfCounter -= perfFreq;
   1.992 +	fileTime -= 10000000;
   1.993 +    }
   1.994 +    timeInfo.sampleNo = 0;
   1.995 +}
   1.996 +
   1.997 +/*
   1.998 + *----------------------------------------------------------------------
   1.999 + *
  1.1000 + * AccumulateSample --
  1.1001 + *
  1.1002 + *	Updates the circular buffer of performance counter and system
  1.1003 + *	time samples with a new data point.
  1.1004 + *
  1.1005 + * Results:
  1.1006 + *	None.
  1.1007 + *
  1.1008 + * Side effects:
  1.1009 + *	The new data point replaces the oldest point in the circular
  1.1010 + *	buffer, and the descriptive statistics are updated to accumulate
  1.1011 + *	the new point.
  1.1012 + *
  1.1013 + * Several things may have gone wrong here that have to
  1.1014 + * be checked for.
  1.1015 + * (1) The performance counter may have jumped.
  1.1016 + * (2) The system clock may have been reset.
  1.1017 + *
  1.1018 + * In either case, we'll need to reinitialize the circular buffer
  1.1019 + * with samples relative to the current system time and the NOMINAL
  1.1020 + * performance frequency (not the actual, because the actual has
  1.1021 + * probably run slow in the first case).
  1.1022 + */
  1.1023 +
  1.1024 +static Tcl_WideInt
  1.1025 +AccumulateSample( Tcl_WideInt perfCounter,
  1.1026 +		  Tcl_WideUInt fileTime )
  1.1027 +{
  1.1028 +    Tcl_WideUInt workFTSample;	/* File time sample being removed
  1.1029 +				 * from or added to the circular buffer */
  1.1030 +
  1.1031 +    Tcl_WideInt workPCSample;	/* Performance counter sample being
  1.1032 +				 * removed from or added to the circular 
  1.1033 +				 * buffer */
  1.1034 +
  1.1035 +    Tcl_WideUInt lastFTSample;	/* Last file time sample recorded */
  1.1036 +
  1.1037 +    Tcl_WideInt lastPCSample;	/* Last performance counter sample recorded */
  1.1038 +
  1.1039 +    Tcl_WideInt FTdiff;		/* Difference between last FT and current */
  1.1040 +
  1.1041 +    Tcl_WideInt PCdiff;		/* Difference between last PC and current */
  1.1042 +
  1.1043 +    Tcl_WideInt estFreq;	/* Estimated performance counter frequency */
  1.1044 +
  1.1045 +    /* Test for jumps and reset the samples if we have one. */
  1.1046 +
  1.1047 +    if ( timeInfo.sampleNo == 0 ) {
  1.1048 +	lastPCSample = timeInfo.perfCounterSample[ timeInfo.sampleNo
  1.1049 +						   + SAMPLES - 1 ];
  1.1050 +	lastFTSample = timeInfo.fileTimeSample[ timeInfo.sampleNo
  1.1051 +						+ SAMPLES - 1 ];
  1.1052 +    } else {
  1.1053 +	lastPCSample = timeInfo.perfCounterSample[ timeInfo.sampleNo - 1 ];
  1.1054 +	lastFTSample = timeInfo.fileTimeSample[ timeInfo.sampleNo - 1 ];
  1.1055 +    }
  1.1056 +    PCdiff = perfCounter - lastPCSample;
  1.1057 +    FTdiff = fileTime - lastFTSample;
  1.1058 +    if ( PCdiff < timeInfo.nominalFreq.QuadPart * 9 / 10
  1.1059 +	 || PCdiff > timeInfo.nominalFreq.QuadPart * 11 / 10
  1.1060 +	 || FTdiff < 9000000
  1.1061 +	 || FTdiff > 11000000 ) {
  1.1062 +	ResetCounterSamples( fileTime, perfCounter,
  1.1063 +			     timeInfo.nominalFreq.QuadPart );
  1.1064 +	return timeInfo.nominalFreq.QuadPart;
  1.1065 +
  1.1066 +    } else {
  1.1067 +    
  1.1068 +	/* Estimate the frequency */
  1.1069 +	
  1.1070 +	workPCSample = timeInfo.perfCounterSample[ timeInfo.sampleNo ];
  1.1071 +	workFTSample = timeInfo.fileTimeSample[ timeInfo.sampleNo ];
  1.1072 +	estFreq = 10000000 * ( perfCounter - workPCSample )
  1.1073 +	    / ( fileTime - workFTSample );
  1.1074 +	timeInfo.perfCounterSample[ timeInfo.sampleNo ] = perfCounter;
  1.1075 +	timeInfo.fileTimeSample[ timeInfo.sampleNo ] = (Tcl_WideInt) fileTime;
  1.1076 +	
  1.1077 +	/* Advance the sample number */
  1.1078 +	
  1.1079 +	if ( ++timeInfo.sampleNo >= SAMPLES ) {
  1.1080 +	    timeInfo.sampleNo = 0;
  1.1081 +	} 
  1.1082 +	
  1.1083 +	return estFreq;
  1.1084 +    }
  1.1085 +}
  1.1086 +
  1.1087 +/*
  1.1088 + *----------------------------------------------------------------------
  1.1089 + *
  1.1090 + * TclpGmtime --
  1.1091 + *
  1.1092 + *	Wrapper around the 'gmtime' library function to make it thread
  1.1093 + *	safe.
  1.1094 + *
  1.1095 + * Results:
  1.1096 + *	Returns a pointer to a 'struct tm' in thread-specific data.
  1.1097 + *
  1.1098 + * Side effects:
  1.1099 + *	Invokes gmtime or gmtime_r as appropriate.
  1.1100 + *
  1.1101 + *----------------------------------------------------------------------
  1.1102 + */
  1.1103 +
  1.1104 +struct tm *
  1.1105 +TclpGmtime( tt )
  1.1106 +    TclpTime_t_CONST tt;
  1.1107 +{
  1.1108 +    CONST time_t *timePtr = (CONST time_t *) tt;
  1.1109 +				/* Pointer to the number of seconds
  1.1110 +				 * since the local system's epoch */
  1.1111 +    /*
  1.1112 +     * The MS implementation of gmtime is thread safe because
  1.1113 +     * it returns the time in a block of thread-local storage,
  1.1114 +     * and Windows does not provide a Posix gmtime_r function.
  1.1115 +     */
  1.1116 +    return gmtime( timePtr );
  1.1117 +}
  1.1118 +
  1.1119 +/*
  1.1120 + *----------------------------------------------------------------------
  1.1121 + *
  1.1122 + * TclpLocaltime --
  1.1123 + *
  1.1124 + *	Wrapper around the 'localtime' library function to make it thread
  1.1125 + *	safe.
  1.1126 + *
  1.1127 + * Results:
  1.1128 + *	Returns a pointer to a 'struct tm' in thread-specific data.
  1.1129 + *
  1.1130 + * Side effects:
  1.1131 + *	Invokes localtime or localtime_r as appropriate.
  1.1132 + *
  1.1133 + *----------------------------------------------------------------------
  1.1134 + */
  1.1135 +
  1.1136 +struct tm *
  1.1137 +TclpLocaltime( tt )
  1.1138 +    TclpTime_t_CONST tt;
  1.1139 +{
  1.1140 +    CONST time_t *timePtr = (CONST time_t *) tt;
  1.1141 +				/* Pointer to the number of seconds
  1.1142 +				 * since the local system's epoch */
  1.1143 +
  1.1144 +    /*
  1.1145 +     * The MS implementation of localtime is thread safe because
  1.1146 +     * it returns the time in a block of thread-local storage,
  1.1147 +     * and Windows does not provide a Posix localtime_r function.
  1.1148 +     */
  1.1149 +    return localtime( timePtr );
  1.1150 +}