1/* 2 * tclUnixTime.c -- 3 * 4 * Contains Unix specific versions of Tcl functions that obtain time 5 * values from the operating system. 6 * 7 * Copyright (c) 1995 Sun Microsystems, Inc. 8 * 9 * See the file "license.terms" for information on usage and redistribution of 10 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 11 * 12 * RCS: @(#) $Id: tclUnixTime.c,v 1.33.2.1 2008/04/14 17:49:59 kennykb Exp $ 13 */ 14 15#include "tclInt.h" 16#include <locale.h> 17#if defined(TCL_WIDE_CLICKS) && defined(MAC_OSX_TCL) 18#include <mach/mach_time.h> 19#endif 20 21#define TM_YEAR_BASE 1900 22#define IsLeapYear(x) (((x)%4 == 0) && ((x)%100 != 0 || (x)%400 == 0)) 23 24/* 25 * TclpGetDate is coded to return a pointer to a 'struct tm'. For thread 26 * safety, this structure must be in thread-specific data. The 'tmKey' 27 * variable is the key to this buffer. 28 */ 29 30static Tcl_ThreadDataKey tmKey; 31typedef struct ThreadSpecificData { 32 struct tm gmtime_buf; 33 struct tm localtime_buf; 34} ThreadSpecificData; 35 36/* 37 * If we fall back on the thread-unsafe versions of gmtime and localtime, use 38 * this mutex to try to protect them. 39 */ 40 41TCL_DECLARE_MUTEX(tmMutex) 42 43static char *lastTZ = NULL; /* Holds the last setting of the TZ 44 * environment variable, or an empty string if 45 * the variable was not set. */ 46 47/* 48 * Static functions declared in this file. 49 */ 50 51static void SetTZIfNecessary(void); 52static void CleanupMemory(ClientData clientData); 53static void NativeScaleTime(Tcl_Time *timebuf, 54 ClientData clientData); 55static void NativeGetTime(Tcl_Time *timebuf, 56 ClientData clientData); 57 58/* 59 * TIP #233 (Virtualized Time): Data for the time hooks, if any. 60 */ 61 62Tcl_GetTimeProc *tclGetTimeProcPtr = NativeGetTime; 63Tcl_ScaleTimeProc *tclScaleTimeProcPtr = NativeScaleTime; 64ClientData tclTimeClientData = NULL; 65 66/* 67 *----------------------------------------------------------------------------- 68 * 69 * TclpGetSeconds -- 70 * 71 * This procedure returns the number of seconds from the epoch. On most 72 * Unix systems the epoch is Midnight Jan 1, 1970 GMT. 73 * 74 * Results: 75 * Number of seconds from the epoch. 76 * 77 * Side effects: 78 * None. 79 * 80 *----------------------------------------------------------------------------- 81 */ 82 83unsigned long 84TclpGetSeconds(void) 85{ 86 return time(NULL); 87} 88 89/* 90 *----------------------------------------------------------------------------- 91 * 92 * TclpGetClicks -- 93 * 94 * This procedure returns a value that represents the highest resolution 95 * clock available on the system. There are no garantees on what the 96 * resolution will be. In Tcl we will call this value a "click". The 97 * start time is also system dependant. 98 * 99 * Results: 100 * Number of clicks from some start time. 101 * 102 * Side effects: 103 * None. 104 * 105 *----------------------------------------------------------------------------- 106 */ 107 108unsigned long 109TclpGetClicks(void) 110{ 111 unsigned long now; 112 113#ifdef NO_GETTOD 114 if (tclGetTimeProcPtr != NativeGetTime) { 115 Tcl_Time time; 116 117 (*tclGetTimeProcPtr) (&time, tclTimeClientData); 118 now = time.sec*1000000 + time.usec; 119 } else { 120 /* 121 * A semi-NativeGetTime, specialized to clicks. 122 */ 123 struct tms dummy; 124 125 now = (unsigned long) times(&dummy); 126 } 127#else 128 Tcl_Time time; 129 130 (*tclGetTimeProcPtr) (&time, tclTimeClientData); 131 now = time.sec*1000000 + time.usec; 132#endif 133 134 return now; 135} 136#ifdef TCL_WIDE_CLICKS 137 138/* 139 *----------------------------------------------------------------------------- 140 * 141 * TclpGetWideClicks -- 142 * 143 * This procedure returns a WideInt value that represents the highest 144 * resolution clock available on the system. There are no garantees on 145 * what the resolution will be. In Tcl we will call this value a "click". 146 * The start time is also system dependant. 147 * 148 * Results: 149 * Number of WideInt clicks from some start time. 150 * 151 * Side effects: 152 * None. 153 * 154 *----------------------------------------------------------------------------- 155 */ 156 157Tcl_WideInt 158TclpGetWideClicks(void) 159{ 160 Tcl_WideInt now; 161 162 if (tclGetTimeProcPtr != NativeGetTime) { 163 Tcl_Time time; 164 165 (*tclGetTimeProcPtr) (&time, tclTimeClientData); 166 now = (Tcl_WideInt) (time.sec*1000000 + time.usec); 167 } else { 168#ifdef MAC_OSX_TCL 169 now = (Tcl_WideInt) (mach_absolute_time() & INT64_MAX); 170#else 171#error Wide high-resolution clicks not implemented on this platform 172#endif 173 } 174 175 return now; 176} 177 178/* 179 *----------------------------------------------------------------------------- 180 * 181 * TclpWideClicksToNanoseconds -- 182 * 183 * This procedure converts click values from the TclpGetWideClicks native 184 * resolution to nanosecond resolution. 185 * 186 * Results: 187 * Number of nanoseconds from some start time. 188 * 189 * Side effects: 190 * None. 191 * 192 *----------------------------------------------------------------------------- 193 */ 194 195double 196TclpWideClicksToNanoseconds( 197 Tcl_WideInt clicks) 198{ 199 double nsec; 200 201 if (tclGetTimeProcPtr != NativeGetTime) { 202 nsec = clicks * 1000; 203 } else { 204#ifdef MAC_OSX_TCL 205 static mach_timebase_info_data_t tb; 206 static uint64_t maxClicksForUInt64; 207 208 if (!tb.denom) { 209 mach_timebase_info(&tb); 210 maxClicksForUInt64 = UINT64_MAX / tb.numer; 211 } 212 if ((uint64_t) clicks < maxClicksForUInt64) { 213 nsec = ((uint64_t) clicks) * tb.numer / tb.denom; 214 } else { 215 nsec = ((long double) (uint64_t) clicks) * tb.numer / tb.denom; 216 } 217#else 218#error Wide high-resolution clicks not implemented on this platform 219#endif 220 } 221 222 return nsec; 223} 224#endif /* TCL_WIDE_CLICKS */ 225 226/* 227 *---------------------------------------------------------------------- 228 * 229 * TclpGetTimeZone -- 230 * 231 * Determines the current timezone. The method varies wildly between 232 * different platform implementations, so its hidden in this function. 233 * 234 * Results: 235 * The return value is the local time zone, measured in minutes away from 236 * GMT (-ve for east, +ve for west). 237 * 238 * Side effects: 239 * None. 240 * 241 *---------------------------------------------------------------------- 242 */ 243 244int 245TclpGetTimeZone( 246 unsigned long currentTime) 247{ 248 int timeZone; 249 250 /* 251 * We prefer first to use the time zone in "struct tm" if the structure 252 * contains such a member. Following that, we try to locate the external 253 * 'timezone' variable and use its value. If both of those methods fail, 254 * we attempt to convert a known time to local time and use the difference 255 * from UTC as the local time zone. In all cases, we need to undo any 256 * Daylight Saving Time adjustment. 257 */ 258 259#if defined(HAVE_TM_TZADJ) 260#define TCL_GOT_TIMEZONE 261 /* 262 * Struct tm contains tm_tzadj - that value may be used. 263 */ 264 265 time_t curTime = (time_t) currentTime; 266 struct tm *timeDataPtr = TclpLocaltime(&curTime); 267 268 timeZone = timeDataPtr->tm_tzadj / 60; 269 if (timeDataPtr->tm_isdst) { 270 timeZone += 60; 271 } 272#endif 273 274#if defined(HAVE_TM_GMTOFF) && !defined (TCL_GOT_TIMEZONE) 275#define TCL_GOT_TIMEZONE 276 /* 277 * Struct tm contains tm_gmtoff - that value may be used. 278 */ 279 280 time_t curTime = (time_t) currentTime; 281 struct tm *timeDataPtr = TclpLocaltime(&curTime); 282 283 timeZone = -(timeDataPtr->tm_gmtoff / 60); 284 if (timeDataPtr->tm_isdst) { 285 timeZone += 60; 286 } 287#endif 288 289#if defined(HAVE_TIMEZONE_VAR) && !defined(TCL_GOT_TIMEZONE) && !defined(USE_DELTA_FOR_TZ) 290#define TCL_GOT_TIMEZONE 291 /* 292 * The 'timezone' external var is present and may be used. 293 */ 294 295 SetTZIfNecessary(); 296 297 /* 298 * Note: this is not a typo in "timezone" below! See tzset documentation 299 * for details. 300 */ 301 302 timeZone = timezone / 60; 303#endif 304 305#if !defined(TCL_GOT_TIMEZONE) 306#define TCL_GOT_TIMEZONE 307 /* 308 * Fallback - determine time zone with a known reference time. 309 */ 310 311 time_t tt; 312 struct tm *stm; 313 314 tt = 849268800L; /* 1996-11-29 12:00:00 GMT */ 315 stm = TclpLocaltime(&tt); /* eg 1996-11-29 6:00:00 CST6CDT */ 316 317 /* 318 * The calculation below assumes a max of +12 or -12 hours from GMT. 319 */ 320 321 timeZone = (12 - stm->tm_hour)*60 + (0 - stm->tm_min); 322 if (stm->tm_isdst) { 323 timeZone += 60; 324 } 325 326 /* 327 * Now have offset for our known reference time, eg +360 for CST6CDT. 328 */ 329#endif 330 331#ifndef TCL_GOT_TIMEZONE 332 /* 333 * Cause fatal compile error, we don't know how to get timezone. 334 */ 335 336#error autoconf did not figure out how to determine the timezone. 337#endif 338 339 return timeZone; 340} 341 342/* 343 *---------------------------------------------------------------------- 344 * 345 * Tcl_GetTime -- 346 * 347 * Gets the current system time in seconds and microseconds since the 348 * beginning of the epoch: 00:00 UCT, January 1, 1970. 349 * 350 * This function is hooked, allowing users to specify their own virtual 351 * system time. 352 * 353 * Results: 354 * Returns the current time in timePtr. 355 * 356 * Side effects: 357 * None. 358 * 359 *---------------------------------------------------------------------- 360 */ 361 362void 363Tcl_GetTime( 364 Tcl_Time *timePtr) /* Location to store time information. */ 365{ 366 (*tclGetTimeProcPtr) (timePtr, tclTimeClientData); 367} 368 369/* 370 *---------------------------------------------------------------------- 371 * 372 * TclpGetDate -- 373 * 374 * This function converts between seconds and struct tm. If useGMT is 375 * true, then the returned date will be in Greenwich Mean Time (GMT). 376 * Otherwise, it will be in the local time zone. 377 * 378 * Results: 379 * Returns a static tm structure. 380 * 381 * Side effects: 382 * None. 383 * 384 *---------------------------------------------------------------------- 385 */ 386 387struct tm * 388TclpGetDate( 389 CONST time_t *time, 390 int useGMT) 391{ 392 if (useGMT) { 393 return TclpGmtime(time); 394 } else { 395 return TclpLocaltime(time); 396 } 397} 398 399/* 400 *---------------------------------------------------------------------- 401 * 402 * TclpGmtime -- 403 * 404 * Wrapper around the 'gmtime' library function to make it thread safe. 405 * 406 * Results: 407 * Returns a pointer to a 'struct tm' in thread-specific data. 408 * 409 * Side effects: 410 * Invokes gmtime or gmtime_r as appropriate. 411 * 412 *---------------------------------------------------------------------- 413 */ 414 415struct tm * 416TclpGmtime( 417 CONST time_t *timePtr) /* Pointer to the number of seconds since the 418 * local system's epoch */ 419{ 420 /* 421 * Get a thread-local buffer to hold the returned time. 422 */ 423 424 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tmKey); 425 426#ifdef HAVE_GMTIME_R 427 gmtime_r(timePtr, &(tsdPtr->gmtime_buf)); 428#else 429 Tcl_MutexLock(&tmMutex); 430 memcpy(&(tsdPtr->gmtime_buf), gmtime(timePtr), sizeof(struct tm)); 431 Tcl_MutexUnlock(&tmMutex); 432#endif 433 434 return &(tsdPtr->gmtime_buf); 435} 436 437/* 438 * Forwarder for obsolete item in Stubs 439 */ 440 441struct tm * 442TclpGmtime_unix( 443 CONST time_t *timePtr) 444{ 445 return TclpGmtime(timePtr); 446} 447 448/* 449 *---------------------------------------------------------------------- 450 * 451 * TclpLocaltime -- 452 * 453 * Wrapper around the 'localtime' library function to make it thread 454 * safe. 455 * 456 * Results: 457 * Returns a pointer to a 'struct tm' in thread-specific data. 458 * 459 * Side effects: 460 * Invokes localtime or localtime_r as appropriate. 461 * 462 *---------------------------------------------------------------------- 463 */ 464 465struct tm * 466TclpLocaltime( 467 CONST time_t *timePtr) /* Pointer to the number of seconds since the 468 * local system's epoch */ 469{ 470 /* 471 * Get a thread-local buffer to hold the returned time. 472 */ 473 474 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&tmKey); 475 476 SetTZIfNecessary(); 477#ifdef HAVE_LOCALTIME_R 478 localtime_r(timePtr, &(tsdPtr->localtime_buf)); 479#else 480 Tcl_MutexLock(&tmMutex); 481 memcpy(&(tsdPtr->localtime_buf), localtime(timePtr), sizeof(struct tm)); 482 Tcl_MutexUnlock(&tmMutex); 483#endif 484 485 return &(tsdPtr->localtime_buf); 486} 487/* 488 * Forwarder for obsolete item in Stubs 489 */ 490struct tm* 491TclpLocaltime_unix( 492 CONST time_t *timePtr) 493{ 494 return TclpLocaltime(timePtr); 495} 496 497/* 498 *---------------------------------------------------------------------- 499 * 500 * Tcl_SetTimeProc -- 501 * 502 * TIP #233 (Virtualized Time): Registers two handlers for the 503 * virtualization of Tcl's access to time information. 504 * 505 * Results: 506 * None. 507 * 508 * Side effects: 509 * Remembers the handlers, alters core behaviour. 510 * 511 *---------------------------------------------------------------------- 512 */ 513 514void 515Tcl_SetTimeProc( 516 Tcl_GetTimeProc *getProc, 517 Tcl_ScaleTimeProc *scaleProc, 518 ClientData clientData) 519{ 520 tclGetTimeProcPtr = getProc; 521 tclScaleTimeProcPtr = scaleProc; 522 tclTimeClientData = clientData; 523} 524 525/* 526 *---------------------------------------------------------------------- 527 * 528 * Tcl_QueryTimeProc -- 529 * 530 * TIP #233 (Virtualized Time): Query which time handlers are registered. 531 * 532 * Results: 533 * None. 534 * 535 * Side effects: 536 * None. 537 * 538 *---------------------------------------------------------------------- 539 */ 540 541void 542Tcl_QueryTimeProc( 543 Tcl_GetTimeProc **getProc, 544 Tcl_ScaleTimeProc **scaleProc, 545 ClientData *clientData) 546{ 547 if (getProc) { 548 *getProc = tclGetTimeProcPtr; 549 } 550 if (scaleProc) { 551 *scaleProc = tclScaleTimeProcPtr; 552 } 553 if (clientData) { 554 *clientData = tclTimeClientData; 555 } 556} 557 558/* 559 *---------------------------------------------------------------------- 560 * 561 * NativeScaleTime -- 562 * 563 * TIP #233: Scale from virtual time to the real-time. For native scaling 564 * the relationship is 1:1 and nothing has to be done. 565 * 566 * Results: 567 * Scales the time in timePtr. 568 * 569 * Side effects: 570 * See above. 571 * 572 *---------------------------------------------------------------------- 573 */ 574 575static void 576NativeScaleTime( 577 Tcl_Time *timePtr, 578 ClientData clientData) 579{ 580 /* Native scale is 1:1. Nothing is done */ 581} 582 583/* 584 *---------------------------------------------------------------------- 585 * 586 * NativeGetTime -- 587 * 588 * TIP #233: Gets the current system time in seconds and microseconds 589 * since the beginning of the epoch: 00:00 UCT, January 1, 1970. 590 * 591 * Results: 592 * Returns the current time in timePtr. 593 * 594 * Side effects: 595 * None. 596 * 597 *---------------------------------------------------------------------- 598 */ 599 600static void 601NativeGetTime( 602 Tcl_Time *timePtr, 603 ClientData clientData) 604{ 605 struct timeval tv; 606 607 (void) gettimeofday(&tv, NULL); 608 timePtr->sec = tv.tv_sec; 609 timePtr->usec = tv.tv_usec; 610} 611/* 612 *---------------------------------------------------------------------- 613 * 614 * SetTZIfNecessary -- 615 * 616 * Determines whether a call to 'tzset' is needed prior to the next call 617 * to 'localtime' or examination of the 'timezone' variable. 618 * 619 * Results: 620 * None. 621 * 622 * Side effects: 623 * If 'tzset' has never been called in the current process, or if the 624 * value of the environment variable TZ has changed since the last call 625 * to 'tzset', then 'tzset' is called again. 626 * 627 *---------------------------------------------------------------------- 628 */ 629 630static void 631SetTZIfNecessary(void) 632{ 633 CONST char *newTZ = getenv("TZ"); 634 635 Tcl_MutexLock(&tmMutex); 636 if (newTZ == NULL) { 637 newTZ = ""; 638 } 639 if (lastTZ == NULL || strcmp(lastTZ, newTZ)) { 640 tzset(); 641 if (lastTZ == NULL) { 642 Tcl_CreateExitHandler(CleanupMemory, (ClientData) NULL); 643 } else { 644 Tcl_Free(lastTZ); 645 } 646 lastTZ = ckalloc(strlen(newTZ) + 1); 647 strcpy(lastTZ, newTZ); 648 } 649 Tcl_MutexUnlock(&tmMutex); 650} 651 652/* 653 *---------------------------------------------------------------------- 654 * 655 * CleanupMemory -- 656 * 657 * Releases the private copy of the TZ environment variable upon exit 658 * from Tcl. 659 * 660 * Results: 661 * None. 662 * 663 * Side effects: 664 * Frees allocated memory. 665 * 666 *---------------------------------------------------------------------- 667 */ 668 669static void 670CleanupMemory( 671 ClientData ignored) 672{ 673 ckfree(lastTZ); 674} 675 676/* 677 * Local Variables: 678 * mode: c 679 * c-basic-offset: 4 680 * fill-column: 78 681 * End: 682 */ 683