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
20mMAKR 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 daysLEG 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.
# 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 splitting as in 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
}
# Imperial gallon
interp alias {} split_ounces {} _split_amount_ {
20 "imp fl oz" pint 2 pint quart 4 quart "imp gal"
}
# US liquid gallon
interp alias {} split_ounces {} _split_amount_ {
16 "fl oz" pint 2 pint quart 4 quart "imp gal"
}
# Data amounts
#
interp alias {} split_binary_JEDEC {} _split_amount_ {
1024 B KB 1024 KB MB 1024 GB MB
1024 GB TiB 1024 TiB PiB 1024 PiB EiB
1024 EiB ZiB 1024 ZiB YiB
}
interp alias {} split_binary_IEC {} _split_amount_ {
1024 B KiB 1024 KiB MiB 1024 GiB MiB
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 byte kibibyte 1024 kibibyte mebibyte 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 B kB 1000 KB MB 1000 GB MB
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 ' °
}
#"#
# Quantities as dozens
#
interp alias {} split_dozens {} _split_amount_ {
12 pcs dz 12 dz gross 12 gross "great gross"
}
Example usage:% split_arcseconds 34000
9° 26' 40"
% split_bibyte [expr {1024*1024*1024+4*1024}] " "
1 gibibyte 4 kibibyte
