diff options
-rw-r--r-- | ChangeLog | 13 | ||||
-rw-r--r-- | generic/tclClock.c | 21 | ||||
-rw-r--r-- | library/clock.tcl | 30 | ||||
-rw-r--r-- | tests/clock.test | 56 |
4 files changed, 110 insertions, 10 deletions
@@ -1,3 +1,16 @@ +2005-07-15 Kevin B. Kenny <kennykb@acm.org> + + * generic/tclClock.c (TclClockLocaltimeObjCmd,ThreadSafeLocalTime): + * library/clock.tcl (GuessWindowsTimeZone, ClearCaches): + * tests/clock.test (clock-49.1, clock-49.2): + Handle correctly the case where localtime() returns NULL to + report a conversion error. Also handle the case where the Windows + registry contains timezone values that can be mapped to a tzdata + file name but the corresponding file does not exist or is + corrupted, by falling back on a Posix timezone string instead; + this last case will avoid calls to localtime() in starpacks on + Windows. [Bug 1237907] + 2005-07-14 Donal K. Fellows <donal.k.fellows@manchester.ac.uk> * generic/tclCompile.c: Update to follow style guidelines. diff --git a/generic/tclClock.c b/generic/tclClock.c index 6611576..9f3816b 100644 --- a/generic/tclClock.c +++ b/generic/tclClock.c @@ -12,7 +12,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclClock.c,v 1.37 2004/10/30 18:04:00 kennykb Exp $ + * RCS: @(#) $Id: tclClock.c,v 1.38 2005/07/15 22:32:23 kennykb Exp $ */ #include "tclInt.h" @@ -166,6 +166,14 @@ TclClockLocaltimeObjCmd( ClientData clientData, } TzsetIfNecessary(); timeVal = ThreadSafeLocalTime( &tock ); + if ( timeVal == NULL ) { + Tcl_SetObjResult(interp, + Tcl_NewStringObj("localtime failed (clock " + "value may be too large/" + "small to represent)", -1)); + Tcl_SetErrorCode(interp, "CLOCK", "localtimeFailed", (char*) NULL); + return TCL_ERROR; + } /* Package the results */ @@ -210,12 +218,19 @@ ThreadSafeLocalTime(timePtr) struct tm *tmPtr = (struct tm *) Tcl_GetThreadData(&tmKey, (int) sizeof(struct tm)); + struct tm *sysTmPtr; #ifdef HAVE_LOCALTIME_R localtime_r(timePtr, tmPtr); #else Tcl_MutexLock(&clockMutex); - memcpy((VOID *) tmPtr, (VOID *) localtime(timePtr), sizeof(struct tm)); - Tcl_MutexUnlock(&clockMutex); + sysTmPtr = localtime(timePtr); + if ( sysTmPtr == NULL ) { + Tcl_MutexUnlock( &clockMutex ); + return NULL; + } else { + memcpy((VOID *) tmPtr, (VOID *) localtime(timePtr), sizeof(struct tm)); + Tcl_MutexUnlock(&clockMutex); + } #endif return tmPtr; } diff --git a/library/clock.tcl b/library/clock.tcl index 61a80d2..5ff786f 100644 --- a/library/clock.tcl +++ b/library/clock.tcl @@ -13,7 +13,7 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -# RCS: @(#) $Id: clock.tcl,v 1.16 2005/05/10 18:34:54 kennykb Exp $ +# RCS: @(#) $Id: clock.tcl,v 1.17 2005/07/15 22:32:24 kennykb Exp $ # #---------------------------------------------------------------------- @@ -3356,6 +3356,7 @@ proc ::tcl::clock::GuessWindowsTimeZone {} { variable WinZoneInfo variable NoRegistry + variable TimeZoneBad if { [info exists NoRegistry] } { return :localtime @@ -3389,9 +3390,20 @@ proc ::tcl::clock::GuessWindowsTimeZone {} { return :localtime } - # Make up a Posix time zone specifier if we can't find one + # Make up a Posix time zone specifier if we can't find one. + # Check here that the tzdata file exists, in case we're running + # in an environment (e.g. starpack) where tzdata is incomplete. + # (Bug 1237907) - if { ! [dict exists $WinZoneInfo $data] } { + if { [dict exists $WinZoneInfo $data] } { + set tzname [dict get $WinZoneInfo $data] + if { ! [dict exists $TimeZoneBad $tzname] } { + dict set TimeZoneBad $tzname [catch {SetupTimeZone $tzname}] + } + } else { + set tzname {} + } + if { $tzname eq {} || [dict get $TimeZoneBad $tzname] } { foreach { bias stdBias dstBias stdYear stdMonth stdDayOfWeek stdDayOfMonth @@ -3404,24 +3416,29 @@ proc ::tcl::clock::GuessWindowsTimeZone {} { if { $stdDelta <= 0 } { set stdSignum + set stdDelta [expr { - $stdDelta }] + set dispStdSignum - } else { set stdSignum - + set dispStdSignum + } set hh [::format %02d [expr { $stdDelta / 3600 }]] set mm [::format %02d [expr { ($stdDelta / 60 ) % 60 }]] set ss [::format %02d [expr { $stdDelta % 60 }]] - append tzname < $stdSignum $hh $mm > $stdSignum $hh : $mm : $ss + set tzname {} + append tzname < $dispStdSignum $hh $mm > $stdSignum $hh : $mm : $ss if { $stdMonth >= 0 } { if { $dstDelta <= 0 } { set dstSignum + set dstDelta [expr { - $dstDelta }] + set dispDstSignum - } else { set dstSignum - + set dispDstSignum + } set hh [::format %02d [expr { $dstDelta / 3600 }]] set mm [::format %02d [expr { ($dstDelta / 60 ) % 60 }]] set ss [::format %02d [expr { $dstDelta % 60 }]] - append tzname < $dstSignum $hh $mm > $dstSignum $hh : $mm : $ss + append tzname < $dispDstSignum $hh $mm > $dstSignum $hh : $mm : $ss if { $dstYear == 0 } { append tzname ,M $dstMonth . $dstDayOfMonth . $dstDayOfWeek } else { @@ -5052,7 +5069,7 @@ proc ::tcl::clock::ClearCaches {} { variable LocaleNumeralCache variable McLoaded variable CachedSystemTimeZone - variable TZData + variable TimeZoneBad foreach p [info procs [namespace current]::scanproc'*] { rename $p {} @@ -5061,6 +5078,7 @@ proc ::tcl::clock::ClearCaches {} { set LocaleNumeralCache {} set McLoaded {} catch {unset CachedSystemTimeZone} + set TimeZoneBad {} InitTZData } diff --git a/tests/clock.test b/tests/clock.test index 4981d96..b8c864d 100644 --- a/tests/clock.test +++ b/tests/clock.test @@ -11,7 +11,7 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -# RCS: @(#) $Id: clock.test,v 1.55 2005/05/10 18:34:56 kennykb Exp $ +# RCS: @(#) $Id: clock.test,v 1.56 2005/07/15 22:32:24 kennykb Exp $ if {[lsearch [namespace children] ::tcltest] == -1} { package require tcltest 2 @@ -35411,6 +35411,60 @@ test clock-48.1 {Bug 1185933: 'i' destroyed by clock init} -setup { interp delete child } -result {0 12345} +test clock-49.1 {regression test - localtime with negative arg (Bug 1237907)} \ + -body { + list [catch { + clock format -86400 -timezone :localtime -format %Y + } result] $result + } \ + -match regexp \ + -result {0 1969|1 {localtime failed \(clock value may be too large/small to represent\)}} + +test clock-49.2 {regression test - missing time zone file (Bug 1237907)} \ + -constraints win \ + -setup { + # override the registry so that the test takes place in New York time + namespace eval ::tcl::clock { + namespace import -force ::testClock::registry + } + if { [info exists env(TZ)] } { + set oldTZ $env(TZ) + unset env(TZ) + } + if { [info exists env(TCL_TZ)] } { + set oldTclTZ $env(TCL_TZ) + unset env(TCL_TZ) + } + # make it so New York time is a missing file + dict set ::tcl::clock::WinZoneInfo \ + {-18000 0 3600 0 10 0 5 2 0 0 0 0 4 0 1 2 0 0 0} \ + :No/Such/File + ::tcl::clock::ClearCaches + } \ + -body { + list [::tcl::clock::GuessWindowsTimeZone] \ + [clock format 0 -locale system -format "%X %Z"] \ + [clock format -86400 -format "%Y"] + } \ + -cleanup { + # restore the registry and environment + namespace eval ::tcl::clock { + rename registry {} + } + if { [info exists oldTclTZ] } { + set env(TCL_TZ) $oldTclTZ + } + if { [info exists oldTZ] } { + set env(TZ) $oldTZ + } + # put New York back on the map + dict set ::tcl::clock::WinZoneInfo \ + {-18000 0 3600 0 10 0 5 2 0 0 0 0 4 0 1 2 0 0 0} \ + :America/New_York + ::tcl::clock::ClearCaches + } \ + -result {<-0500>+05:00:00<-0400>+04:00:00,M4.1.0/02:00:00,M10.5.0/02:00:00 { 7:00:00 PM -0500} 1969} + # cleanup namespace delete ::testClock |