Subject: [5f71353740] Support the "weekdays" unit in [clock add]

From: gahr <[email-masked]>
Date: Wed, 17 Feb 2016 11:03:48 +0000

---
 doc/clock.n       | 13 ++++++-------
 library/clock.tcl | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++----
 tests/clock.test  | 37 ++++++++++++++++++++++++++++++++++++
 3 files changed, 95 insertions(+), 11 deletions(-)

diff --git a/doc/clock.n b/doc/clock.n
index 889a5da..53db33e 100644
--- a/doc/clock.n
+++ b/doc/clock.n
@@ -89,10 +89,9 @@ 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.
+\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
@@ -175,8 +174,7 @@ 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.
+\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)
@@ -213,7 +211,8 @@ 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 epoch time.  The \fBweekdays\fR keyword is similar to \fBdays\fR,
+with the only difference that weekends 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)
diff --git a/library/clock.tcl b/library/clock.tcl
index 8e4b657..231f1ae 100755
--- a/library/clock.tcl
+++ b/library/clock.tcl
@@ -4248,7 +4248,7 @@ proc ::tcl::clock::add { clockval args } {
              ?-gmt boolean? ?-locale LOCALE? ?-timezone ZONE?\""
     }
     if { [catch { expr {wide($clockval)} } result] } {
-	return -code error $result
+	return -code error "expected integer but got \"$clockval\""
     }
 
     set offsets {}
@@ -4287,9 +4287,6 @@ proc ::tcl::clock::add { clockval args } {
 	    -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 } {
@@ -4326,6 +4323,11 @@ proc ::tcl::clock::add { clockval args } {
 			    $changeover]
 		}
 
+		weekdays - weekday {
+		    set clockval [AddWeekDays $quantity $clockval $timezone \
+			    $changeover]
+		}
+
 		hours - hour {
 		    set clockval [expr { 3600 * $quantity + $clockval }]
 		}
@@ -4425,6 +4427,52 @@ proc ::tcl::clock::AddMonths { months clockval timezone changeover } {
 
 #----------------------------------------------------------------------
 #
+# 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 } {
+
+    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
diff --git a/tests/clock.test b/tests/clock.test
index 615f3a8..fc7992d 100644
--- a/tests/clock.test
+++ b/tests/clock.test
@@ -34992,6 +34992,10 @@ test clock-29.1800 {time parsing} {
 } 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]
@@ -35218,6 +35222,39 @@ test clock-30.25 {clock add seconds at DST conversion} {
     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 weekdays systematic} -body {
+    set n [clock seconds]
+    set d [clock format $n -format %u]
+    for {set i 1} {$i < 100} {incr i} {
+        set res_no   [clock format [clock add $n $i weekdays] -format %u]
+        set exp_mod  [expr {($d+$i)%5}]
+        if {$exp_mod == 0} {
+            set exp_mod 5
+        }
+        if {$res_no != $exp_mod} {
+            return "Got $res_no adding $i to $n, expected: $exp_mod"
+        }
+    }
+    return "OK"
+} -result {OK}
+
+# END testcases30
+
 
 test clock-31.1 {system locale} \
     -constraints win \
-- 
cgit v0.12