4 * Contains Windows specific versions of Tcl functions that
5 * obtain time values from the operating system.
7 * Copyright 1995-1998 by Sun Microsystems, Inc.
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15 #include "tclWinInt.h"
17 #define SECSPERDAY (60L * 60L * 24L)
18 #define SECSPERYEAR (SECSPERDAY * 365L)
19 #define SECSPER4YEAR (SECSPERYEAR * 4L + SECSPERDAY)
22 * The following arrays contain the day of year for the last day of
23 * each month, where index 1 is January.
26 static int normalDays[] = {
27 -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364
30 static int leapDays[] = {
31 -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
34 typedef struct ThreadSpecificData {
35 char tzName[64]; /* Time zone name */
36 struct tm tm; /* time information */
38 static Tcl_ThreadDataKey dataKey;
41 * Calibration interval for the high-resolution timer, in msec
44 static CONST unsigned long clockCalibrateWakeupInterval = 10000;
45 /* FIXME: 10 s -- should be about 10 min! */
48 * Data for managing high-resolution timers.
51 typedef struct TimeInfo {
53 CRITICAL_SECTION cs; /* Mutex guarding this structure */
55 int initialized; /* Flag == 1 if this structure is
58 int perfCounterAvailable; /* Flag == 1 if the hardware has a
59 * performance counter */
61 HANDLE calibrationThread; /* Handle to the thread that keeps the
62 * virtual clock calibrated. */
64 HANDLE readyEvent; /* System event used to
65 * trigger the requesting thread
66 * when the clock calibration procedure
67 * is initialized for the first time */
68 HANDLE exitEvent; /* Event to signal out of an exit handler
69 * to tell the calibration loop to
74 * The following values are used for calculating virtual time.
75 * Virtual time is always equal to:
76 * lastFileTime + (current perf counter - lastCounter)
77 * * 10000000 / curCounterFreq
78 * and lastFileTime and lastCounter are updated any time that
79 * virtual time is returned to a caller.
82 ULARGE_INTEGER lastFileTime;
83 LARGE_INTEGER lastCounter;
84 LARGE_INTEGER curCounterFreq;
87 * The next two values are used only in the calibration thread, to track
88 * the frequency of the performance counter.
91 LONGLONG lastPerfCounter; /* Performance counter the last time
92 * that UpdateClockEachSecond was called */
93 LONGLONG lastSysTime; /* System clock at the last time
94 * that UpdateClockEachSecond was called */
95 LONGLONG estPerfCounterFreq;
96 /* Current estimate of the counter frequency
97 * using the system clock as the standard */
101 static TimeInfo timeInfo = {
116 CONST static FILETIME posixEpoch = { 0xD53E8000, 0x019DB1DE };
119 * Declarations for functions defined later in this file.
122 static struct tm * ComputeGMT _ANSI_ARGS_((const time_t *tp));
123 static void StopCalibration _ANSI_ARGS_(( ClientData ));
124 static DWORD WINAPI CalibrationThread _ANSI_ARGS_(( LPVOID arg ));
125 static void UpdateTimeEachSecond _ANSI_ARGS_(( void ));
128 *----------------------------------------------------------------------
132 * This procedure returns the number of seconds from the epoch.
133 * On most Unix systems the epoch is Midnight Jan 1, 1970 GMT.
136 * Number of seconds from the epoch.
141 *----------------------------------------------------------------------
153 *----------------------------------------------------------------------
157 * This procedure returns a value that represents the highest
158 * resolution clock available on the system. There are no
159 * guarantees on what the resolution will be. In Tcl we will
160 * call this value a "click". The start time is also system
164 * Number of clicks from some start time.
169 *----------------------------------------------------------------------
176 * Use the Tcl_GetTime abstraction to get the time in microseconds,
177 * as nearly as we can, and return it.
180 Tcl_Time now; /* Current Tcl time */
181 unsigned long retval; /* Value to return */
184 retval = ( now.sec * 1000000 ) + now.usec;
190 *----------------------------------------------------------------------
194 * Determines the current timezone. The method varies wildly
195 * between different Platform implementations, so its hidden in
199 * Minutes west of GMT.
204 *----------------------------------------------------------------------
208 TclpGetTimeZone (currentTime)
209 unsigned long currentTime;
214 timeZone = _timezone / 60;
220 *----------------------------------------------------------------------
224 * Gets the current system time in seconds and microseconds
225 * since the beginning of the epoch: 00:00 UCT, January 1, 1970.
228 * Returns the current time in timePtr.
231 * On the first call, initializes a set of static variables to
232 * keep track of the base value of the performance counter, the
233 * corresponding wall clock (obtained through ftime) and the
234 * frequency of the performance counter. Also spins a thread
235 * whose function is to wake up periodically and monitor these
236 * values, adjusting them as necessary to correct for drift
237 * in the performance counter's oscillator.
239 *----------------------------------------------------------------------
244 Tcl_Time *timePtr; /* Location to store time information. */
249 /* Initialize static storage on the first trip through. */
252 * Note: Outer check for 'initialized' is a performance win
253 * since it avoids an extra mutex lock in the common case.
256 if ( !timeInfo.initialized ) {
258 if ( !timeInfo.initialized ) {
259 timeInfo.perfCounterAvailable
260 = QueryPerformanceFrequency( &timeInfo.curCounterFreq );
263 * Some hardware abstraction layers use the CPU clock
264 * in place of the real-time clock as a performance counter
265 * reference. This results in:
266 * - inconsistent results among the processors on
267 * multi-processor systems.
268 * - unpredictable changes in performance counter frequency
269 * on "gearshift" processors such as Transmeta and
272 * There seems to be no way to test whether the performance
273 * counter is reliable, but a useful heuristic is that
274 * if its frequency is 1.193182 MHz or 3.579545 MHz, it's
275 * derived from a colorburst crystal and is therefore
276 * the RTC rather than the TSC.
278 * A sloppier but serviceable heuristic is that the RTC crystal
279 * is normally less than 15 MHz while the TSC crystal is
280 * virtually assured to be greater than 100 MHz. Since Win98SE
281 * appears to fiddle with the definition of the perf counter
282 * frequency (perhaps in an attempt to calibrate the clock?)
283 * we use the latter rule rather than an exact match.
286 if ( timeInfo.perfCounterAvailable
287 /* The following lines would do an exact match on
289 * && timeInfo.curCounterFreq.QuadPart != (LONGLONG) 1193182
290 * && timeInfo.curCounterFreq.QuadPart != (LONGLONG) 3579545
292 && timeInfo.curCounterFreq.QuadPart > (LONGLONG) 15000000 ) {
293 timeInfo.perfCounterAvailable = FALSE;
297 * If the performance counter is available, start a thread to
301 if ( timeInfo.perfCounterAvailable ) {
303 InitializeCriticalSection( &timeInfo.cs );
304 timeInfo.readyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
305 timeInfo.exitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
306 timeInfo.calibrationThread = CreateThread( NULL,
312 SetThreadPriority( timeInfo.calibrationThread,
313 THREAD_PRIORITY_HIGHEST );
316 * Wait for the thread just launched to start running,
317 * and create an exit handler that kills it so that it
318 * doesn't outlive unloading tclXX.dll
321 WaitForSingleObject( timeInfo.readyEvent, INFINITE );
322 CloseHandle( timeInfo.readyEvent );
323 Tcl_CreateExitHandler( StopCalibration, (ClientData) NULL );
325 timeInfo.initialized = TRUE;
330 if ( timeInfo.perfCounterAvailable ) {
333 * Query the performance counter and use it to calculate the
337 LARGE_INTEGER curCounter;
338 /* Current performance counter */
340 LONGLONG curFileTime;
341 /* Current estimated time, expressed
342 * as 100-ns ticks since the Windows epoch */
344 static LARGE_INTEGER posixEpoch;
345 /* Posix epoch expressed as 100-ns ticks
346 * since the windows epoch */
348 LONGLONG usecSincePosixEpoch;
349 /* Current microseconds since Posix epoch */
351 posixEpoch.LowPart = 0xD53E8000;
352 posixEpoch.HighPart = 0x019DB1DE;
354 EnterCriticalSection( &timeInfo.cs );
356 QueryPerformanceCounter( &curCounter );
357 curFileTime = timeInfo.lastFileTime.QuadPart
358 + ( ( curCounter.QuadPart - timeInfo.lastCounter.QuadPart )
359 * 10000000 / timeInfo.curCounterFreq.QuadPart );
360 timeInfo.lastFileTime.QuadPart = curFileTime;
361 timeInfo.lastCounter.QuadPart = curCounter.QuadPart;
362 usecSincePosixEpoch = ( curFileTime - posixEpoch.QuadPart ) / 10;
363 timePtr->sec = (time_t) ( usecSincePosixEpoch / 1000000 );
364 timePtr->usec = (unsigned long ) ( usecSincePosixEpoch % 1000000 );
366 LeaveCriticalSection( &timeInfo.cs );
371 /* High resolution timer is not available. Just use ftime */
374 timePtr->sec = t.time;
375 timePtr->usec = t.millitm * 1000;
380 *----------------------------------------------------------------------
384 * Turns off the calibration thread in preparation for exiting the
391 * Sets the 'exitEvent' event in the 'timeInfo' structure to ask
392 * the thread in question to exit, and waits for it to do so.
394 *----------------------------------------------------------------------
398 StopCalibration( ClientData unused )
399 /* Client data is unused */
401 SetEvent( timeInfo.exitEvent );
402 WaitForSingleObject( timeInfo.calibrationThread, INFINITE );
403 CloseHandle( timeInfo.exitEvent );
404 CloseHandle( timeInfo.calibrationThread );
408 *----------------------------------------------------------------------
412 * Gets the current timezone string.
415 * Returns a pointer to a static string, or NULL on failure.
420 *----------------------------------------------------------------------
424 TclpGetTZName(int dst)
428 TIME_ZONE_INFORMATION tz;
429 Tcl_Encoding encoding;
430 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
431 char *name = tsdPtr->tzName;
434 * tzset() under Borland doesn't seem to set up tzname[] at all.
435 * tzset() under MSVC has the following weird observed behavior:
436 * First time we call "clock format [clock seconds] -format %Z -gmt 1"
437 * we get "GMT", but on all subsequent calls we get the current time
438 * zone string, even though env(TZ) is GMT and the variable _timezone
447 * TZ is of form "NST-4:30NDT", where "NST" would be the
448 * name of the standard time zone for this area, "-4:30" is
449 * the offset from GMT in hours, and "NDT is the name of
450 * the daylight savings time zone in this area. The offset
451 * and DST strings are optional.
460 * Skip the offset string and get the DST string.
464 p += strspn(p, "+-:0123456789");
473 Tcl_ExternalToUtf(NULL, NULL, zone, len, 0, NULL, name,
474 sizeof(tsdPtr->tzName), NULL, NULL, NULL);
476 if (name[0] == '\0') {
477 if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_UNKNOWN) {
479 * MSDN: On NT this is returned if DST is not used in
484 encoding = Tcl_GetEncoding(NULL, "unicode");
485 Tcl_ExternalToUtf(NULL, encoding,
486 (char *) ((dst) ? tz.DaylightName : tz.StandardName), -1,
487 0, NULL, name, sizeof(tsdPtr->tzName), NULL, NULL, NULL);
488 Tcl_FreeEncoding(encoding);
494 *----------------------------------------------------------------------
498 * This function converts between seconds and struct tm. If
499 * useGMT is true, then the returned date will be in Greenwich
500 * Mean Time (GMT). Otherwise, it will be in the local time zone.
503 * Returns a static tm structure.
508 *----------------------------------------------------------------------
512 TclpGetDate(t, useGMT)
516 const time_t *tp = (const time_t *) t;
524 * If we are in the valid range, let the C run-time library
525 * handle it. Otherwise we need to fake it. Note that this
526 * algorithm ignores daylight savings time before the epoch.
530 return localtime(tp);
533 time = *tp - _timezone;
536 * If we aren't near to overflowing the long, just add the bias and
537 * use the normal calculation. Otherwise we will need to adjust
538 * the result at the end.
541 if (*tp < (LONG_MAX - 2 * SECSPERDAY)
542 && *tp > (LONG_MIN + 2 * SECSPERDAY)) {
543 tmPtr = ComputeGMT(&time);
545 tmPtr = ComputeGMT(tp);
550 * Add the bias directly to the tm structure to avoid overflow.
551 * Propagate seconds overflow into minutes, hours and days.
554 time = tmPtr->tm_sec - _timezone;
555 tmPtr->tm_sec = (int)(time % 60);
556 if (tmPtr->tm_sec < 0) {
561 time = tmPtr->tm_min + time/60;
562 tmPtr->tm_min = (int)(time % 60);
563 if (tmPtr->tm_min < 0) {
568 time = tmPtr->tm_hour + time/60;
569 tmPtr->tm_hour = (int)(time % 24);
570 if (tmPtr->tm_hour < 0) {
571 tmPtr->tm_hour += 24;
576 tmPtr->tm_mday += time;
577 tmPtr->tm_yday += time;
578 tmPtr->tm_wday = (tmPtr->tm_wday + time) % 7;
581 tmPtr = ComputeGMT(tp);
587 *----------------------------------------------------------------------
591 * This function computes GMT given the number of seconds since
592 * the epoch (midnight Jan 1 1970).
595 * Returns a (per thread) statically allocated struct tm.
598 * Updates the values of the static struct tm.
600 *----------------------------------------------------------------------
611 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
616 * Compute the 4 year span containing the specified time.
619 tmp = *tp / SECSPER4YEAR;
620 rem = *tp % SECSPER4YEAR;
623 * Correct for weird mod semantics so the remainder is always positive.
632 * Compute the year after 1900 by taking the 4 year span and adjusting
633 * for the remainder. This works because 2000 is a leap year, and
634 * 1900/2100 are out of the range.
637 tmp = (tmp * 4) + 70;
639 if (rem >= SECSPERYEAR) { /* 1971, etc. */
642 if (rem >= SECSPERYEAR) { /* 1972, etc. */
645 if (rem >= SECSPERYEAR + SECSPERDAY) { /* 1973, etc. */
647 rem -= SECSPERYEAR + SECSPERDAY;
653 tmPtr->tm_year = tmp;
656 * Compute the day of year and leave the seconds in the current day in
660 tmPtr->tm_yday = rem / SECSPERDAY;
664 * Compute the time of day.
667 tmPtr->tm_hour = rem / 3600;
669 tmPtr->tm_min = rem / 60;
670 tmPtr->tm_sec = rem % 60;
673 * Compute the month and day of month.
676 days = (isLeap) ? leapDays : normalDays;
677 for (tmp = 1; days[tmp] < tmPtr->tm_yday; tmp++) {
679 tmPtr->tm_mon = --tmp;
680 tmPtr->tm_mday = tmPtr->tm_yday - days[tmp];
683 * Compute day of week. Epoch started on a Thursday.
686 tmPtr->tm_wday = (*tp / SECSPERDAY) + 4;
687 if ((*tp % SECSPERDAY) < 0) {
691 if (tmPtr->tm_wday < 0) {
699 *----------------------------------------------------------------------
701 * CalibrationThread --
703 * Thread that manages calibration of the hi-resolution time
704 * derived from the performance counter, to keep it synchronized
705 * with the system clock.
708 * arg -- Client data from the CreateThread call. This parameter
709 * points to the static TimeInfo structure.
712 * None. This thread embeds an infinite loop.
715 * At an interval of clockCalibrateWakeupInterval ms, this thread
716 * performs virtual time discipline.
718 * Note: When this thread is entered, TclpInitLock has been called
719 * to safeguard the static storage. There is therefore no synchronization
720 * in the body of this procedure.
722 *----------------------------------------------------------------------
726 CalibrationThread( LPVOID arg )
728 FILETIME curFileTime;
731 /* Get initial system time and performance counter */
733 GetSystemTimeAsFileTime( &curFileTime );
734 QueryPerformanceCounter( &timeInfo.lastCounter );
735 QueryPerformanceFrequency( &timeInfo.curCounterFreq );
736 timeInfo.lastFileTime.LowPart = curFileTime.dwLowDateTime;
737 timeInfo.lastFileTime.HighPart = curFileTime.dwHighDateTime;
739 /* Initialize the working storage for the calibration callback */
741 timeInfo.lastPerfCounter = timeInfo.lastCounter.QuadPart;
742 timeInfo.estPerfCounterFreq = timeInfo.curCounterFreq.QuadPart;
745 * Wake up the calling thread. When it wakes up, it will release the
746 * initialization lock.
749 SetEvent( timeInfo.readyEvent );
751 /* Run the calibration once a second */
755 /* If the exitEvent is set, break out of the loop. */
757 waitResult = WaitForSingleObjectEx(timeInfo.exitEvent, 1000, FALSE);
758 if ( waitResult == WAIT_OBJECT_0 ) {
761 UpdateTimeEachSecond();
769 *----------------------------------------------------------------------
771 * UpdateTimeEachSecond --
773 * Callback from the waitable timer in the clock calibration thread
774 * that updates system time.
777 * info -- Pointer to the static TimeInfo structure
783 * Performs virtual time calibration discipline.
785 *----------------------------------------------------------------------
789 UpdateTimeEachSecond()
792 LARGE_INTEGER curPerfCounter;
793 /* Current value returned from
794 * QueryPerformanceCounter */
796 LONGLONG perfCounterDiff; /* Difference between the current value
797 * and the value of 1 second ago */
799 FILETIME curSysTime; /* Current system time */
801 LARGE_INTEGER curFileTime; /* File time at the time this callback
804 LONGLONG fileTimeDiff; /* Elapsed time on the system clock
805 * since the last time this procedure
808 LONGLONG instantFreq; /* Instantaneous estimate of the
809 * performance counter frequency */
811 LONGLONG delta; /* Increment to add to the estimated
812 * performance counter frequency in the
815 LONGLONG fuzz; /* Tolerance for the perf counter frequency */
817 LONGLONG lowBound; /* Lower bound for the frequency assuming
818 * 1000 ppm tolerance */
820 LONGLONG hiBound; /* Upper bound for the frequency */
823 * Get current performance counter and system time.
826 QueryPerformanceCounter( &curPerfCounter );
827 GetSystemTimeAsFileTime( &curSysTime );
828 curFileTime.LowPart = curSysTime.dwLowDateTime;
829 curFileTime.HighPart = curSysTime.dwHighDateTime;
831 EnterCriticalSection( &timeInfo.cs );
834 * Find out how many ticks of the performance counter and the
835 * system clock have elapsed since we got into this procedure.
836 * Estimate the current frequency.
839 perfCounterDiff = curPerfCounter.QuadPart - timeInfo.lastPerfCounter;
840 timeInfo.lastPerfCounter = curPerfCounter.QuadPart;
841 fileTimeDiff = curFileTime.QuadPart - timeInfo.lastSysTime;
842 timeInfo.lastSysTime = curFileTime.QuadPart;
843 instantFreq = ( 10000000 * perfCounterDiff / fileTimeDiff );
846 * Consider this a timing glitch if instant frequency varies
847 * significantly from the current estimate.
850 fuzz = timeInfo.estPerfCounterFreq >> 10;
851 lowBound = timeInfo.estPerfCounterFreq - fuzz;
852 hiBound = timeInfo.estPerfCounterFreq + fuzz;
853 if ( instantFreq < lowBound || instantFreq > hiBound ) {
854 LeaveCriticalSection( &timeInfo.cs );
859 * Update the current estimate of performance counter frequency.
860 * This code is equivalent to the loop filter of a phase locked
864 delta = ( instantFreq - timeInfo.estPerfCounterFreq ) >> 6;
865 timeInfo.estPerfCounterFreq += delta;
868 * Update the current virtual time.
871 timeInfo.lastFileTime.QuadPart
872 += ( ( curPerfCounter.QuadPart - timeInfo.lastCounter.QuadPart )
873 * 10000000 / timeInfo.curCounterFreq.QuadPart );
874 timeInfo.lastCounter.QuadPart = curPerfCounter.QuadPart;
876 delta = curFileTime.QuadPart - timeInfo.lastFileTime.QuadPart;
877 if ( delta > 10000000 || delta < -10000000 ) {
880 * If the virtual time slip exceeds one second, then adjusting
881 * the counter frequency is hopeless (it'll take over fifteen
882 * minutes to line up with the system clock). The most likely
883 * cause of this large a slip is a sudden change to the system
884 * clock, perhaps because it was being corrected by wristwatch
885 * and eyeball. Accept the system time, and set the performance
886 * counter frequency to the current estimate.
889 timeInfo.lastFileTime.QuadPart = curFileTime.QuadPart;
890 timeInfo.curCounterFreq.QuadPart = timeInfo.estPerfCounterFreq;
895 * Compute a counter frequency that will cause virtual time to line
896 * up with system time one second from now, assuming that the
897 * performance counter continues to tick at timeInfo.estPerfCounterFreq.
900 timeInfo.curCounterFreq.QuadPart
901 = 10000000 * timeInfo.estPerfCounterFreq / ( delta + 10000000 );
904 * Limit frequency excursions to 1000 ppm from estimate
907 if ( timeInfo.curCounterFreq.QuadPart < lowBound ) {
908 timeInfo.curCounterFreq.QuadPart = lowBound;
909 } else if ( timeInfo.curCounterFreq.QuadPart > hiBound ) {
910 timeInfo.curCounterFreq.QuadPart = hiBound;
914 LeaveCriticalSection( &timeInfo.cs );