tclUnixTime.c

Go to the documentation of this file.
00001 /*
00002  * tclUnixTime.c --
00003  *
00004  *      Contains Unix specific versions of Tcl functions that obtain time
00005  *      values from the operating system.
00006  *
00007  * Copyright (c) 1995 Sun Microsystems, Inc.
00008  *
00009  * See the file "license.terms" for information on usage and redistribution of
00010  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00011  *
00012  * RCS: @(#) $Id: tclUnixTime.c,v 1.33 2007/12/13 15:28:42 dgp Exp $
00013  */
00014 
00015 #include "tclInt.h"
00016 #include <locale.h>
00017 #if defined(TCL_WIDE_CLICKS) && defined(MAC_OSX_TCL)
00018 #include <mach/mach_time.h>
00019 #endif
00020 
00021 #define TM_YEAR_BASE 1900
00022 #define IsLeapYear(x)   (((x)%4 == 0) && ((x)%100 != 0 || (x)%400 == 0))
00023 
00024 /*
00025  * TclpGetDate is coded to return a pointer to a 'struct tm'. For thread
00026  * safety, this structure must be in thread-specific data. The 'tmKey'
00027  * variable is the key to this buffer.
00028  */
00029 
00030 static Tcl_ThreadDataKey tmKey;
00031 typedef struct ThreadSpecificData {
00032     struct tm gmtime_buf;
00033     struct tm localtime_buf;
00034 } ThreadSpecificData;
00035 
00036 /*
00037  * If we fall back on the thread-unsafe versions of gmtime and localtime, use
00038  * this mutex to try to protect them.
00039  */
00040 
00041 TCL_DECLARE_MUTEX(tmMutex)
00042 
00043 static char *lastTZ = NULL;     /* Holds the last setting of the TZ
00044                                  * environment variable, or an empty string if
00045                                  * the variable was not set. */
00046 
00047 /*
00048  * Static functions declared in this file.
00049  */
00050 
00051 static void             SetTZIfNecessary(void);
00052 static void             CleanupMemory(ClientData clientData);
00053 static void             NativeScaleTime(Tcl_Time *timebuf,
00054                             ClientData clientData);
00055 static void             NativeGetTime(Tcl_Time *timebuf,
00056                             ClientData clientData);
00057 
00058 /*
00059  * TIP #233 (Virtualized Time): Data for the time hooks, if any.
00060  */
00061 
00062 Tcl_GetTimeProc *tclGetTimeProcPtr = NativeGetTime;
00063 Tcl_ScaleTimeProc *tclScaleTimeProcPtr = NativeScaleTime;
00064 ClientData tclTimeClientData = NULL;
00065 
00066 /*
00067  *-----------------------------------------------------------------------------
00068  *
00069  * TclpGetSeconds --
00070  *
00071  *      This procedure returns the number of seconds from the epoch. On most
00072  *      Unix systems the epoch is Midnight Jan 1, 1970 GMT.
00073  *
00074  * Results:
00075  *      Number of seconds from the epoch.
00076  *
00077  * Side effects:
00078  *      None.
00079  *
00080  *-----------------------------------------------------------------------------
00081  */
00082 
00083 unsigned long
00084 TclpGetSeconds(void)
00085 {
00086     return time(NULL);
00087 }
00088 
00089 /*
00090  *-----------------------------------------------------------------------------
00091  *
00092  * TclpGetClicks --
00093  *
00094  *      This procedure returns a value that represents the highest resolution
00095  *      clock available on the system. There are no garantees on what the
00096  *      resolution will be. In Tcl we will call this value a "click". The
00097  *      start time is also system dependant.
00098  *
00099  * Results:
00100  *      Number of clicks from some start time.
00101  *
00102  * Side effects:
00103  *      None.
00104  *
00105  *-----------------------------------------------------------------------------
00106  */
00107 
00108 unsigned long
00109 TclpGetClicks(void)
00110 {
00111     unsigned long now;
00112 
00113 #ifdef NO_GETTOD
00114     if (tclGetTimeProcPtr != NativeGetTime) {
00115         Tcl_Time time;
00116 
00117         (*tclGetTimeProcPtr) (&time, tclTimeClientData);
00118         now = time.sec*1000000 + time.usec;
00119     } else {
00120         /*
00121          * A semi-NativeGetTime, specialized to clicks.
00122          */
00123         struct tms dummy;
00124 
00125         now = (unsigned long) times(&dummy);
00126     }
00127 #else
00128     Tcl_Time time;
00129 
00130     (*tclGetTimeProcPtr) (&time, tclTimeClientData);
00131     now = time.sec*1000000 + time.usec;
00132 #endif
00133 
00134     return now;
00135 }
00136 #ifdef TCL_WIDE_CLICKS
00137 
00138 /*
00139  *-----------------------------------------------------------------------------
00140  *
00141  * TclpGetWideClicks --
00142  *
00143  *      This procedure returns a WideInt value that represents the highest
00144  *      resolution clock available on the system. There are no garantees on
00145  *      what the resolution will be. In Tcl we will call this value a "click".
00146  *      The start time is also system dependant.
00147  *
00148  * Results:
00149  *      Number of WideInt clicks from some start time.
00150  *
00151  * Side effects:
00152  *      None.
00153  *
00154  *-----------------------------------------------------------------------------
00155  */
00156 
00157 Tcl_WideInt
00158 TclpGetWideClicks(void)
00159 {
00160     Tcl_WideInt now;
00161 
00162     if (tclGetTimeProcPtr != NativeGetTime) {
00163         Tcl_Time time;
00164 
00165         (*tclGetTimeProcPtr) (&time, tclTimeClientData);
00166         now = (Tcl_WideInt) (time.sec*1000000 + time.usec);
00167     } else {
00168 #ifdef MAC_OSX_TCL
00169         now = (Tcl_WideInt) (mach_absolute_time() & INT64_MAX);
00170 #else
00171 #error Wide high-resolution clicks not implemented on this platform
00172 #endif
00173     }
00174 
00175     return now;
00176 }
00177 
00178 /*
00179  *-----------------------------------------------------------------------------
00180  *
00181  * TclpWideClicksToNanoseconds --
00182  *
00183  *      This procedure converts click values from the TclpGetWideClicks native
00184  *      resolution to nanosecond resolution.
00185  *
00186  * Results:
00187  *      Number of nanoseconds from some start time.
00188  *
00189  * Side effects:
00190  *      None.
00191  *
00192  *-----------------------------------------------------------------------------
00193  */
00194 
00195 double
00196 TclpWideClicksToNanoseconds(
00197     Tcl_WideInt clicks)
00198 {
00199     double nsec;
00200 
00201     if (tclGetTimeProcPtr != NativeGetTime) {
00202         nsec = clicks * 1000;
00203     } else {
00204 #ifdef MAC_OSX_TCL
00205         static mach_timebase_info_data_t tb;
00206         static uint64_t maxClicksForUInt64;
00207         
00208         if (!tb.denom) {
00209             mach_timebase_info(&tb);
00210             maxClicksForUInt64 = UINT64_MAX / tb.numer;
00211         }
00212         if ((uint64_t) clicks < maxClicksForUInt64) {
00213             nsec = ((uint64_t) clicks) * tb.numer / tb.denom;
00214         } else {
00215             nsec = ((long double) (uint64_t) clicks) * tb.numer / tb.denom;
00216         }
00217 #else
00218 #error Wide high-resolution clicks not implemented on this platform
00219 #endif
00220     }
00221 
00222     return nsec;
00223 }
00224 #endif /* TCL_WIDE_CLICKS */
00225 
00226 /*
00227  *----------------------------------------------------------------------
00228  *
00229  * TclpGetTimeZone --
00230  *
00231  *      Determines the current timezone. The method varies wildly between
00232  *      different platform implementations, so its hidden in this function.
00233  *
00234  * Results:
00235  *      The return value is the local time zone, measured in minutes away from
00236  *      GMT (-ve for east, +ve for west).
00237  *
00238  * Side effects:
00239  *      None.
00240  *
00241  *----------------------------------------------------------------------
00242  */
00243 
00244 int
00245 TclpGetTimeZone(
00246     unsigned long currentTime)
00247 {
00248     int timeZone;
00249 
00250     /*
00251      * We prefer first to use the time zone in "struct tm" if the structure
00252      * contains such a member. Following that, we try to locate the external
00253      * 'timezone' variable and use its value. If both of those methods fail,
00254      * we attempt to convert a known time to local time and use the difference
00255      * from UTC as the local time zone. In all cases, we need to undo any
00256      * Daylight Saving Time adjustment.
00257      */
00258 
00259 #if defined(HAVE_TM_TZADJ)
00260 #define TCL_GOT_TIMEZONE
00261     /*
00262      * Struct tm contains tm_tzadj - that value may be used.
00263      */
00264 
00265     time_t curTime = (time_t) currentTime;
00266     struct tm *timeDataPtr = TclpLocaltime(&curTime);
00267 
00268     timeZone = timeDataPtr->tm_tzadj / 60;
00269     if (timeDataPtr->tm_isdst) {
00270         timeZone += 60;
00271     }
00272 #endif
00273 
00274 #if defined(HAVE_TM_GMTOFF) && !defined (TCL_GOT_TIMEZONE)
00275 #define TCL_GOT_TIMEZONE
00276     /*
00277      * Struct tm contains tm_gmtoff - that value may be used.
00278      */
00279 
00280     time_t curTime = (time_t) currentTime;
00281     struct tm *timeDataPtr = TclpLocaltime(&curTime);
00282 
00283     timeZone = -(timeDataPtr->tm_gmtoff / 60);
00284     if (timeDataPtr->tm_isdst) {
00285         timeZone += 60;
00286     }
00287 #endif
00288 
00289 #if defined(HAVE_TIMEZONE_VAR) && !defined(TCL_GOT_TIMEZONE) && !defined(USE_DELTA_FOR_TZ)
00290 #define TCL_GOT_TIMEZONE
00291     /*
00292      * The 'timezone' external var is present and may be used.
00293      */
00294 
00295     SetTZIfNecessary();
00296 
00297     /*
00298      * Note: this is not a typo in "timezone" below! See tzset documentation
00299      * for details.
00300      */
00301 
00302     timeZone = timezone / 60;
00303 #endif
00304 
00305 #if !defined(TCL_GOT_TIMEZONE)
00306 #define TCL_GOT_TIMEZONE
00307     /*
00308      * Fallback - determine time zone with a known reference time.
00309      */
00310 
00311     time_t tt;
00312     struct tm *stm;
00313 
00314     tt = 849268800L;            /* 1996-11-29 12:00:00  GMT */
00315     stm = TclpLocaltime(&tt);   /* eg 1996-11-29  6:00:00  CST6CDT */
00316 
00317     /*
00318      * The calculation below assumes a max of +12 or -12 hours from GMT.
00319      */
00320 
00321     timeZone = (12 - stm->tm_hour)*60 + (0 - stm->tm_min);
00322     if (stm->tm_isdst) {
00323         timeZone += 60;
00324     }
00325 
00326     /*
00327      * Now have offset for our known reference time, eg +360 for CST6CDT.
00328      */
00329 #endif
00330 
00331 #ifndef TCL_GOT_TIMEZONE
00332     /*
00333      * Cause fatal compile error, we don't know how to get timezone.
00334      */
00335 
00336 #error autoconf did not figure out how to determine the timezone.
00337 #endif
00338 
00339     return timeZone;
00340 }
00341 
00342 /*
00343  *----------------------------------------------------------------------
00344  *
00345  * Tcl_GetTime --
00346  *
00347  *      Gets the current system time in seconds and microseconds since the
00348  *      beginning of the epoch: 00:00 UCT, January 1, 1970.
00349  *
00350  *      This function is hooked, allowing users to specify their own virtual
00351  *      system time.
00352  *
00353  * Results:
00354  *      Returns the current time in timePtr.
00355  *
00356  * Side effects:
00357  *      None.
00358  *
00359  *----------------------------------------------------------------------
00360  */
00361 
00362 void
00363 Tcl_GetTime(
00364     Tcl_Time *timePtr)          /* Location to store time information. */
00365 {
00366     (*tclGetTimeProcPtr) (timePtr, tclTimeClientData);
00367 }
00368 
00369 /*
00370  *----------------------------------------------------------------------
00371  *
00372  * TclpGetDate --
00373  *
00374  *      This function converts between seconds and struct tm. If useGMT is
00375  *      true, then the returned date will be in Greenwich Mean Time (GMT).
00376  *      Otherwise, it will be in the local time zone.
00377  *
00378  * Results:
00379  *      Returns a static tm structure.
00380  *
00381  * Side effects:
00382  *      None.
00383  *
00384  *----------------------------------------------------------------------
00385  */
00386 
00387 struct tm *
00388 TclpGetDate(
00389     CONST time_t *time,
00390     int useGMT)
00391 {
00392     if (useGMT) {
00393         return TclpGmtime(time);
00394     } else {
00395         return TclpLocaltime(time);
00396     }
00397 }
00398 
00399 /*
00400  *----------------------------------------------------------------------
00401  *
00402  * TclpGmtime --
00403  *
00404  *      Wrapper around the 'gmtime' library function to make it thread safe.
00405  *
00406  * Results:
00407  *      Returns a pointer to a 'struct tm' in thread-specific data.
00408  *
00409  * Side effects:
00410  *      Invokes gmtime or gmtime_r as appropriate.
00411  *
00412  *----------------------------------------------------------------------
00413  */
00414 
00415 struct tm *
00416 TclpGmtime(
00417     CONST time_t *timePtr)      /* Pointer to the number of seconds since the
00418                                  * local system's epoch */
00419 {
00420     /*
00421      * Get a thread-local buffer to hold the returned time.
00422      */
00423 
00424     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tmKey);
00425 
00426 #ifdef HAVE_GMTIME_R
00427     gmtime_r(timePtr, &(tsdPtr->gmtime_buf));
00428 #else
00429     Tcl_MutexLock(&tmMutex);
00430     memcpy(&(tsdPtr->gmtime_buf), gmtime(timePtr), sizeof(struct tm));
00431     Tcl_MutexUnlock(&tmMutex);
00432 #endif
00433 
00434     return &(tsdPtr->gmtime_buf);
00435 }
00436 
00437 /*
00438  * Forwarder for obsolete item in Stubs
00439  */
00440 
00441 struct tm *
00442 TclpGmtime_unix(
00443     CONST time_t *timePtr)
00444 {
00445     return TclpGmtime(timePtr);
00446 }
00447 
00448 /*
00449  *----------------------------------------------------------------------
00450  *
00451  * TclpLocaltime --
00452  *
00453  *      Wrapper around the 'localtime' library function to make it thread
00454  *      safe.
00455  *
00456  * Results:
00457  *      Returns a pointer to a 'struct tm' in thread-specific data.
00458  *
00459  * Side effects:
00460  *      Invokes localtime or localtime_r as appropriate.
00461  *
00462  *----------------------------------------------------------------------
00463  */
00464 
00465 struct tm *
00466 TclpLocaltime(
00467     CONST time_t *timePtr)      /* Pointer to the number of seconds since the
00468                                  * local system's epoch */
00469 {
00470     /*
00471      * Get a thread-local buffer to hold the returned time.
00472      */
00473 
00474     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tmKey);
00475 
00476     SetTZIfNecessary();
00477 #ifdef HAVE_LOCALTIME_R
00478     localtime_r(timePtr, &(tsdPtr->localtime_buf));
00479 #else
00480     Tcl_MutexLock(&tmMutex);
00481     memcpy(&(tsdPtr->localtime_buf), localtime(timePtr), sizeof(struct tm));
00482     Tcl_MutexUnlock(&tmMutex);
00483 #endif
00484 
00485     return &(tsdPtr->localtime_buf);
00486 }
00487 /*
00488  * Forwarder for obsolete item in Stubs
00489  */
00490 struct tm*
00491 TclpLocaltime_unix(
00492     CONST time_t *timePtr)
00493 {
00494     return TclpLocaltime(timePtr);
00495 }
00496 
00497 /*
00498  *----------------------------------------------------------------------
00499  *
00500  * Tcl_SetTimeProc --
00501  *
00502  *      TIP #233 (Virtualized Time): Registers two handlers for the
00503  *      virtualization of Tcl's access to time information.
00504  *
00505  * Results:
00506  *      None.
00507  *
00508  * Side effects:
00509  *      Remembers the handlers, alters core behaviour.
00510  *
00511  *----------------------------------------------------------------------
00512  */
00513 
00514 void
00515 Tcl_SetTimeProc(
00516     Tcl_GetTimeProc *getProc,
00517     Tcl_ScaleTimeProc *scaleProc,
00518     ClientData clientData)
00519 {
00520     tclGetTimeProcPtr = getProc;
00521     tclScaleTimeProcPtr = scaleProc;
00522     tclTimeClientData = clientData;
00523 }
00524 
00525 /*
00526  *----------------------------------------------------------------------
00527  *
00528  * Tcl_QueryTimeProc --
00529  *
00530  *      TIP #233 (Virtualized Time): Query which time handlers are registered.
00531  *
00532  * Results:
00533  *      None.
00534  *
00535  * Side effects:
00536  *      None.
00537  *
00538  *----------------------------------------------------------------------
00539  */
00540 
00541 void
00542 Tcl_QueryTimeProc(
00543     Tcl_GetTimeProc **getProc,
00544     Tcl_ScaleTimeProc **scaleProc,
00545     ClientData *clientData)
00546 {
00547     if (getProc) {
00548         *getProc = tclGetTimeProcPtr;
00549     }
00550     if (scaleProc) {
00551         *scaleProc = tclScaleTimeProcPtr;
00552     }
00553     if (clientData) {
00554         *clientData = tclTimeClientData;
00555     }
00556 }
00557 
00558 /*
00559  *----------------------------------------------------------------------
00560  *
00561  * NativeScaleTime --
00562  *
00563  *      TIP #233: Scale from virtual time to the real-time. For native scaling
00564  *      the relationship is 1:1 and nothing has to be done.
00565  *
00566  * Results:
00567  *      Scales the time in timePtr.
00568  *
00569  * Side effects:
00570  *      See above.
00571  *
00572  *----------------------------------------------------------------------
00573  */
00574 
00575 static void
00576 NativeScaleTime(
00577     Tcl_Time *timePtr,
00578     ClientData clientData)
00579 {
00580     /* Native scale is 1:1. Nothing is done */
00581 }
00582 
00583 /*
00584  *----------------------------------------------------------------------
00585  *
00586  * NativeGetTime --
00587  *
00588  *      TIP #233: Gets the current system time in seconds and microseconds
00589  *      since the beginning of the epoch: 00:00 UCT, January 1, 1970.
00590  *
00591  * Results:
00592  *      Returns the current time in timePtr.
00593  *
00594  * Side effects:
00595  *      None.
00596  *
00597  *----------------------------------------------------------------------
00598  */
00599 
00600 static void
00601 NativeGetTime(
00602     Tcl_Time *timePtr,
00603     ClientData clientData)
00604 {
00605     struct timeval tv;
00606     struct timezone tz;
00607 
00608     (void) gettimeofday(&tv, &tz);
00609     timePtr->sec = tv.tv_sec;
00610     timePtr->usec = tv.tv_usec;
00611 }
00612 /*
00613  *----------------------------------------------------------------------
00614  *
00615  * SetTZIfNecessary --
00616  *
00617  *      Determines whether a call to 'tzset' is needed prior to the next call
00618  *      to 'localtime' or examination of the 'timezone' variable.
00619  *
00620  * Results:
00621  *      None.
00622  *
00623  * Side effects:
00624  *      If 'tzset' has never been called in the current process, or if the
00625  *      value of the environment variable TZ has changed since the last call
00626  *      to 'tzset', then 'tzset' is called again.
00627  *
00628  *----------------------------------------------------------------------
00629  */
00630 
00631 static void
00632 SetTZIfNecessary(void)
00633 {
00634     CONST char *newTZ = getenv("TZ");
00635 
00636     Tcl_MutexLock(&tmMutex);
00637     if (newTZ == NULL) {
00638         newTZ = "";
00639     }
00640     if (lastTZ == NULL || strcmp(lastTZ, newTZ)) {
00641         tzset();
00642         if (lastTZ == NULL) {
00643             Tcl_CreateExitHandler(CleanupMemory, (ClientData) NULL);
00644         } else {
00645             Tcl_Free(lastTZ);
00646         }
00647         lastTZ = ckalloc(strlen(newTZ) + 1);
00648         strcpy(lastTZ, newTZ);
00649     }
00650     Tcl_MutexUnlock(&tmMutex);
00651 }
00652 
00653 /*
00654  *----------------------------------------------------------------------
00655  *
00656  * CleanupMemory --
00657  *
00658  *      Releases the private copy of the TZ environment variable upon exit
00659  *      from Tcl.
00660  *
00661  * Results:
00662  *      None.
00663  *
00664  * Side effects:
00665  *      Frees allocated memory.
00666  *
00667  *----------------------------------------------------------------------
00668  */
00669 
00670 static void
00671 CleanupMemory(
00672     ClientData ignored)
00673 {
00674     ckfree(lastTZ);
00675 }
00676 
00677 /*
00678  * Local Variables:
00679  * mode: c
00680  * c-basic-offset: 4
00681  * fill-column: 78
00682  * End:
00683  */



Generated on Wed Mar 12 12:18:26 2008 by  doxygen 1.5.1