tclUnixTime.cGo 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 1.5.1 |