Version 5 of now

Updated 2017-08-24 19:22:53 by dbohdan

dbohdan 2017-08-23: now is a little date and time calculator utility for the command line. It leverages Tcl's clock add for date arithmetic.

Download with wiki-reaper: wiki-reaper -x 48963 1 | tee now.tcl

Use examples

$ now
Wed Aug 23 19:46:15 DST 2017
$ now -3491 days
Fri Feb 01 19:46:37 STD 2008
$ now -timezone :UTC -9 years -6 months -22 days +5 hours 5 minutes
Fri Feb 01 22:54:59 UTC 2008

Code

#! /usr/bin/env tclsh
# now v0.3.1 copyright (c) 2017 dbohdan. License: MIT.
package require Tcl 8.5

proc usage {} {
    puts stderr [format {usage: %s [-format fmt] [-gmt 0/1] [-locale loc]\
                         [-timezone tz] [count unit ...]} \
                        [file tail [info script]]]
}

# Parse the command line options. Give an informative message on error.
proc parse opts {
    set formatOpts {}
    while {[llength $opts] > 0 &&
           ![string is integer -strict [lindex $opts 0]]} {
        set opts [lassign $opts k v]
        switch -exact -- $k {
            -h         -
            --help     { return -code 1 -errorcode {PARSE usage} }
            -format    -
            -gmt       -
            -locale    -
            -timezone  { lappend formatOpts $k $v }
            default    {
                return -code 1 -errorcode {PARSE unknownOption} \
                       "unknown option \"$k\""
            }
        }
    }

    set delta {}
    foreach {count unit} $opts {
        set units {years months days hours minutes seconds}
        if {![string is integer -strict $count]} {
            return -code 1 "expected integer count, but got \"$count\""
        }
        set unit [
            switch -regexp -- $unit {
                ^ys?$    { lindex years }
                ^ms?$    { lindex months }
                ^ds?$    { lindex days }
                ^hs?$    { lindex hours }
                ^mins?$  { lindex minutes }
                ^ss?$    { lindex seconds }
                default  { lindex $unit }
            }
        ]
        if {$unit eq {}} {
            error "no unit given for count $count"
        } elseif {$unit ni $units} {
            error [format {unknown unit "%s", must be "years" ("y"),\
                           "months" ("m"), "days" ("d"), "hours" ("h"),\
                           "minutes" ("min") or "seconds" ("s")} $unit]
        }
        lappend delta $count $unit
    }

    return [list $formatOpts $delta]
}

# Output the date and time.
if {[catch {
    lassign [parse $argv] formatOpts delta
    puts [clock format [clock add [clock seconds] {*}$delta] {*}$formatOpts]
} res errOpts]} {
    set ec [dict get $errOpts -errorcode]
    if {$ec eq {PARSE usage}} {
        usage
        exit 0
    } else {
        puts stderr "error: $res"
        if {$ec eq {PARSE unknownOption}} {
            usage
        }
        exit 1
    }
}