Skip to content

Releases: velikodniy/hmrc-rates

v0.3.1

Choose a tag to compare

@velikodniy velikodniy released this 09 Jul 16:51
1820c93

Security patch

Updates the transitive bytes dependency to 1.12.1, addressing GHSA-434x-w66g-qw3r — a medium-severity integer overflow in BytesMut::reserve (affected: bytes >= 1.2.1, < 1.11.1). It reaches this crate transitively through ureq, so it applies with the http feature enabled and to the Python wheel (which enables it).

No API changes.

Also

  • Dropped "Official" from the crate/module docs, CLI --help, and the PyPI description, and added a Disclaimer section clarifying the project is not affiliated with, endorsed by, or maintained by HMRC — only the rate data comes from them.

v0.3.0

Choose a tag to compare

@velikodniy velikodniy released this 09 Jul 16:36
2fdb6d7

⚠️ Breaking release

This release renames the year-month value type and everything named after it, and ships the first Python bindings on PyPI.

Python bindings — new

pip install hmrc-rates (Python ≥ 3.10). abi3 wheels for Linux (glibc + musl, x86_64 + aarch64), macOS (universal2) and Windows (x64); the sdist builds anywhere with a Rust toolchain.

from decimal import Decimal
from hmrc_rates import YearMonth, Rates, YearEnd

rates = Rates()
rate = rates.monthly_rate("USD", YearMonth(2025, 8))   # also accepts datetime.date or "2025-08"
gbp = rate.to_gbp(Decimal("2500"))                     # exact decimal.Decimal — you choose the rounding

Breaking changes

Rust crate

  • MonthYearMonth, and ParseMonthErrorParseYearMonthError.
  • month arguments → year_month on Rates::monthly_rate, monthly_rate_or_earlier, monthly.
  • YearEnd::from_monthfrom_year_month; YearEnd::end_monthend_year_month.
  • Period::Month variant → Period::YearMonth.
  • Serde wire format changed: a Period now serializes as {"year_month": "2025-08"} (previously {"month": …}). Data serialized by earlier versions will not deserialize.
  • The dependency pin is now hmrc-rates = "0.3".

Python

  • Class MonthYearMonth; type alias MonthLikeYearMonthLike.
  • Keyword argument month=year_month= on the lookup methods.
  • YearEnd.from_monthfrom_year_month; YearEnd.end_monthend_year_month.
  • Period.kind now returns "year_month" (was "month"); the Period.month property is now Period.year_month.

Unchanged

YearMonth.month/.year (the numeric 1–12 month and the year), RateType/MONTHLY, the monthly*() lookups, the exception hierarchy, and the bundled data coverage.

v0.2.0

Choose a tag to compare

@velikodniy velikodniy released this 09 Jul 08:53
57546d9

Complete rewrite. Breaking changes throughout — not compatible with 0.1.x.

What's new

  • All four HMRC rate series, not just monthly: monthly customs/VAT, spot, yearly average, and the discontinued 2014-2016 weekly amendments, all through one Rates value.
  • Deeper history: monthly back to 2014-02 (150 months), spot and average back to Dec 2010 (32 periods each, years ending 31 Mar / 31 Dec), backfilled from the UK Government Web Archive.
  • Zero-cost bundling: the full dataset compiles into static tables at build time — Rates::new() is free and infallible, no parsing or I/O at startup.
  • Exact arithmetic, strict lookups: conversions use rust_decimal and are never rounded; an unpublished period is always an error, never a silently substituted older rate. An explicit, opt-in fallback (monthly_rate_or_earlier) is available when you want it.
  • Optional live updates (http feature): a blocking Updater fetches newly published periods from the Trade Tariff API, with a verbatim on-disk cache.
  • no_std + alloc core: builds on wasm32-unknown-unknown with just the bundled feature.
  • serde feature for compact string forms ("2026-07", "USD", "monthly").
  • Optional CLI (cli feature): hmrc-rates convert, rate, list, currencies.
  • CI now checks for new HMRC rates daily and auto-releases patch versions.

Example

use hmrc_rates::{Month, Rates, YearEnd};
use rust_decimal::Decimal;

let rates = Rates::new();
let rate = rates.monthly_rate("USD", Month::new(2025, 8).unwrap())?;
println!("{}", rate.to_gbp(Decimal::from(2500)).round_dp(2)); // £1846.24

let eur = rates.average(YearEnd::march(2025))?.rate("EUR")?;
println!("{}", eur.units_per_gbp()); // 1.1880

Coverage

Series Range
Monthly 2014-02 - present
Spot Dec 2010 - present (31 Mar / 31 Dec)
Yearly average Dec 2010 - present (31 Mar / 31 Dec)
Weekly amendments 2014-01-08 - 2016-04-27 (discontinued)

Full details: README · data sources · API docs

MSRV 1.85 · MIT

v0.1.0

Choose a tag to compare

@velikodniy velikodniy released this 14 Aug 13:44
3cf7dda

Initial release