summaryrefslogtreecommitdiffstats
path: root/unix/tclUnixTime.c
diff options
context:
space:
mode:
Diffstat (limited to 'unix/tclUnixTime.c')
-rw-r--r--unix/tclUnixTime.c578
1 files changed, 352 insertions, 226 deletions
diff --git a/unix/tclUnixTime.c b/unix/tclUnixTime.c
index fc72f35..926e8f4 100644
--- a/unix/tclUnixTime.c
+++ b/unix/tclUnixTime.c
@@ -1,54 +1,73 @@
-/*
+/*
* 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.
- *
- * RCS: @(#) $Id: tclUnixTime.c,v 1.15 2002/07/19 12:31:10 dkf Exp $
+ * 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;
+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 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
+
+static char *lastTZ = NULL; /* Holds the last setting of the TZ
+ * environment variable, or an empty string if
+ * the variable was not set. */
/*
- * Forward declarations for procedures defined later in this file.
+ * 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(void);
+static void CleanupMemory(ClientData clientData);
+static void NativeScaleTime(Tcl_Time *timebuf,
+ ClientData clientData);
+static void NativeGetTime(Tcl_Time *timebuf,
+ ClientData clientData);
+
+/*
+ * TIP #233 (Virtualized Time): Data for the time hooks, if any.
+ */
+
+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.
@@ -56,23 +75,23 @@ static struct tm *ThreadSafeLocalTime _ANSI_ARGS_(( CONST time_t* ));
* Side effects:
* None.
*
- *-----------------------------------------------------------------------------
+ *----------------------------------------------------------------------
*/
unsigned long
-TclpGetSeconds()
+TclpGetSeconds(void)
{
- return time((time_t *) NULL);
+ return time(NULL);
}
/*
- *-----------------------------------------------------------------------------
+ *----------------------------------------------------------------------
*
* 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:
@@ -81,42 +100,51 @@ TclpGetSeconds()
* Side effects:
* None.
*
- *-----------------------------------------------------------------------------
+ *----------------------------------------------------------------------
*/
unsigned long
-TclpGetClicks()
+TclpGetClicks(void)
{
unsigned long now;
-#ifdef NO_GETTOD
- struct tms dummy;
-#else
- struct timeval date;
- struct timezone tz;
-#endif
#ifdef NO_GETTOD
- now = (unsigned long) times(&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
- gettimeofday(&date, &tz);
- now = date.tv_sec*1000000 + date.tv_usec;
+ Tcl_Time time;
+
+ tclGetTimeProcPtr(&time, tclTimeClientData);
+ now = time.sec*1000000 + time.usec;
#endif
return now;
}
+#ifdef TCL_WIDE_CLICKS
/*
*----------------------------------------------------------------------
*
- * TclpGetTimeZone --
+ * TclpGetWideClicks --
*
- * Determines the current timezone. The method varies wildly
- * between different platform implementations, so its hidden in
- * this function.
+ * 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:
- * The return value is the local time zone, measured in
- * minutes away from GMT (-ve for east, +ve for west).
+ * Number of WideInt clicks from some start time.
*
* Side effects:
* None.
@@ -124,127 +152,85 @@ TclpGetClicks()
*----------------------------------------------------------------------
*/
-int
-TclpGetTimeZone (currentTime)
- unsigned long currentTime;
+Tcl_WideInt
+TclpGetWideClicks(void)
{
- /*
- * 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.
- */
-
-#if defined(HAVE_TM_TZADJ)
-# define TCL_GOT_TIMEZONE
- time_t curTime = (time_t) currentTime;
- struct tm *timeDataPtr = ThreadSafeLocalTime(&curTime);
- int timeZone;
-
- timeZone = timeDataPtr->tm_tzadj / 60;
- if (timeDataPtr->tm_isdst) {
- timeZone += 60;
- }
-
- return timeZone;
-#endif
+ Tcl_WideInt now;
-#if defined(HAVE_TM_GMTOFF) && !defined (TCL_GOT_TIMEZONE)
-# define TCL_GOT_TIMEZONE
- time_t curTime = (time_t) currentTime;
- struct tm *timeDataPtr = ThreadSafeLocalTime(&curTime);
- int timeZone;
+ if (tclGetTimeProcPtr != NativeGetTime) {
+ Tcl_Time time;
- timeZone = -(timeDataPtr->tm_gmtoff / 60);
- if (timeDataPtr->tm_isdst) {
- timeZone += 60;
- }
-
- 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)
-# define TCL_GOT_TIMEZONE
- static int setTZ = 0;
-#ifdef TCL_THREADS
- static Tcl_Mutex tzMutex;
+ 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
+#error Wide high-resolution clicks not implemented on this platform
#endif
- int timeZone;
-
- Tcl_MutexLock(&tzMutex);
- if (!setTZ) {
- tzset();
- setTZ = 1;
}
- Tcl_MutexUnlock(&tzMutex);
- /*
- * Note: this is not a typo in "timezone" below! See tzset
- * documentation for details.
- */
+ 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.
+ *
+ *----------------------------------------------------------------------
+ */
- timeZone = timezone / 60;
+double
+TclpWideClicksToNanoseconds(
+ Tcl_WideInt clicks)
+{
+ double nsec;
- return timeZone;
+ 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
-
-#if !defined(NO_GETTOD) && !defined (TCL_GOT_TIMEZONE)
-# define TCL_GOT_TIMEZONE
- struct timeval tv;
- struct timezone tz;
- int timeZone;
-
- gettimeofday(&tv, &tz);
- timeZone = tz.tz_minuteswest;
- if (tz.tz_dsttime) {
- timeZone += 60;
}
-
- return timeZone;
-#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.
-#endif
+ return nsec;
}
+#endif /* TCL_WIDE_CLICKS */
/*
*----------------------------------------------------------------------
*
* 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.
@@ -256,15 +242,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;
- struct timezone tz;
-
- (void) gettimeofday(&tv, &tz);
- timePtr->sec = tv.tv_sec;
- timePtr->usec = tv.tv_usec;
+ tclGetTimeProcPtr(timePtr, tclTimeClientData);
}
/*
@@ -272,9 +253,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.
@@ -286,133 +267,278 @@ Tcl_GetTime(timePtr)
*/
struct tm *
-TclpGetDate(time, useGMT)
- TclpTime_t time;
- int useGMT;
+TclpGetDate(
+ const time_t *time,
+ int useGMT)
{
- CONST time_t *tp = (CONST time_t *)time;
-
if (useGMT) {
- return ThreadSafeGMTime(tp);
+ return TclpGmtime(time);
} else {
- return ThreadSafeLocalTime(tp);
+ return TclpLocaltime(time);
}
}
/*
*----------------------------------------------------------------------
*
- * TclpStrftime --
+ * TclpGmtime --
*
- * On Unix, we can safely call the native strftime implementation,
- * and also ignore the useGMT parameter.
+ * Wrapper around the 'gmtime' library function to make it thread safe.
*
* Results:
- * The normal strftime result.
+ * Returns a pointer to a 'struct tm' in thread-specific data.
*
* Side effects:
- * None.
+ * Invokes gmtime or gmtime_r as appropriate.
*
*----------------------------------------------------------------------
*/
-size_t
-TclpStrftime(s, maxsize, format, t, useGMT)
- char *s;
- size_t maxsize;
- CONST char *format;
- CONST struct tm *t;
- int useGMT;
+struct tm *
+TclpGmtime(
+ const time_t *timePtr) /* Pointer to the number of seconds since the
+ * local system's epoch */
{
- 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);
+ /*
+ * Get a thread-local buffer to hold the returned time.
+ */
+
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tmKey);
+
+#ifdef HAVE_GMTIME_R
+ gmtime_r(timePtr, &tsdPtr->gmtime_buf);
+#else
+ Tcl_MutexLock(&tmMutex);
+ memcpy(&tsdPtr->gmtime_buf, gmtime(timePtr), sizeof(struct tm));
+ Tcl_MutexUnlock(&tmMutex);
+#endif
+
+ return &tsdPtr->gmtime_buf;
}
/*
*----------------------------------------------------------------------
*
- * ThreadSafeGMTime --
+ * TclpLocaltime --
*
- * Wrapper around the 'gmtime' library function to make it thread
+ * Wrapper around the 'localtime' library function to make it thread
* safe.
*
* Results:
* Returns a pointer to a 'struct tm' in thread-specific data.
*
* Side effects:
- * Invokes gmtime or gmtime_r as appropriate.
+ * Invokes localtime or localtime_r as appropriate.
*
*----------------------------------------------------------------------
*/
-static struct tm *
-ThreadSafeGMTime(timePtr)
- CONST time_t *timePtr; /* Pointer to the number of seconds
- * since the local system's epoch
- */
-
+struct tm *
+TclpLocaltime(
+ const time_t *timePtr) /* Pointer to the number of seconds 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));
-#ifdef HAVE_GMTIME_R
- gmtime_r(timePtr, tmPtr);
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tmKey);
+
+ SetTZIfNecessary();
+#ifdef HAVE_LOCALTIME_R
+ localtime_r(timePtr, &tsdPtr->localtime_buf);
#else
Tcl_MutexLock(&tmMutex);
- memcpy((VOID *) tmPtr, (VOID *) gmtime(timePtr), sizeof(struct tm));
+ memcpy(&tsdPtr->localtime_buf, localtime(timePtr), sizeof(struct tm));
Tcl_MutexUnlock(&tmMutex);
-#endif
- return tmPtr;
+#endif
+
+ return &tsdPtr->localtime_buf;
}
/*
*----------------------------------------------------------------------
*
- * ThreadSafeLocalTime --
+ * Tcl_SetTimeProc --
*
- * Wrapper around the 'localtime' library function to make it thread
- * safe.
+ * TIP #233 (Virtualized Time): Registers two handlers for the
+ * virtualization of Tcl's access to time information.
*
* Results:
- * Returns a pointer to a 'struct tm' in thread-specific data.
+ * None.
*
* Side effects:
- * Invokes localtime or localtime_r as appropriate.
+ * Remembers the handlers, alters core behaviour.
*
*----------------------------------------------------------------------
*/
-static struct tm *
-ThreadSafeLocalTime(timePtr)
- CONST time_t *timePtr; /* Pointer to the number of seconds
- * since the local system's epoch
- */
+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)
{
- /*
- * Get a thread-local buffer to hold the returned time.
- */
+ 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.
+ *
+ * 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(void)
+{
+ const char *newTZ = getenv("TZ");
- struct tm *tmPtr = (struct tm *)
- Tcl_GetThreadData(&tmKey, sizeof(struct tm));
-#ifdef HAVE_LOCALTIME_R
- localtime_r(timePtr, tmPtr);
-#else
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, NULL);
+ } else {
+ ckfree(lastTZ);
+ }
+ lastTZ = ckalloc(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)
+{
+ ckfree(lastTZ);
+}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */