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
