diff options
Diffstat (limited to 'unix/tclUnixTime.c')
-rw-r--r-- | unix/tclUnixTime.c | 594 |
1 files changed, 393 insertions, 201 deletions
diff --git a/unix/tclUnixTime.c b/unix/tclUnixTime.c index 68fc779..cf730a2 100644 --- a/unix/tclUnixTime.c +++ b/unix/tclUnixTime.c @@ -1,25 +1,28 @@ -/* +/* * tclUnixTime.c -- * - * Contains Unix specific versions of Tcl functions that - * obtain time values from the operating system. + * Contains Unix specific versions of Tcl functions that obtain time + * values from the operating system. * * Copyright (c) 1995 Sun Microsystems, Inc. * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tclInt.h" -#include "tclPort.h" #include <locale.h> +#if defined(TCL_WIDE_CLICKS) && defined(MAC_OSX_TCL) +#include <mach/mach_time.h> +#endif + #define TM_YEAR_BASE 1900 -#define IsLeapYear(x) ((x % 4 == 0) && (x % 100 != 0 || x % 400 == 0)) +#define IsLeapYear(x) (((x)%4 == 0) && ((x)%100 != 0 || (x)%400 == 0)) /* - * TclpGetDate is coded to return a pointer to a 'struct tm'. For - * thread safety, this structure must be in thread-specific data. - * The 'tmKey' variable is the key to this buffer. + * TclpGetDate is coded to return a pointer to a 'struct tm'. For thread + * safety, this structure must be in thread-specific data. The 'tmKey' + * variable is the key to this buffer. */ static Tcl_ThreadDataKey tmKey; @@ -29,29 +32,42 @@ typedef struct ThreadSpecificData { } ThreadSpecificData; /* - * If we fall back on the thread-unsafe versions of gmtime and localtime, - * use this mutex to try to protect them. + * If we fall back on the thread-unsafe versions of gmtime and localtime, use + * this mutex to try to protect them. */ TCL_DECLARE_MUTEX(tmMutex) -static char* lastTZ = NULL; /* Holds the last setting of the - * TZ environment variable, or an - * empty string if the variable was - * not set. */ +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 void SetTZIfNecessary(void); +static void CleanupMemory(ClientData clientData); +static void NativeScaleTime(Tcl_Time *timebuf, + ClientData clientData); +static void NativeGetTime(Tcl_Time *timebuf, + ClientData clientData); -/* Static functions declared in this file */ +/* + * TIP #233 (Virtualized Time): Data for the time hooks, if any. + */ -static void SetTZIfNecessary _ANSI_ARGS_((void)); -static void CleanupMemory _ANSI_ARGS_((ClientData)); +Tcl_GetTimeProc *tclGetTimeProcPtr = NativeGetTime; +Tcl_ScaleTimeProc *tclScaleTimeProcPtr = NativeScaleTime; +ClientData tclTimeClientData = NULL; /* *----------------------------------------------------------------------------- * * TclpGetSeconds -- * - * This procedure returns the number of seconds from the epoch. On - * most Unix systems the epoch is Midnight Jan 1, 1970 GMT. + * This procedure returns the number of seconds from the epoch. On most + * Unix systems the epoch is Midnight Jan 1, 1970 GMT. * * Results: * Number of seconds from the epoch. @@ -63,9 +79,9 @@ static void CleanupMemory _ANSI_ARGS_((ClientData)); */ unsigned long -TclpGetSeconds() +TclpGetSeconds(void) { - return time((time_t *) NULL); + return time(NULL); } /* @@ -74,8 +90,8 @@ TclpGetSeconds() * TclpGetClicks -- * * This procedure returns a value that represents the highest resolution - * clock available on the system. There are no garantees on what the - * resolution will be. In Tcl we will call this value a "click". The + * clock available on the system. There are no garantees on what the + * resolution will be. In Tcl we will call this value a "click". The * start time is also system dependant. * * Results: @@ -88,37 +104,134 @@ TclpGetSeconds() */ unsigned long -TclpGetClicks() +TclpGetClicks(void) { unsigned long now; + #ifdef NO_GETTOD - struct tms dummy; + if (tclGetTimeProcPtr != NativeGetTime) { + Tcl_Time time; + + (*tclGetTimeProcPtr) (&time, tclTimeClientData); + now = time.sec*1000000 + time.usec; + } else { + /* + * A semi-NativeGetTime, specialized to clicks. + */ + struct tms dummy; + + now = (unsigned long) times(&dummy); + } #else - struct timeval date; + Tcl_Time time; + + (*tclGetTimeProcPtr) (&time, tclTimeClientData); + now = time.sec*1000000 + time.usec; #endif -#ifdef NO_GETTOD - now = (unsigned long) times(&dummy); + return now; +} +#ifdef TCL_WIDE_CLICKS + +/* + *----------------------------------------------------------------------------- + * + * TclpGetWideClicks -- + * + * This procedure returns a WideInt value that represents the highest + * resolution clock available on the system. There are no garantees on + * what the resolution will be. In Tcl we will call this value a "click". + * The start time is also system dependant. + * + * Results: + * Number of WideInt clicks from some start time. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +Tcl_WideInt +TclpGetWideClicks(void) +{ + Tcl_WideInt now; + + if (tclGetTimeProcPtr != NativeGetTime) { + Tcl_Time time; + + (*tclGetTimeProcPtr) (&time, tclTimeClientData); + now = (Tcl_WideInt) (time.sec*1000000 + time.usec); + } else { +#ifdef MAC_OSX_TCL + now = (Tcl_WideInt) (mach_absolute_time() & INT64_MAX); #else - gettimeofday(&date, NULL); - now = date.tv_sec*1000000 + date.tv_usec; +#error Wide high-resolution clicks not implemented on this platform #endif + } return now; } /* + *----------------------------------------------------------------------------- + * + * TclpWideClicksToNanoseconds -- + * + * This procedure converts click values from the TclpGetWideClicks native + * resolution to nanosecond resolution. + * + * Results: + * Number of nanoseconds from some start time. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +double +TclpWideClicksToNanoseconds( + Tcl_WideInt clicks) +{ + double nsec; + + if (tclGetTimeProcPtr != NativeGetTime) { + nsec = clicks * 1000; + } else { +#ifdef MAC_OSX_TCL + static mach_timebase_info_data_t tb; + static uint64_t maxClicksForUInt64; + + if (!tb.denom) { + mach_timebase_info(&tb); + maxClicksForUInt64 = UINT64_MAX / tb.numer; + } + if ((uint64_t) clicks < maxClicksForUInt64) { + nsec = ((uint64_t) clicks) * tb.numer / tb.denom; + } else { + nsec = ((long double) (uint64_t) clicks) * tb.numer / tb.denom; + } +#else +#error Wide high-resolution clicks not implemented on this platform +#endif + } + + return nsec; +} +#endif /* TCL_WIDE_CLICKS */ + +/* *---------------------------------------------------------------------- * * TclpGetTimeZone -- * - * Determines the current timezone. The method varies wildly - * between different platform implementations, so its hidden in - * this function. + * Determines the current timezone. The method varies wildly between + * different platform implementations, so its hidden in this function. * * Results: - * The return value is the local time zone, measured in - * minutes away from GMT (-ve for east, +ve for west). + * The return value is the local time zone, measured in minutes away from + * GMT (-ve for east, +ve for west). * * Side effects: * None. @@ -127,102 +240,101 @@ TclpGetClicks() */ int -TclpGetTimeZone (currentTime) - Tcl_WideInt currentTime; +TclpGetTimeZone( + unsigned long currentTime) { + int timeZone; + /* - * 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. + * 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. */ +#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 = TclpLocaltime((TclpTime_t) &curTime); - int timeZone; + time_t curTime = (time_t) currentTime; + struct tm *timeDataPtr = TclpLocaltime(&curTime); - timeZone = timeDataPtr->tm_tzadj / 60; + timeZone = timeDataPtr->tm_tzadj / 60; if (timeDataPtr->tm_isdst) { - timeZone += 60; + timeZone += 60; } - - 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. */ +#define TCL_GOT_TIMEZONE + /* + * Struct tm contains tm_gmtoff - that value may be used. + */ - time_t curTime = (time_t) currentTime; - struct tm *timeDataPtr = TclpLocaltime((TclpTime_t) &curTime); - int timeZone; + time_t curTime = (time_t) currentTime; + struct tm *timeDataPtr = TclpLocaltime(&curTime); timeZone = -(timeDataPtr->tm_gmtoff / 60); if (timeDataPtr->tm_isdst) { - timeZone += 60; + timeZone += 60; } - - return timeZone; - #endif #if defined(HAVE_TIMEZONE_VAR) && !defined(TCL_GOT_TIMEZONE) && !defined(USE_DELTA_FOR_TZ) -# define TCL_GOT_TIMEZONE - - int timeZone; - - /* The 'timezone' external var is present and may be used. */ +#define TCL_GOT_TIMEZONE + /* + * The 'timezone' external var is present and may be used. + */ SetTZIfNecessary(); /* - * Note: this is not a typo in "timezone" below! See tzset - * documentation for details. + * Note: this is not a typo in "timezone" below! See tzset documentation + * for details. */ timeZone = timezone / 60; - return timeZone; - #endif -#if !defined(TCL_GOT_TIMEZONE) -#define TCL_GOT_TIMEZONE 1 +#if !defined(TCL_GOT_TIMEZONE) +#define TCL_GOT_TIMEZONE /* * Fallback - determine time zone with a known reference time. */ - int timeZone; time_t tt; struct tm *stm; - tt = 849268800L; /* 1996-11-29 12:00:00 GMT */ - stm = TclpLocaltime((TclpTime_t) &tt); /* eg 1996-11-29 6:00:00 CST6CDT */ - /* The calculation below assumes a max of +12 or -12 hours from GMT */ + + 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; + if (stm->tm_isdst) { + timeZone += 60; } - return timeZone; /* eg +360 for CST6CDT */ + + /* + * Now have offset for our known reference time, eg +360 for CST6CDT. + */ #endif #ifndef TCL_GOT_TIMEZONE /* - * Cause compile error, we don't know how to get timezone. + * Cause fatal 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 + return timeZone; } /* @@ -230,8 +342,11 @@ TclpGetTimeZone (currentTime) * * Tcl_GetTime -- * - * Gets the current system time in seconds and microseconds - * since the beginning of the epoch: 00:00 UCT, January 1, 1970. + * Gets the current system time in seconds and microseconds since the + * beginning of the epoch: 00:00 UCT, January 1, 1970. + * + * This function is hooked, allowing users to specify their own virtual + * system time. * * Results: * Returns the current time in timePtr. @@ -243,14 +358,10 @@ TclpGetTimeZone (currentTime) */ void -Tcl_GetTime(timePtr) - Tcl_Time *timePtr; /* Location to store time information. */ +Tcl_GetTime( + Tcl_Time *timePtr) /* Location to store time information. */ { - struct timeval tv; - - (void) gettimeofday(&tv, NULL); - timePtr->sec = tv.tv_sec; - timePtr->usec = tv.tv_usec; + (*tclGetTimeProcPtr) (timePtr, tclTimeClientData); } /* @@ -258,9 +369,9 @@ Tcl_GetTime(timePtr) * * TclpGetDate -- * - * This function converts between seconds and struct tm. If - * useGMT is true, then the returned date will be in Greenwich - * Mean Time (GMT). Otherwise, it will be in the local time zone. + * This function converts between seconds and struct tm. If useGMT is + * true, then the returned date will be in Greenwich Mean Time (GMT). + * Otherwise, it will be in the local time zone. * * Results: * Returns a static tm structure. @@ -272,9 +383,9 @@ Tcl_GetTime(timePtr) */ struct tm * -TclpGetDate(time, useGMT) - TclpTime_t time; - int useGMT; +TclpGetDate( + CONST time_t *time, + int useGMT) { if (useGMT) { return TclpGmtime(time); @@ -286,48 +397,9 @@ TclpGetDate(time, useGMT) /* *---------------------------------------------------------------------- * - * TclpStrftime -- - * - * On Unix, we can safely call the native strftime implementation, - * and also ignore the useGMT parameter. - * - * Results: - * The normal strftime result. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -size_t -TclpStrftime(s, maxsize, format, t, useGMT) - char *s; - size_t maxsize; - CONST char *format; - CONST struct tm *t; - int useGMT; -{ - if (format[0] == '%' && format[1] == 'Q') { - /* Format as a stardate */ - sprintf(s, "Stardate %2d%03d.%01d", - (((t->tm_year + TM_YEAR_BASE) + 377) - 2323), - (((t->tm_yday + 1) * 1000) / - (365 + IsLeapYear((t->tm_year + TM_YEAR_BASE)))), - (((t->tm_hour * 60) + t->tm_min)/144)); - return(strlen(s)); - } - setlocale(LC_TIME, ""); - return strftime(s, maxsize, format, t); -} - -/* - *---------------------------------------------------------------------- - * * TclpGmtime -- * - * Wrapper around the 'gmtime' library function to make it thread - * safe. + * Wrapper around the 'gmtime' library function to make it thread safe. * * Results: * Returns a pointer to a 'struct tm' in thread-specific data. @@ -339,37 +411,36 @@ TclpStrftime(s, maxsize, format, t, useGMT) */ struct tm * -TclpGmtime( tt ) - TclpTime_t_CONST tt; +TclpGmtime( + CONST time_t *timePtr) /* Pointer to the number of seconds since the + * local system's epoch */ { - CONST time_t *timePtr = (CONST time_t *) tt; - /* Pointer to the number of seconds - * since the local system's epoch */ - /* * Get a thread-local buffer to hold the returned time. */ - ThreadSpecificData *tsdPtr = TCL_TSD_INIT( &tmKey ); + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tmKey); + #ifdef HAVE_GMTIME_R - gmtime_r(timePtr, &( tsdPtr->gmtime_buf )); + gmtime_r(timePtr, &(tsdPtr->gmtime_buf)); #else - Tcl_MutexLock( &tmMutex ); - memcpy( (VOID *) &( tsdPtr->gmtime_buf ), - (VOID *) gmtime( timePtr ), - sizeof( struct tm ) ); - Tcl_MutexUnlock( &tmMutex ); -#endif - return &( tsdPtr->gmtime_buf ); + Tcl_MutexLock(&tmMutex); + memcpy(&(tsdPtr->gmtime_buf), gmtime(timePtr), sizeof(struct tm)); + Tcl_MutexUnlock(&tmMutex); +#endif + + return &(tsdPtr->gmtime_buf); } + /* * Forwarder for obsolete item in Stubs */ -struct tm* -TclpGmtime_unix( timePtr ) - TclpTime_t_CONST timePtr; + +struct tm * +TclpGmtime_unix( + CONST time_t *timePtr) { - return TclpGmtime( timePtr ); + return TclpGmtime(timePtr); } /* @@ -390,87 +461,199 @@ TclpGmtime_unix( timePtr ) */ struct tm * -TclpLocaltime( tt ) - TclpTime_t_CONST tt; +TclpLocaltime( + CONST time_t *timePtr) /* Pointer to the number of seconds since the + * local system's epoch */ { - CONST time_t *timePtr = (CONST time_t *) tt; - /* Pointer to the number of seconds - * since the local system's epoch */ /* * Get a thread-local buffer to hold the returned time. */ - ThreadSpecificData *tsdPtr = TCL_TSD_INIT( &tmKey ); + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tmKey); + SetTZIfNecessary(); #ifdef HAVE_LOCALTIME_R - localtime_r( timePtr, &( tsdPtr->localtime_buf ) ); + 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 ); + Tcl_MutexLock(&tmMutex); + memcpy(&(tsdPtr->localtime_buf), localtime(timePtr), sizeof(struct tm)); + Tcl_MutexUnlock(&tmMutex); +#endif + + return &(tsdPtr->localtime_buf); } /* * Forwarder for obsolete item in Stubs */ struct tm* -TclpLocaltime_unix( timePtr ) - TclpTime_t_CONST timePtr; +TclpLocaltime_unix( + CONST time_t *timePtr) { - return TclpLocaltime( timePtr ); + return TclpLocaltime(timePtr); } + +/* + *---------------------------------------------------------------------- + * + * Tcl_SetTimeProc -- + * + * TIP #233 (Virtualized Time): Registers two handlers for the + * virtualization of Tcl's access to time information. + * + * Results: + * None. + * + * Side effects: + * Remembers the handlers, alters core behaviour. + * + *---------------------------------------------------------------------- + */ +void +Tcl_SetTimeProc( + Tcl_GetTimeProc *getProc, + Tcl_ScaleTimeProc *scaleProc, + ClientData clientData) +{ + tclGetTimeProcPtr = getProc; + tclScaleTimeProcPtr = scaleProc; + tclTimeClientData = clientData; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_QueryTimeProc -- + * + * TIP #233 (Virtualized Time): Query which time handlers are registered. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_QueryTimeProc( + Tcl_GetTimeProc **getProc, + Tcl_ScaleTimeProc **scaleProc, + ClientData *clientData) +{ + if (getProc) { + *getProc = tclGetTimeProcPtr; + } + if (scaleProc) { + *scaleProc = tclScaleTimeProcPtr; + } + if (clientData) { + *clientData = tclTimeClientData; + } +} + +/* + *---------------------------------------------------------------------- + * + * NativeScaleTime -- + * + * TIP #233: Scale from virtual time to the real-time. For native scaling + * the relationship is 1:1 and nothing has to be done. + * + * Results: + * Scales the time in timePtr. + * + * Side effects: + * See above. + * + *---------------------------------------------------------------------- + */ + +static void +NativeScaleTime( + Tcl_Time *timePtr, + ClientData clientData) +{ + /* Native scale is 1:1. Nothing is done */ +} + +/* + *---------------------------------------------------------------------- + * + * NativeGetTime -- + * + * TIP #233: Gets the current system time in seconds and microseconds + * since the beginning of the epoch: 00:00 UCT, January 1, 1970. + * + * Results: + * Returns the current time in timePtr. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +NativeGetTime( + Tcl_Time *timePtr, + ClientData clientData) +{ + struct timeval tv; + + (void) gettimeofday(&tv, NULL); + timePtr->sec = tv.tv_sec; + timePtr->usec = tv.tv_usec; +} /* *---------------------------------------------------------------------- * * SetTZIfNecessary -- * - * Determines whether a call to 'tzset' is needed prior to the - * next call to 'localtime' or examination of the 'timezone' variable. + * 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. + * 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() { +SetTZIfNecessary(void) +{ + CONST char *newTZ = getenv("TZ"); - CONST char* newTZ = getenv( "TZ" ); Tcl_MutexLock(&tmMutex); - if ( newTZ == NULL ) { + if (newTZ == NULL) { newTZ = ""; } - if ( lastTZ == NULL || strcmp( lastTZ, newTZ ) ) { - tzset(); - if ( lastTZ == NULL ) { - Tcl_CreateExitHandler( CleanupMemory, (ClientData) NULL ); + if (lastTZ == NULL || strcmp(lastTZ, newTZ)) { + tzset(); + if (lastTZ == NULL) { + Tcl_CreateExitHandler(CleanupMemory, (ClientData) NULL); } else { - Tcl_Free( lastTZ ); + Tcl_Free(lastTZ); } - lastTZ = ckalloc( strlen( newTZ ) + 1 ); - strcpy( lastTZ, newTZ ); + lastTZ = ckalloc(strlen(newTZ) + 1); + strcpy(lastTZ, newTZ); } Tcl_MutexUnlock(&tmMutex); - } - + /* *---------------------------------------------------------------------- * * CleanupMemory -- * - * Releases the private copy of the TZ environment variable - * upon exit from Tcl. + * Releases the private copy of the TZ environment variable upon exit + * from Tcl. * * Results: * None. @@ -482,7 +665,16 @@ SetTZIfNecessary() { */ static void -CleanupMemory( ClientData ignored ) +CleanupMemory( + ClientData ignored) { - ckfree( lastTZ ); + ckfree(lastTZ); } + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ |