Tcl Source Code

Changes On Branch tip-444
Login
Bounty program for improvements to Tcl and certain Tcl packages.

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch tip-444 Excluding Merge-Ins

This is equivalent to a diff from 976ec94ceb to 743b989b39

2016-04-04
15:43
Merge *both* commits to get the TclAsyncReady optimization. Without both parts, the test interp-34.... check-in: c86902986e user: dgp tags: trunk
11:30
Merge tip-444 Closed-Leaf check-in: e73c28499f user: gahr tags: micro-opt
11:25
merge trunk Closed-Leaf check-in: 743b989b39 user: gahr tags: gahr-bug-5f71353740, tip-444
11:21
micro-optimization from drh-micro-optimization branch: Knock perhaps 1% off execution time: guard on... check-in: 1b590c664f user: jan.nijtmans tags: micro-opt
2016-04-01
13:09
Merge trunk. Remove excess spacing. check-in: d48b5dfaf7 user: jan.nijtmans tags: novem
13:06
RFE [0ef5e653]: Add nsf to coffbase.txt. Eliminate exess sp... check-in: 976ec94ceb user: jan.nijtmans tags: trunk
13:04
RFE [0ef5e653]: Add nsf to coffbase.txt check-in: 06ccf650a8 user: jan.nijtmans tags: core-8-6-branch
11:56
Two micro-optimizations in Win and UNIX notifier. See: [http://code.activestate.com/lists/tcl-core/1... check-in: b1a8aa4406 user: jan.nijtmans tags: trunk
2016-03-03
08:34
merge trunk check-in: 00229de885 user: gahr tags: gahr-bug-5f71353740, tip-444

Changes to doc/clock.n.

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
...
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
...
209
210
211
212
213
214
215
216

217
218
219
220
221
222
223
exactly 86400 seconds.  Tcl responds to leap seconds by speeding or
slowing its clock by a tiny fraction for some minutes until it is
back in sync with UTC; its data model does not represent minutes that
have 59 or 61 seconds.
.TP
\fIunit\fR
One of the words, \fBseconds\fR, \fBminutes\fR, \fBhours\fR,
\fBdays\fR, \fBweeks\fR, \fBmonths\fR, or \fByears\fR, or
any unique prefix of such a word. Used in conjunction with \fIcount\fR
to identify an interval of time, for example, \fI3 seconds\fR or
\fI1 year\fR.
.SS "OPTIONS"
.TP
\fB\-base\fR time
Specifies that any relative times present in a \fBclock scan\fR command
are to be given relative to \fItime\fR.  \fItime\fR must be expressed as
a count of nominal seconds from the epoch time of 1 January 1970, 00:00 UTC.
.TP
................................................................................
.PP
The \fBclock add\fR command performs clock arithmetic on a value
(expressed as nominal seconds from the epoch time of 1 January 1970, 00:00 UTC)
given as its first argument.  The remaining arguments (other than the
possible \fB\-timezone\fR, \fB\-locale\fR and \fB\-gmt\fR options)
are integers and keywords in alternation, where the keywords are chosen
from \fBseconds\fR, \fBminutes\fR, \fBhours\fR,
\fBdays\fR, \fBweeks\fR, \fBmonths\fR, or \fByears\fR, or
any unique prefix of such a word.
.PP
Addition of seconds, minutes and hours is fairly straightforward;
the given time increment (times sixty for minutes, or 3600 for hours)
is simply added to the \fItimeVal\fR given
to the \fBclock add\fR command.  The result is interpreted as
a nominal number of seconds from the Epoch.
.PP
................................................................................
.CE
.PP
Adding and subtracting days and weeks is accomplished by converting
the given time to a calendar day and time of day in the appropriate
time zone and locale.  The requisite number of days (weeks are converted
to days by multiplying by seven) is added to the calendar day, and
the date and time are then converted back to a count of seconds from
the epoch time.

.PP
Adding and subtracting a given number of days across the point that
the time changes at the start or end of summer time (Daylight Saving Time)
results in the \fIsame local time\fR on the day in question.  For
instance, the following code sets the value of \fBx\fR to \fB05:00:00\fR.
.PP
.CS






|
|
|
<







 







|
<







 







|
>







85
86
87
88
89
90
91
92
93
94

95
96
97
98
99
100
101
...
170
171
172
173
174
175
176
177

178
179
180
181
182
183
184
...
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
exactly 86400 seconds.  Tcl responds to leap seconds by speeding or
slowing its clock by a tiny fraction for some minutes until it is
back in sync with UTC; its data model does not represent minutes that
have 59 or 61 seconds.
.TP
\fIunit\fR
One of the words, \fBseconds\fR, \fBminutes\fR, \fBhours\fR,
\fBdays\fR, \fBweekdays\fR, \fBweeks\fR, \fBmonths\fR, or \fByears\fR.
Used in conjunction with \fIcount\fR to identify an interval of time,
for example, \fI3 seconds\fR or \fI1 year\fR.

.SS "OPTIONS"
.TP
\fB\-base\fR time
Specifies that any relative times present in a \fBclock scan\fR command
are to be given relative to \fItime\fR.  \fItime\fR must be expressed as
a count of nominal seconds from the epoch time of 1 January 1970, 00:00 UTC.
.TP
................................................................................
.PP
The \fBclock add\fR command performs clock arithmetic on a value
(expressed as nominal seconds from the epoch time of 1 January 1970, 00:00 UTC)
given as its first argument.  The remaining arguments (other than the
possible \fB\-timezone\fR, \fB\-locale\fR and \fB\-gmt\fR options)
are integers and keywords in alternation, where the keywords are chosen
from \fBseconds\fR, \fBminutes\fR, \fBhours\fR,
\fBdays\fR, \fBweekdays\fR, \fBweeks\fR, \fBmonths\fR, or \fByears\fR.

.PP
Addition of seconds, minutes and hours is fairly straightforward;
the given time increment (times sixty for minutes, or 3600 for hours)
is simply added to the \fItimeVal\fR given
to the \fBclock add\fR command.  The result is interpreted as
a nominal number of seconds from the Epoch.
.PP
................................................................................
.CE
.PP
Adding and subtracting days and weeks is accomplished by converting
the given time to a calendar day and time of day in the appropriate
time zone and locale.  The requisite number of days (weeks are converted
to days by multiplying by seven) is added to the calendar day, and
the date and time are then converted back to a count of seconds from
the epoch time.  The \fBweekdays\fR keyword is similar to \fBdays\fR,
with the only difference that weekends - Saturdays and Sundays - are skipped.
.PP
Adding and subtracting a given number of days across the point that
the time changes at the start or end of summer time (Daylight Saving Time)
results in the \fIsame local time\fR on the day in question.  For
instance, the following code sets the value of \fBx\fR to \fB05:00:00\fR.
.PP
.CS

Changes to generic/tclExecute.c.

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
....
2111
2112
2113
2114
2115
2116
2117

2118
2119
2120
2121
2122
2123
2124
2125
....
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319

2320
2321
2322
2323
2324
2325
2326
2327
 */

#if (FLT_RADIX == 2) && (DBL_MANT_DIG == 53) && (DBL_MAX_EXP == 1024)
#define IEEE_FLOATING_POINT
#endif

/*
 * A mask (should be 2**n-1) that is used to work out when the bytecode engine
 * should call Tcl_AsyncReady() to see whether there is a signal that needs
 * handling.
 */

#ifndef ASYNC_CHECK_COUNT_MASK
#   define ASYNC_CHECK_COUNT_MASK	63
#endif /* !ASYNC_CHECK_COUNT_MASK */

/*
 * Boolean flag indicating whether the Tcl bytecode interpreter has been
 * initialized.
 */

static int execInitialized = 0;
................................................................................
     */

    /*
     * Constants: variables that do not change during the execution, used
     * sporadically: no special need for speed.
     */


    int instructionCount = 0;	/* Counter that is used to work out when to
				 * call Tcl_AsyncReady() */
    const char *curInstName;
#ifdef TCL_COMPILE_DEBUG
    int traceInstructions;	/* Whether we are doing instruction-level
				 * tracing or not. */
#endif

................................................................................

	break;
    }
  cleanup0:

    /*
     * Check for asynchronous handlers [Bug 746722]; we do the check every
     * ASYNC_CHECK_COUNT_MASK instruction, of the form (2**n-1).
     */


    if ((instructionCount++ & ASYNC_CHECK_COUNT_MASK) == 0) {
	DECACHE_STACK_INFO();
	if (TclAsyncReady(iPtr)) {
	    result = Tcl_AsyncInvoke(interp, result);
	    if (result == TCL_ERROR) {
		CACHE_STACK_INFO();
		goto gotError;
	    }






|
|
|


|
|
|







 







>
|







 







|


>
|







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
....
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
....
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
 */

#if (FLT_RADIX == 2) && (DBL_MANT_DIG == 53) && (DBL_MAX_EXP == 1024)
#define IEEE_FLOATING_POINT
#endif

/*
 * A counter that is used to work out when the bytecode engine should call
 * Tcl_AsyncReady() to see whether there is a signal that needs handling, and
 * other expensive periodic operations.
 */

#ifndef ASYNC_CHECK_COUNT
#   define ASYNC_CHECK_COUNT	64
#endif /* !ASYNC_CHECK_COUNT */

/*
 * Boolean flag indicating whether the Tcl bytecode interpreter has been
 * initialized.
 */

static int execInitialized = 0;
................................................................................
     */

    /*
     * Constants: variables that do not change during the execution, used
     * sporadically: no special need for speed.
     */

    int instructionCount = ASYNC_CHECK_COUNT;
				/* Counter that is used to work out when to
				 * call Tcl_AsyncReady() */
    const char *curInstName;
#ifdef TCL_COMPILE_DEBUG
    int traceInstructions;	/* Whether we are doing instruction-level
				 * tracing or not. */
#endif

................................................................................

	break;
    }
  cleanup0:

    /*
     * Check for asynchronous handlers [Bug 746722]; we do the check every
     * ASYNC_CHECK_COUNT instructions.
     */

    if (!(--instructionCount)) {
	instructionCount = ASYNC_CHECK_COUNT;
	DECACHE_STACK_INFO();
	if (TclAsyncReady(iPtr)) {
	    result = Tcl_AsyncInvoke(interp, result);
	    if (result == TCL_ERROR) {
		CACHE_STACK_INFO();
		goto gotError;
	    }

Changes to library/clock.tcl.

4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
....
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
....
4321
4322
4323
4324
4325
4326
4327





4328
4329
4330
4331
4332
4333
4334
....
4418
4419
4420
4421
4422
4423
4424


















































4425
4426
4427
4428
4429
4430
4431
	return -code error \
	    -errorcode [list CLOCK wrongNumArgs] \
	    "wrong \# args: should be\
             \"$cmdName clockval ?number units?...\
             ?-gmt boolean? ?-locale LOCALE? ?-timezone ZONE?\""
    }
    if { [catch { expr {wide($clockval)} } result] } {
	return -code error $result
    }

    set offsets {}
    set gmt 0
    set locale c
    set timezone [GetSystemTimeZone]

................................................................................
    # Check options for validity

    if { [info exists saw(-gmt)] && [info exists saw(-timezone)] } {
	return -code error \
	    -errorcode [list CLOCK gmtWithTimezone] \
	    "cannot use -gmt and -timezone in same call"
    }
    if { [catch { expr { wide($clockval) } } result] } {
	return -code error "expected integer but got \"$clockval\""
    }
    if { ![string is boolean -strict $gmt] } {
	return -code error "expected boolean value but got \"$gmt\""
    } elseif { $gmt } {
	set timezone :GMT
    }

    EnterLocale $locale
................................................................................
		    set clockval [AddDays [expr { 7 * $quantity }] \
			    $clockval $timezone $changeover]
		}
		days - day {
		    set clockval [AddDays $quantity $clockval $timezone \
			    $changeover]
		}






		hours - hour {
		    set clockval [expr { 3600 * $quantity + $clockval }]
		}
		minutes - minute {
		    set clockval [expr { 60 * $quantity + $clockval }]
		}
................................................................................
    }]
    set date [ConvertLocalToUTC $date[set date {}] $TZData($timezone) \
		 $changeover]

    return [dict get $date seconds]

}



















































#----------------------------------------------------------------------
#
# AddDays --
#
#	Add a given number of days to a given clock value in a given time
#	zone.






|







 







<
<
<







 







>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
....
4283
4284
4285
4286
4287
4288
4289



4290
4291
4292
4293
4294
4295
4296
....
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
....
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
4447
4448
4449
4450
4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
	return -code error \
	    -errorcode [list CLOCK wrongNumArgs] \
	    "wrong \# args: should be\
             \"$cmdName clockval ?number units?...\
             ?-gmt boolean? ?-locale LOCALE? ?-timezone ZONE?\""
    }
    if { [catch { expr {wide($clockval)} } result] } {
	return -code error "expected integer but got \"$clockval\""
    }

    set offsets {}
    set gmt 0
    set locale c
    set timezone [GetSystemTimeZone]

................................................................................
    # Check options for validity

    if { [info exists saw(-gmt)] && [info exists saw(-timezone)] } {
	return -code error \
	    -errorcode [list CLOCK gmtWithTimezone] \
	    "cannot use -gmt and -timezone in same call"
    }



    if { ![string is boolean -strict $gmt] } {
	return -code error "expected boolean value but got \"$gmt\""
    } elseif { $gmt } {
	set timezone :GMT
    }

    EnterLocale $locale
................................................................................
		    set clockval [AddDays [expr { 7 * $quantity }] \
			    $clockval $timezone $changeover]
		}
		days - day {
		    set clockval [AddDays $quantity $clockval $timezone \
			    $changeover]
		}

		weekdays - weekday {
		    set clockval [AddWeekDays $quantity $clockval $timezone \
			    $changeover]
		}

		hours - hour {
		    set clockval [expr { 3600 * $quantity + $clockval }]
		}
		minutes - minute {
		    set clockval [expr { 60 * $quantity + $clockval }]
		}
................................................................................
    }]
    set date [ConvertLocalToUTC $date[set date {}] $TZData($timezone) \
		 $changeover]

    return [dict get $date seconds]

}

#----------------------------------------------------------------------
#
# AddWeekDays --
#
#	Add a given number of week days (skipping Saturdays and Sundays)
#	to a given clock value in a given time zone.
#
# Parameters:
#	days - Number of days to add (may be negative)
#	clockval - Seconds since the epoch before the operation
#	timezone - Time zone in which the operation is to be performed
#	changeover - Julian Day on which the Gregorian calendar was adopted
#		     in the target locale.
#
# Results:
#	Returns the new clock value as a number of seconds since the epoch.
#
# Side effects:
#	None.
#
#----------------------------------------------------------------------

proc ::tcl::clock::AddWeekDays { days clockval timezone changeover } {

    if {$days == 0} {
        return $clockval
    }

    set day [format $clockval -format %u]

    set weeks  [expr {$days / 5}]
    set rdays  [expr {$days % 5}]
    set toAdd  [expr {7 * $weeks + $rdays}]
    set resDay [expr {$day + ($toAdd % 7)}]

    # Adjust if we start from a weekend
    if {$day > 5} {
	set adj [expr {5 - $day}]
	incr toAdd  $adj
	incr resDay $adj
    }

    # Adjust if we end up on a weekend
    if {$resDay > 5} {
	incr toAdd 2
    }

    AddDays $toAdd $clockval $timezone $changeover
}

#----------------------------------------------------------------------
#
# AddDays --
#
#	Add a given number of days to a given clock value in a given time
#	zone.

Changes to tests/clock.test.

34988
34989
34990
34991
34992
34993
34994




34995
34996
34997
34998
34999
35000
35001
.....
35214
35215
35216
35217
35218
35219
35220



















































35221
35222
35223
35224
35225
35226
35227
test clock-29.1800 {time parsing} {
    clock scan {2440588 xi:lix:lix pm} \
        -gmt true -locale en_US_roman \
        -format {%J %Ol:%OM:%OS %P}
} 86399
# END testcases29





test clock-30.1 {clock add years} {
    set t [clock scan 2000-01-01 -format %Y-%m-%d -timezone :UTC]
    set f [clock add $t 1 year -timezone :UTC]
    clock format $f -format %Y-%m-%d -timezone :UTC
} {2001-01-01}
test clock-30.2 {clock add years - leap day} {
    set t [clock scan 2000-02-29 -format %Y-%m-%d -timezone :UTC]
................................................................................
    set t [clock scan {2004-10-31 01:00:00 -0400} \
	       -format {%Y-%m-%d %H:%M:%S %z} \
	       -timezone EST05:00EDT04:00,M4.1.0/02:00,M10.5.0/02:00]
    set f1 [clock add $t 3600 seconds -timezone EST05:00EDT04:00,M4.1.0/02:00,M10.5.0/02:00]
    set x1 [clock format $f1 -format {%Y-%m-%d %H:%M:%S %z} \
		-timezone EST05:00EDT04:00,M4.1.0/02:00,M10.5.0/02:00]
} {2004-10-31 01:00:00 -0500}




















































test clock-31.1 {system locale} \
    -constraints win \
    -setup {
	namespace eval ::tcl::clock {
	    namespace import -force ::testClock::registry
	}






>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







34988
34989
34990
34991
34992
34993
34994
34995
34996
34997
34998
34999
35000
35001
35002
35003
35004
35005
.....
35218
35219
35220
35221
35222
35223
35224
35225
35226
35227
35228
35229
35230
35231
35232
35233
35234
35235
35236
35237
35238
35239
35240
35241
35242
35243
35244
35245
35246
35247
35248
35249
35250
35251
35252
35253
35254
35255
35256
35257
35258
35259
35260
35261
35262
35263
35264
35265
35266
35267
35268
35269
35270
35271
35272
35273
35274
35275
35276
35277
35278
35279
35280
35281
35282
test clock-29.1800 {time parsing} {
    clock scan {2440588 xi:lix:lix pm} \
        -gmt true -locale en_US_roman \
        -format {%J %Ol:%OM:%OS %P}
} 86399
# END testcases29


# BEGIN testcases30

# Test [clock add]
test clock-30.1 {clock add years} {
    set t [clock scan 2000-01-01 -format %Y-%m-%d -timezone :UTC]
    set f [clock add $t 1 year -timezone :UTC]
    clock format $f -format %Y-%m-%d -timezone :UTC
} {2001-01-01}
test clock-30.2 {clock add years - leap day} {
    set t [clock scan 2000-02-29 -format %Y-%m-%d -timezone :UTC]
................................................................................
    set t [clock scan {2004-10-31 01:00:00 -0400} \
	       -format {%Y-%m-%d %H:%M:%S %z} \
	       -timezone EST05:00EDT04:00,M4.1.0/02:00,M10.5.0/02:00]
    set f1 [clock add $t 3600 seconds -timezone EST05:00EDT04:00,M4.1.0/02:00,M10.5.0/02:00]
    set x1 [clock format $f1 -format {%Y-%m-%d %H:%M:%S %z} \
		-timezone EST05:00EDT04:00,M4.1.0/02:00,M10.5.0/02:00]
} {2004-10-31 01:00:00 -0500}
test clock-30.26 {clock add weekdays} {
    set t [clock scan {2013-11-20}] ;# Wednesday
    set f1 [clock add $t 3 weekdays]
    set x1 [clock format $f1 -format {%Y-%m-%d}]
} {2013-11-25}
test clock-30.27 {clock add weekdays starting on Saturday} {
    set t [clock scan {2013-11-23}] ;# Saturday
    set f1 [clock add $t 1 weekday]
    set x1 [clock format $f1 -format {%Y-%m-%d}]
} {2013-11-25}
test clock-30.28 {clock add weekdays starting on Sunday} {
    set t [clock scan {2013-11-24}] ;# Sunday
    set f1 [clock add $t 1 weekday]
    set x1 [clock format $f1 -format {%Y-%m-%d}]
} {2013-11-25}
test clock-30.29 {clock add 0 weekdays starting on a weekend} {
    set t [clock scan {2016-02-27}] ;# Saturday
    set f1 [clock add $t 0 weekdays]
    set x1 [clock format $f1 -format {%Y-%m-%d}]
} {2016-02-27}
test clock-30.30 {clock add weekdays and back} -body {
    set n [clock seconds]
    # we start on each day of the week
    for {set i 0} {$i < 7} {incr i} {
        set start  [clock add $n $i days]
        set startu [clock format $start -format %u]
        # add 0 - 100 weekdays
        for {set j 0} {$j < 100} {incr j} {
            set forth [clock add $start $j weekdays]
            set back  [clock add $forth -$j weekdays]
            # If $s was a weekday or $j was 0, $b must be the same day.
            # Otherwise, $b must be the immediately preceeding Friday
            set fail 0
            if {$j == 0 || $startu < 6} {
                if {$start != $back} { set fail 1}
            } else {
                set friday [clock add $start -[expr {$startu % 5}] days]
                if {$friday != $back} { set fail 1 }
            }
            if {$fail} {
                set sdate [clock format $start -format {%Y-%m-%d}]
                set bdate [clock format $back  -format {%Y-%m-%d}]
                return "$sdate + $j - $j := $bdate"
            }
        }
    }
    return "OK"
} -result {OK}

# END testcases30


test clock-31.1 {system locale} \
    -constraints win \
    -setup {
	namespace eval ::tcl::clock {
	    namespace import -force ::testClock::registry
	}