summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--generic/tclClock.c21
-rw-r--r--library/clock.tcl30
-rw-r--r--tests/clock.test56
4 files changed, 110 insertions, 10 deletions
diff --git a/ChangeLog b/ChangeLog
index a695df6..2f2bc93 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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