Updated 2012-11-12 03:08:42 by RLE

davidw - I'm trying to (efficiently) calculate the arrival date of a package given a start date and number of business days it is supposed to take to arrive. I think this might be of interest to others. Here's my first cut at it (I have some suspicions that I missed something...):
    proc add_days {start days} {
        set 1day [expr {3600 * 24}]

        set end [expr {$start + [days2seconds $days]}]

        set diff [expr {$end - $start}]

        set weeks [expr {$diff / ($1day * 7)}]

        set remainder [expr {($diff - $weeks * $1day * 7) / $1day}]

        set startdow [clock format $start -format "%u"]
        set enddow   [clock format $end -format "%u"]

        set add 0
        incr add [expr {$weeks * 2}]
        incr add [expr {$startdow > $enddow ? 2 : 0}]
        incr add [expr {$startdow == 6 ? 2 : 0}]
        incr add [expr {$startdow == 7 ? 1 : 0}]

        set realend [expr {$end + $add * $1day}]
        set realenddow   [clock format $realend -format "%u"]

        set add [expr {$realenddow == 6 ? 2 : 0}]
        incr add [expr {$realenddow == 7 ? 2 : 0}]
        puts "ordered on [clock format $start] and should take $days days"
        puts "arrival on [clock format [expr {$realend + $add * $1day}]]"
    }

Please feel free to hack at the code in-line rather than tacking on new versions, unless they are radically different approaches - like the simple one of simply starting on the start day and going through each day and either counting it or not depending on whether it is a weekend day.

TODO: holidays, perhaps adjust it to make weekend days configurable (depending on the country, weekend days might vary).

If the Italian postal system gets its hands on the package, double the time, approximately;-)

davidw - The naive way of doing this is actually pretty simple... maybe enough that it's worth doing it this way even though it's kind of annoying to bludgeon one's way through:
    proc is_holiday? {date} {
        array set holidays {
            "01 01" X
            "06 01" X
            "25 04" X
            "01 05" X
            "02 06" X
            "15 08" X
            "01 11" X
            "08 12" X
            "25 12" X
            "26 12" X
        }
        set dm [clock format $date -format "%d %m"]
        if { [info exists holidays($dm)] } {
            puts "Holiday [clock format $date]"
            return 1
        } else {
            return 0
        }
    }

    proc is_weekend? {date} {
        set dow [day_of_week $date]
        if { $dow == 6 || $dow == 7 } {
            puts "Weekend [clock format $date]"
            return 1
        } else {
            return 0
        }
    }

    proc add_days {start days} {
        set current $start
        puts "Started on [clock format $start]"

        set 1day [expr {3600 * 24}]

        set i 0
        while {$i < $days} {
            incr current $1day
            if { [is_weekend? $current] || [is_holiday? $current] } {
                continue
            }
            incr i
        }
        puts "Delivered on [clock format $current]"
    }

See also: Calculating the Date of Easter