[MJ] - A frequent question on the Tcl [Chat] is how to split the number of seconds in the number of days, minutes etc. The following procedure provides this and allows you to specifiy the divisors of the different units, making it very flexible. The largest unit should not have a divider, this indicates that this unit should not be split any further (see examples below) The ''lead'' and ''inter'' parameters indicate whether amounts that are 0 should be displayed. For example the following {a x b y c z d} would translate to: A d is z c's which are y b's consisting of x a's. Note that this only works for integer amounts and dividers. ====== proc split_amount {amount dividers {lead 0} {inter 1}} { set result {} foreach {unit divider} $dividers { if {!$lead && $amount==0} break; if {$divider eq {}} { set result "$amount$unit $result" break } if {$inter || !($amount%$divider == 0)} { set result "[expr {$amount % $divider}]$unit $result" } set amount [expr {$amount/$divider}] } return $result } ====== Example usage: % split_amount [clock seconds] {s 60 m 60 h 24 d 365 y} 37y 238d 13h 2m 59s % split_amount [clock seconds] {{ seconds} 60 { minutes and} 60 { hours,} 24 { days,} 365 { years,}} 37 years, 238 days, 13 hours, 4 minutes and 7 seconds % split_amount 2234141 {{ gram} 1000 { kilogram and} 1000 { tonne,}} 2 tonne, 234 kilogram and 141 gram % split_amount 1200 {s 60 m 60 h 24 d 365 y} 20m 0s % split_amount 1200 {s 60 m 60 h 24 d 365 y} 1 1 0y 0d 0h 20m 0s % split_amount 1200 {s 60 m 60 h 24 d 365 y} 1 0 0y 20m % split_amount 1200 {s 60 m 60 h 24 d 365 y} 0 0 20m ---- [MAKR] 2007-08-31: Something similar, but less flexible: ====== proc timemsg {seconds} { if {$seconds < 0} { return -code error "seconds should be unsigned integer" } elseif {$seconds < 60} { set num $seconds set unit second } elseif {$seconds < 3600} { set num [expr {($seconds%3600)/60}] set unit minute } elseif {$seconds < 86400} { set num [expr {($seconds%86400)/3600}] set unit hour } else { set num [expr {int($seconds/86400)}] set unit day } if {$num > 1} { append unit "s" } return "$num $unit" } % timemsg 1 1 second % timemsg 125 2 minutes % timemsg [clock seconds] 13756 days ====== ----- [LEG] 2015-0913:- A slightly different implementation of the first algorithm which is friendly to [interp alias]: ====== proc _split_amount_ {dividers t {_s {}} {_P {}}} { if {!$t} {return "0[lindex $dividers 1]"} foreach {_D _S _U} $dividers { lassign [list [expr {$t/$_D}] [expr {$t%$_D}]] $_U $_S if {[set $_S]} {set _P [append $_S $_s $_S [expr {[string length $_P]?" $_P":$_P}]]} if {![set t [set $_U]]} {return $_P} } return "$t$_s$_U $_P" } ====== t is the amount you want to split, _s is an optional string between value and unit. _P is a dirty trick to save one line of code for initializing a local variable - don't use _P. Some properties of the implementation: * If one of the intermediate values is zero it is not included in the resulting string. * No leading or trailing spaces are generated. * If the amount is zero, the returned string is the '0' followed by the smallest unit. * The dividers are specified in a small language with triplets which answer the question how much of a smaller unit yield the next one, think '60 s m' as: 60 seconds a minute. And now a bunch of splitters, mostly taken from Wikipedia and not tested thoroughly: ====== # Elapsed time # interp alias {} elapsed_s {} _split_amount_ { 60 s m 60 m h 24 h d 7 d w 4 w mon 12 mon y } interp alias {} elapsed_ms {} _split_amount_ { 1000 ms s 60 s m 60 m h 24 h d 7 d w 4 w mon 12 mon y } interp alias {} elapsed_s_months_only {} _split_amount_ { 60 s m 60 m h 24 h d 30 d mon 12 mon y } # this is the same as the first example from MJ interp alias {} elapsed_s_years_only {} _split_amount_ { 60 s m 60 m h 24 h d 365 d y } # Imperial lengths # interp alias {} split_inch {} _split_amount_ { 12 in ft 3 ft yrd 1760 yrd mi } interp alias {} split_mil {} _split_amount_ { 1000 mil in 12 in ft 3 ft yrd 1760 yrd mi } # call as: split_1/32in $amount " " # or: split_1/32in [expr {$amount_float_inch/32.}] " " interp alias {} split_1/32in {} _split_amount_ { 2 1/32in 1/16in 2 1/16in 1/8in 2 1/8in 1/4in 2 1/4in 1/2in 2 1/2in in 12 in ft 3 ft yrd 1760 yrd mi } # Data amounts # interp alias {} split_binary_JEDEC {} _split_amount_ { 1024 KB B 1024 MB KB 1024 MB GB 1024 GB TiB 1024 TiB PiB 1024 PiB EiB 1024 EiB ZiB 1024 ZiB YiB } interp alias {} split_binary_IEC {} _split_amount_ { 1024 KiB B 1024 MiB KiB 1024 MiB GiB 1024 GiB TiB 1024 TiB PiB 1024 PiB EiB 1024 EiB ZiB 1024 ZiB YiB } # call as: split_bibyte $amount " " interp alias {} split_bibyte {} _split_amount_ { 1024 kibibyte byte 1024 mebibyte kibibyte 1024 mebibyte gibibyte 1024 gibibyte tebibyte 1024 tebibyte pebibyte 1024 pebibyte exibyte 1024 exibyte zebibyte 1024 zebibyte yobibyte } interp alias {} split_byte_decimal {} _split_amount_ { 1000 kB B 1000 MB KB 1000 MB GB 1000 GB TB 1000 TB PB 1000 PB EB 1000 EB ZB 1000 ZB YB } # Degrees (angles) # interp alias {} split_arcseconds {} _split_amount_ { 60 \" ' 60 ' ° } # " (this line fixing code coloring) # Quantities as dozens # interp alias {} split_dozens {} _split_amount_ { 12 pcs dz 12 dz gross 12 gross "great gross" } ====== While writing this it occurs to me, that gallons, ounces, etc. are also candidates for splitting. Some volunteer? <> Date and Time | Algorithm