Logo Search packages:      
Sourcecode: tcl8.5 version File versions  Download package

tclUnixTime.c

/*
 * tclUnixTime.c --
 *
 *    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.33 2007/12/13 15:28:42 dgp Exp $
 */

#include "tclInt.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))

/*
 * 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.
 */

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 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);

/*
 * 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.
 *
 * Results:
 *    Number of seconds from the epoch.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

unsigned long
TclpGetSeconds(void)
{
    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
 *    start time is also system dependant.
 *
 * Results:
 *    Number of clicks from some start time.
 *
 * Side effects:
 *    None.
 *
 *-----------------------------------------------------------------------------
 */

unsigned long
TclpGetClicks(void)
{
    unsigned long now;

#ifdef NO_GETTOD
    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
    Tcl_Time time;

    (*tclGetTimeProcPtr) (&time, tclTimeClientData);
    now = time.sec*1000000 + time.usec;
#endif

    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
#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.
 *
 * Results:
 *    The return value is the local time zone, measured in minutes away from
 *    GMT (-ve for east, +ve for west).
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

int
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.
     */

#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(&curTime);

    timeZone = timeDataPtr->tm_tzadj / 60;
    if (timeDataPtr->tm_isdst) {
      timeZone += 60;
    }
#endif

#if defined(HAVE_TM_GMTOFF) && !defined (TCL_GOT_TIMEZONE)
#define TCL_GOT_TIMEZONE
    /*
     * Struct tm contains tm_gmtoff - that value may be used.
     */

    time_t curTime = (time_t) currentTime;
    struct tm *timeDataPtr = TclpLocaltime(&curTime);

    timeZone = -(timeDataPtr->tm_gmtoff / 60);
    if (timeDataPtr->tm_isdst) {
      timeZone += 60;
    }
#endif

#if defined(HAVE_TIMEZONE_VAR) && !defined(TCL_GOT_TIMEZONE) && !defined(USE_DELTA_FOR_TZ)
#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.
     */

    timeZone = timezone / 60;
#endif

#if !defined(TCL_GOT_TIMEZONE)
#define TCL_GOT_TIMEZONE
    /*
     * Fallback - determine time zone with a known reference time.
     */

    time_t tt;
    struct tm *stm;

    tt = 849268800L;          /* 1996-11-29 12:00:00  GMT */
    stm = TclpLocaltime(&tt); /* eg 1996-11-29  6:00:00  CST6CDT */

    /*
     * The calculation below assumes a max of +12 or -12 hours from GMT.
     */

    timeZone = (12 - stm->tm_hour)*60 + (0 - stm->tm_min);
    if (stm->tm_isdst) {
      timeZone += 60;
    }

    /*
     * Now have offset for our known reference time, eg +360 for CST6CDT.
     */
#endif

#ifndef TCL_GOT_TIMEZONE
    /*
     * Cause fatal compile error, we don't know how to get timezone.
     */

#error autoconf did not figure out how to determine the timezone.
#endif

    return timeZone;
}

/*
 *----------------------------------------------------------------------
 *
 * Tcl_GetTime --
 *
 *    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.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

void
Tcl_GetTime(
    Tcl_Time *timePtr)        /* Location to store time information. */
{
    (*tclGetTimeProcPtr) (timePtr, tclTimeClientData);
}

/*
 *----------------------------------------------------------------------
 *
 * 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.
 *
 * Results:
 *    Returns a static tm structure.
 *
 * Side effects:
 *    None.
 *
 *----------------------------------------------------------------------
 */

struct tm *
TclpGetDate(
    CONST time_t *time,
    int useGMT)
{
    if (useGMT) {
      return TclpGmtime(time);
    } else {
      return TclpLocaltime(time);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TclpGmtime --
 *
 *    Wrapper around the 'gmtime' 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.
 *
 *----------------------------------------------------------------------
 */

struct tm *
TclpGmtime(
    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.
     */

    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);
}

/*
 * Forwarder for obsolete item in Stubs
 */

struct tm *
TclpGmtime_unix(
    CONST time_t *timePtr)
{
    return TclpGmtime(timePtr);
}

/*
 *----------------------------------------------------------------------
 *
 * TclpLocaltime --
 *
 *    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 localtime or localtime_r as appropriate.
 *
 *----------------------------------------------------------------------
 */

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.
     */

    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tmKey);

    SetTZIfNecessary();
#ifdef HAVE_LOCALTIME_R
    localtime_r(timePtr, &(tsdPtr->localtime_buf));
#else
    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(
    CONST time_t *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;
    struct timezone tz;

    (void) gettimeofday(&tv, &tz);
    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");

    Tcl_MutexLock(&tmMutex);
    if (newTZ == NULL) {
      newTZ = "";
    }
    if (lastTZ == NULL || strcmp(lastTZ, newTZ)) {
      tzset();
      if (lastTZ == NULL) {
          Tcl_CreateExitHandler(CleanupMemory, (ClientData) NULL);
      } else {
          Tcl_Free(lastTZ);
      }
      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.
 *
 * 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:
 */

Generated by  Doxygen 1.6.0   Back to index