sl@0: /* sl@0: * tclUnixTime.c -- sl@0: * sl@0: * Contains Unix specific versions of Tcl functions that sl@0: * obtain time values from the operating system. sl@0: * sl@0: * Copyright (c) 1995 Sun Microsystems, Inc. sl@0: * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved. sl@0: * sl@0: * See the file "license.terms" for information on usage and redistribution sl@0: * of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: * sl@0: * RCS: @(#) $Id: tclUnixTime.c,v 1.15.2.6 2007/04/21 19:52:15 kennykb Exp $ sl@0: */ sl@0: sl@0: #include "tclInt.h" sl@0: #include "tclPort.h" sl@0: #include sl@0: #define TM_YEAR_BASE 1900 sl@0: #define IsLeapYear(x) ((x % 4 == 0) && (x % 100 != 0 || x % 400 == 0)) sl@0: sl@0: /* sl@0: * TclpGetDate is coded to return a pointer to a 'struct tm'. For sl@0: * thread safety, this structure must be in thread-specific data. sl@0: * The 'tmKey' variable is the key to this buffer. sl@0: */ sl@0: sl@0: static Tcl_ThreadDataKey tmKey; sl@0: typedef struct ThreadSpecificData { sl@0: struct tm gmtime_buf; sl@0: struct tm localtime_buf; sl@0: } ThreadSpecificData; sl@0: sl@0: /* sl@0: * If we fall back on the thread-unsafe versions of gmtime and localtime, sl@0: * use this mutex to try to protect them. sl@0: */ sl@0: sl@0: TCL_DECLARE_MUTEX(tmMutex) sl@0: sl@0: static char* lastTZ = NULL; /* Holds the last setting of the sl@0: * TZ environment variable, or an sl@0: * empty string if the variable was sl@0: * not set. */ sl@0: sl@0: /* Static functions declared in this file */ sl@0: sl@0: static void SetTZIfNecessary _ANSI_ARGS_((void)); sl@0: static void CleanupMemory _ANSI_ARGS_((ClientData)); sl@0: sl@0: /* sl@0: *----------------------------------------------------------------------------- sl@0: * sl@0: * TclpGetSeconds -- sl@0: * sl@0: * This procedure returns the number of seconds from the epoch. On sl@0: * most Unix systems the epoch is Midnight Jan 1, 1970 GMT. sl@0: * sl@0: * Results: sl@0: * Number of seconds from the epoch. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *----------------------------------------------------------------------------- sl@0: */ sl@0: sl@0: unsigned long sl@0: TclpGetSeconds() sl@0: { sl@0: return time((time_t *) NULL); sl@0: } sl@0: sl@0: /* sl@0: *----------------------------------------------------------------------------- sl@0: * sl@0: * TclpGetClicks -- sl@0: * sl@0: * This procedure returns a value that represents the highest resolution sl@0: * clock available on the system. There are no garantees on what the sl@0: * resolution will be. In Tcl we will call this value a "click". The sl@0: * start time is also system dependant. sl@0: * sl@0: * Results: sl@0: * Number of clicks from some start time. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *----------------------------------------------------------------------------- sl@0: */ sl@0: sl@0: unsigned long sl@0: TclpGetClicks() sl@0: { sl@0: unsigned long now; sl@0: #ifdef NO_GETTOD sl@0: struct tms dummy; sl@0: #else sl@0: struct timeval date; sl@0: struct timezone tz; sl@0: #endif sl@0: sl@0: #ifdef NO_GETTOD sl@0: now = (unsigned long) times(&dummy); sl@0: #else sl@0: gettimeofday(&date, &tz); sl@0: now = date.tv_sec*1000000 + date.tv_usec; sl@0: #endif sl@0: sl@0: return now; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpGetTimeZone -- sl@0: * sl@0: * Determines the current timezone. The method varies wildly sl@0: * between different platform implementations, so its hidden in sl@0: * this function. sl@0: * sl@0: * Results: sl@0: * The return value is the local time zone, measured in sl@0: * minutes away from GMT (-ve for east, +ve for west). sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclpGetTimeZone (currentTime) sl@0: Tcl_WideInt currentTime; sl@0: { sl@0: /* sl@0: * We prefer first to use the time zone in "struct tm" if the sl@0: * structure contains such a member. Following that, we try sl@0: * to locate the external 'timezone' variable and use its value. sl@0: * If both of those methods fail, we attempt to convert a known sl@0: * time to local time and use the difference from UTC as the local sl@0: * time zone. In all cases, we need to undo any Daylight Saving Time sl@0: * adjustment. sl@0: */ sl@0: sl@0: #if defined(HAVE_TM_TZADJ) sl@0: # define TCL_GOT_TIMEZONE sl@0: sl@0: /* Struct tm contains tm_tzadj - that value may be used. */ sl@0: sl@0: time_t curTime = (time_t) currentTime; sl@0: struct tm *timeDataPtr = TclpLocaltime((TclpTime_t) &curTime); sl@0: int timeZone; sl@0: sl@0: timeZone = timeDataPtr->tm_tzadj / 60; sl@0: if (timeDataPtr->tm_isdst) { sl@0: timeZone += 60; sl@0: } sl@0: sl@0: return timeZone; sl@0: sl@0: #endif sl@0: sl@0: #if defined(HAVE_TM_GMTOFF) && !defined (TCL_GOT_TIMEZONE) sl@0: # define TCL_GOT_TIMEZONE sl@0: sl@0: /* Struct tm contains tm_gmtoff - that value may be used. */ sl@0: sl@0: time_t curTime = (time_t) currentTime; sl@0: struct tm *timeDataPtr = TclpLocaltime((TclpTime_t) &curTime); sl@0: int timeZone; sl@0: sl@0: timeZone = -(timeDataPtr->tm_gmtoff / 60); sl@0: if (timeDataPtr->tm_isdst) { sl@0: timeZone += 60; sl@0: } sl@0: sl@0: return timeZone; sl@0: sl@0: #endif sl@0: sl@0: #if defined(HAVE_TIMEZONE_VAR) && !defined(TCL_GOT_TIMEZONE) && !defined(USE_DELTA_FOR_TZ) sl@0: # define TCL_GOT_TIMEZONE sl@0: sl@0: int timeZone; sl@0: sl@0: /* The 'timezone' external var is present and may be used. */ sl@0: sl@0: SetTZIfNecessary(); sl@0: sl@0: /* sl@0: * Note: this is not a typo in "timezone" below! See tzset sl@0: * documentation for details. sl@0: */ sl@0: sl@0: timeZone = timezone / 60; sl@0: return timeZone; sl@0: sl@0: #endif sl@0: sl@0: #if !defined(TCL_GOT_TIMEZONE) sl@0: #define TCL_GOT_TIMEZONE 1 sl@0: /* sl@0: * Fallback - determine time zone with a known reference time. sl@0: */ sl@0: sl@0: int timeZone; sl@0: time_t tt; sl@0: struct tm *stm; sl@0: tt = 849268800L; /* 1996-11-29 12:00:00 GMT */ sl@0: stm = TclpLocaltime((TclpTime_t) &tt); /* eg 1996-11-29 6:00:00 CST6CDT */ sl@0: /* The calculation below assumes a max of +12 or -12 hours from GMT */ sl@0: timeZone = (12 - stm->tm_hour)*60 + (0 - stm->tm_min); sl@0: if ( stm -> tm_isdst ) { sl@0: timeZone += 60; sl@0: } sl@0: return timeZone; /* eg +360 for CST6CDT */ sl@0: #endif sl@0: sl@0: #ifndef TCL_GOT_TIMEZONE sl@0: /* sl@0: * Cause compile error, we don't know how to get timezone. sl@0: */ sl@0: sl@0: #error autoconf did not figure out how to determine the timezone. sl@0: sl@0: #endif sl@0: sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_GetTime -- sl@0: * sl@0: * Gets the current system time in seconds and microseconds sl@0: * since the beginning of the epoch: 00:00 UCT, January 1, 1970. sl@0: * sl@0: * Results: sl@0: * Returns the current time in timePtr. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C void sl@0: Tcl_GetTime(timePtr) sl@0: Tcl_Time *timePtr; /* Location to store time information. */ sl@0: { sl@0: struct timeval tv; sl@0: struct timezone tz; sl@0: sl@0: (void) gettimeofday(&tv, &tz); sl@0: timePtr->sec = tv.tv_sec; sl@0: timePtr->usec = tv.tv_usec; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpGetDate -- sl@0: * sl@0: * This function converts between seconds and struct tm. If sl@0: * useGMT is true, then the returned date will be in Greenwich sl@0: * Mean Time (GMT). Otherwise, it will be in the local time zone. sl@0: * sl@0: * Results: sl@0: * Returns a static tm structure. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: struct tm * sl@0: TclpGetDate(time, useGMT) sl@0: TclpTime_t time; sl@0: int useGMT; sl@0: { sl@0: if (useGMT) { sl@0: return TclpGmtime(time); sl@0: } else { sl@0: return TclpLocaltime(time); sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpStrftime -- sl@0: * sl@0: * On Unix, we can safely call the native strftime implementation, sl@0: * and also ignore the useGMT parameter. sl@0: * sl@0: * Results: sl@0: * The normal strftime result. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: size_t sl@0: TclpStrftime(s, maxsize, format, t, useGMT) sl@0: char *s; sl@0: size_t maxsize; sl@0: CONST char *format; sl@0: CONST struct tm *t; sl@0: int useGMT; sl@0: { sl@0: if (format[0] == '%' && format[1] == 'Q') { sl@0: /* Format as a stardate */ sl@0: sprintf(s, "Stardate %2d%03d.%01d", sl@0: (((t->tm_year + TM_YEAR_BASE) + 377) - 2323), sl@0: (((t->tm_yday + 1) * 1000) / sl@0: (365 + IsLeapYear((t->tm_year + TM_YEAR_BASE)))), sl@0: (((t->tm_hour * 60) + t->tm_min)/144)); sl@0: return(strlen(s)); sl@0: } sl@0: setlocale(LC_TIME, ""); sl@0: return strftime(s, maxsize, format, t); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpGmtime -- sl@0: * sl@0: * Wrapper around the 'gmtime' library function to make it thread sl@0: * safe. sl@0: * sl@0: * Results: sl@0: * Returns a pointer to a 'struct tm' in thread-specific data. sl@0: * sl@0: * Side effects: sl@0: * Invokes gmtime or gmtime_r as appropriate. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: struct tm * sl@0: TclpGmtime( tt ) sl@0: TclpTime_t_CONST tt; sl@0: { sl@0: CONST time_t *timePtr = (CONST time_t *) tt; sl@0: /* Pointer to the number of seconds sl@0: * since the local system's epoch */ sl@0: sl@0: /* sl@0: * Get a thread-local buffer to hold the returned time. sl@0: */ sl@0: sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT( &tmKey ); sl@0: #ifdef HAVE_GMTIME_R sl@0: gmtime_r(timePtr, &( tsdPtr->gmtime_buf )); sl@0: #else sl@0: Tcl_MutexLock( &tmMutex ); sl@0: memcpy( (VOID *) &( tsdPtr->gmtime_buf ), sl@0: (VOID *) gmtime( timePtr ), sl@0: sizeof( struct tm ) ); sl@0: Tcl_MutexUnlock( &tmMutex ); sl@0: #endif sl@0: return &( tsdPtr->gmtime_buf ); sl@0: } sl@0: /* sl@0: * Forwarder for obsolete item in Stubs sl@0: */ sl@0: struct tm* sl@0: TclpGmtime_unix( timePtr ) sl@0: TclpTime_t_CONST timePtr; sl@0: { sl@0: return TclpGmtime( timePtr ); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpLocaltime -- sl@0: * sl@0: * Wrapper around the 'localtime' library function to make it thread sl@0: * safe. sl@0: * sl@0: * Results: sl@0: * Returns a pointer to a 'struct tm' in thread-specific data. sl@0: * sl@0: * Side effects: sl@0: * Invokes localtime or localtime_r as appropriate. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: struct tm * sl@0: TclpLocaltime( tt ) sl@0: TclpTime_t_CONST tt; sl@0: { sl@0: CONST time_t *timePtr = (CONST time_t *) tt; sl@0: /* Pointer to the number of seconds sl@0: * since the local system's epoch */ sl@0: /* sl@0: * Get a thread-local buffer to hold the returned time. sl@0: */ sl@0: sl@0: ThreadSpecificData *tsdPtr = TCL_TSD_INIT( &tmKey ); sl@0: SetTZIfNecessary(); sl@0: #ifdef HAVE_LOCALTIME_R sl@0: localtime_r( timePtr, &( tsdPtr->localtime_buf ) ); sl@0: #else sl@0: Tcl_MutexLock( &tmMutex ); sl@0: memcpy( (VOID *) &( tsdPtr -> localtime_buf ), sl@0: (VOID *) localtime( timePtr ), sl@0: sizeof( struct tm ) ); sl@0: Tcl_MutexUnlock( &tmMutex ); sl@0: #endif sl@0: return &( tsdPtr->localtime_buf ); sl@0: } sl@0: /* sl@0: * Forwarder for obsolete item in Stubs sl@0: */ sl@0: struct tm* sl@0: TclpLocaltime_unix( timePtr ) sl@0: TclpTime_t_CONST timePtr; sl@0: { sl@0: return TclpLocaltime( timePtr ); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * SetTZIfNecessary -- sl@0: * sl@0: * Determines whether a call to 'tzset' is needed prior to the sl@0: * next call to 'localtime' or examination of the 'timezone' variable. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * If 'tzset' has never been called in the current process, or if sl@0: * the value of the environment variable TZ has changed since the sl@0: * last call to 'tzset', then 'tzset' is called again. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: SetTZIfNecessary() { sl@0: sl@0: CONST char* newTZ = getenv( "TZ" ); sl@0: Tcl_MutexLock(&tmMutex); sl@0: if ( newTZ == NULL ) { sl@0: newTZ = ""; sl@0: } sl@0: if ( lastTZ == NULL || strcmp( lastTZ, newTZ ) ) { sl@0: tzset(); sl@0: if ( lastTZ == NULL ) { sl@0: Tcl_CreateExitHandler( CleanupMemory, (ClientData) NULL ); sl@0: } else { sl@0: Tcl_Free( lastTZ ); sl@0: } sl@0: lastTZ = ckalloc( strlen( newTZ ) + 1 ); sl@0: strcpy( lastTZ, newTZ ); sl@0: } sl@0: Tcl_MutexUnlock(&tmMutex); sl@0: sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * CleanupMemory -- sl@0: * sl@0: * Releases the private copy of the TZ environment variable sl@0: * upon exit from Tcl. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Frees allocated memory. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: CleanupMemory( ClientData ignored ) sl@0: { sl@0: ckfree( lastTZ ); sl@0: }