“Rain might wash you away
Yet time already does”
-- Protesilaos Stavrou, “Tempests”
– Introduction –
hms
is a command-line tool for doing math with time durations in the
standard HH:MM:SS
format (hours, minutes, seconds). It supports convenient
syntax for writing these forms, so instead of typing out the full 00:00:05
to
specify a duration of 5 seconds, you can simply write 5
, or 0:5
, or 00:05
,
etc.
# 20 seconds + 40 seconds = 1 minute
hms "20 + 40"
# 00:01:00
# 1 minute - 2 seconds = 58 seconds
hms "1:00 - 2"
# 00:00:58
# 2 minutes and 20 seconds + 4 minutes and 40 seconds
# = 7 minutes
hms "2:20 + 4:40"
# 00:07:00
# 1 hour + 2 minutes + 3 seconds
hms "1:0:0 + 0:2:0 + 0:0:3"
# 01:02:03
# equivalently:
hms "1:0:0 + 2:0 + 3"
# 01:02:03
# equivalently:
hms "01:00:00 + 00:02:00 + 00:00:03"
# 01:02:03
hms
also supports fractional durations, so you can do calculations involving
fractions of seconds (or fractions of minutes (or hours)):
# 10.5 seconds + 20.4 seconds
hms "10.5 + 20.4"
# 00:00:30.9
# half an hour + 10.2 minutes
hms "1/2:00:00 + 10.2:00"
# 00:40:12.0
# 2.5 hours + 8 three-quarter minutes:
hms "2.5:00:00 + (8 * 3/4:00)"
# 02:36:00.0
# 5 minutes - 3 tenths of a second
hms "5:00 - 3/10"
# 00:04:59.7
– Try it out! –
>
– Features –
⏲ Operations
As of version 1.0.1.0
, hms
currently supports the following operations:
+
-- addition, left-associative
-
-- subtraction, left-associative
*
-- multiplication, left-associative
Multiplication makes the most sense when thinking of one of the operands as a scalar instead of a duration, as it then becomes a shorthand for summing a duration with itself a given number of times:
# 1 hour * 2 = (1 hour + 1 hour) = 2 hours
hms "1:00:00 * 2"
# 02:00:00
# 1:30 * 3 = (1:30 + 1:30 + 1:30) = 4:30
hms "1:30 * 3"
# 00:04:30
Under the hood, an expression like "1:30 * 3"
actually becomes 00:01:30 * 00:00:03
, so multiplication is always between durations and durations, not
durations and pure scalars. The logic behind this is that a duration can be seen
simply as a number of seconds, so 1:30 * 3
is really 90 * 3
(because 1:30
is equivalent to 0:90
), so we then do integer multiplication in the standard
way, i.e. 90 * 3 = 270
, and then 270
, which we’re viewing as a number of
seconds, is written out to the user as 4:30
(by having the seconds overflow to
minutes when it’s >= 60
).
This system means that you can do calculations like this:
# 1 minute * 1 minute = 1 minute * 60
# = 60 minutes = 1 hour
hms "1:00 * 1:00"
# 01:00:00
which don’t make much sense semantically, but may be useful in some situation that I can’t think of.
⏲ Precise real-valued arithmetic
hms
uses arbitrary precision integers to store its fractional values, enabling
you to do calculations involving many decimal places without any of the
imprecision that comes with floating point arithmetic:
hms "0.10000000000 + 0.00000200000 + 0.00000000003"
# 00:00:00.10000200003
hms "123.9876587654 + 234.8765476543 + 345.7654365432 + 456.6543254321"
# 00:19:21.283968395
⏲ Nested expressions
hms
supports nested expressions with parentheses:
hms "0:10 - (0:05 + 0:01)"
# 00:00:04
# without parentheses, the result differs:
hms "0:10 - 0:05 + 0:01"
# 00:00:06
⏲ Descriptive failures
If hms
can’t parse an expression you give it, it’ll tell you why:
hms "10,20,30"
# Parse Error: (line 1, column 3):
# unexpected ','
# expecting " ", "\n", "\t", "+", "-", "(" or end of input
hms "00:10 plus 00:20"
# Parse Error: (line 1, column 7):
# unexpected 'p'
# expecting " ", "\n", "\t", "+", "-", "(" or end of input
If hms
can parse, but can’t evaluate, the expression you give it, it’ll tell
you why the evaluation failed:
hms "+ 01:00"
# Evaluation Error:
# Infix operator '+' cannot appear at the beginning of an expression.
hms "01:00 +"
# Evaluation Error:
# Infix operator '+' missing second operand.
hms "01:00 02:00"
# Evaluation Error:
# Operator missing between durations '00:01:00' and '00:02:00'.
hms "01:00 + (02:00 + - 03:00)"
# Evaluation Error:
# While evaluating parenthesized expression '(00:02:00 + - 00:03:00)':
# Infix operator '+' cannot be applied to infix operator '-'.
– Motivation –
I wrote hms
because I wanted add up the lengths of some audio files, but
didn’t have an easy way to. Audio processing tools like soxi
and ffprobe
can
give you the length of an audio file in a format that hms
understands, but I
couldn’t find a simple tool to do arithmetic on these values. A typical small
script doesn’t suffice, because you’ll run into some handle-with-care problem
components, such as managing fractional numbers of seconds with precise
arithmetic, and properly overflowing from seconds to minutes and minutes to
hours with fractional (and potentially negative) values.
– Use cases –
If you have a sequence of durations in a file like so:
↓ ~/lengths.txt
05:10 03:24 02:55 04:29
then you can use hms
to easily add up the durations:
cat ~/lengths.txt | sed 's/ /+/g' | xargs hms
# 00:15:58
You can also use a tool like soxi
to calculate the durations and then pass
them directly to hms
:
soxi -d *.mp3 | xargs echo | tr ' ' '+' | xargs hms
# or, to calculate to more decimal places:
soxi -D *.mp3 | xargs echo | tr ' ' '+' | xargs hms
Or, using ffprobe
to do the same thing:
for audio_file in *.mp3; do
ffprobe -i $audio_file -show_entries format=duration -v quiet -of csv="p=0"
done | xargs echo | tr ' ' '+' | xargs hms
– Installation –
The source code for hms
is hosted on my Github page at
https://github.com/emfred-dot-com/hms.
The project is written in the Haskell programming language and uses the cabal
build system, making it possible to compile and install hms
with a single
command.
The following shell commands run through an installation:
git clone https://github.com/emfred-dot-com/hms
cd hms
cabal install
This will install the executable either to ~/.cabal/bin/hms
or ~/.local/bin
(depending on which version of cabal
you have), so make sure to add
~/.cabal/bin
and/or ~/.local/bin
to your shell’s PATH
environment variable
if you haven’t already.
(Note for developers: the hms
source repository also has a test suite, which
you can run using the cabal test
command.)
Banner image: “Crossbeams”. As the creator of this photo I declare it licensed under CC BY-NC-SA 4.0.