In my opinion, the terminal is almost always the most desired interface for using any computer, and I prefer to stay in that friendly environment for as long as possible. It is a pleasant place to be, predictable and fairly free from unwanted distractions. Any task I can get done from the terminal, I want to perform there. To be clear I especially am not a fan of unnecessarily needing to open a browser to the contemporary world wide web, having to spend minutes on rejecting cookies, dismissing pop-ups and pointy-clickying barely navigatable pages in order to find facts which should merely take seconds to find. One type of information provoking such a struggle is currency conversion rates. I do infrequently, but recurringly, need to look those up.
Searching for how others was solving this I found this reddit thread which essentially gave nothing. I also found the tools cash, ccov, mmc, rates, sesters and a bunch of others all claiming to be intended for currency conversion, but with the common limitation that they all only handle exchange rates for the point in time at which they are run. While a pragmatic soul might claim fluctuations even out over time, that's never the perspective held by accountants or tax clerks. Insignificant details such as getting every transaction locked to the exact right day is what they love to enforce, and I do actually understand why they do cause all the annoyance. With it typically being a couple of days between me having an expense and spending the happy fun time to file an expense report or add it to my accounting, the tools above do not meet my need.
For a while I feared I would have to hack up my own little tool. I found this introductionary text on How to handle money and currency conversions by Chris Wenham, and then I realized that rusty-money seemed to be written in accordancy to that text. In essence; use quantified decimal representation rather than fractional floats and always defer conversions as long as possible. That's possibly also true for python-money (pypi) which hasn't been updated for over a decade.
I learned that there are a close to infinite amount of entities providing API:s for currency conversion, but they all require registering for an API key and forming a relationship. Which is way too high maintenance for what I was looking for. The European Central Bank publishes a dataset with daily exchange rates, and that tends to be what sane people actually use. It appears the Swedish Central Bank (SOAP API) provides data too, but short of actually investigating it, I suspect it might just be the ECB's data repackaged.
While looking at possibly implementing the tool in python rather than Rust, I
realized that this currencyconverter (pypi)
library actually contains a command line utility meeting all my needs. As this
does not seem to be packaged by Debian, one must do something like pip3 install --user currencyconverter
to install it. Once that is done a call like
the following reveals the cost a train ride I had to do for work a couple of
days ago:
% currency_converter --to SEK --date 2022-09-16 210 DKK 210.000 DKK = 303.682 SEK on 2022-09-16
Beware that (in version 0.17.1) there is no error detected when converting for
a missing date, so make sure to keep the conversion database updated. A
convenient way to accomplish that without having to think about it is to add
this function to .zshrc
:
currency_converter() { local python_version="$( python3 --version )" python_version="${python_version#* }" local max_age=1 # Days of age before refreshing the file. local eurofxref_url _eurofxref_file state eurofxref_url='https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.zip' eurofxref_file="$( printf "%s/%spython%s%s%s" "${HOME}" ".local/lib/" \ "${python_version%.*}" "/site-packages/currency_converter/" \ "eurofxref-hist.zip" )" state="$( find "${eurofxref_file}" -mtime "${max_age}" -printf 'OK' || :)" if [ "${state}" != 'OK' ]; then wget --output-document "${eurofxref_file}" "${eurofxref_url}" fi command currency_converter "$@" }
That wraps all calls to currency_converter
, performing an up-to-date check
and automatic database download prior to calling the actual command.