hospitality.tourist-tax
Visitor levy (tourist tax) on an overnight stay: Edinburgh's 5%, Cardiff's per person per night, or a caller's own rule.
1.0.0 (not the latest) · published 2026-10-03 by charlie · Anterra
Pinned by 30 tests, run in TypeScript, Python and Rust.
Not professional advice. This capability calculates tax figures from published rules. It is a software component for developers, not tax advice. Rules change and every rate here has an effective date. Check that the dates cover your case. Verify results against the official sources listed in its README, and have a tax adviser review how you use it, before anyone relies on the output. Provided “as is” under its licence, without warranty.
What it does
The visitor levy (tourist tax) due on one overnight stay, for a council area in the data or for a rule the caller supplies. It returns the levy, how many nights were levied, why nothing is due if it isn't, and the rule it used.
## What is in the data
For example
touristTax(GB-EDH, —, arrival 2026-08-10, nightly charges £150.00, £150.00, £150.00, accommodation standard, adults 2, children 0, booked on 2026-03-01, first paid on 2026-03-01)→ locality GB-EDH, levy £22.50, leviable nights 3, exemption —, rule … Edinburgh: 5% of three nights' accommodationtouristTax(GB-EDH, —, arrival 2026-08-10, nightly charges £120.00, £120.00, £120.00, £120.00, £120.00, £120.00, £120.00, accommodation standard, adults 2, children 0, booked on 2026-03-01, f…)→ locality GB-EDH, levy £30.00, leviable nights 5, exemption —, rule … Edinburgh: a week is levied on the first five nights onlytouristTax(GB-EDH, —, arrival 2026-08-10, nightly charges £100.00, £100.00, £200.00, £200.00, £200.00, £300.00, accommodation standard, adults 2, children 0, booked on 2026-03-01, first paid…)→ locality GB-EDH, levy £40.00, leviable nights 5, exemption —, rule … Edinburgh: the first five nights' own prices, not an average of the stay
The function
The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.
export function touristTax(locality: string, customRule: LevyRule | null, stay: Stay): TouristTax
| locality | string | ISO 3166-2 code of the council area, such as GB-EDH or GB-CRF; custom for customRule |
| customRule | LevyRule? | a levy the data does not have; null unless locality is custom |
| stay | Stay | one booking: its nights, guests and dates |
| returns | TouristTax |
The types it declares, generated into your project
export type LevyMethod = "percent" | "per-person-night";
export type AccommodationType = "standard" | "shared";
/** How a levy is charged. */
export interface LevyRule {
readonly method: LevyMethod;
/** percent: the rate on the accommodation charge; 500 = 5% */
readonly basisPoints: number;
/** per-person-night: the amount per liable person per night */
readonly amount: Money;
/** only the first this many nights are levied; null for all */
readonly maxNights: number | null;
/** a stay longer than this is not levied at all; null for none */
readonly exemptOverNights: number | null;
/** children are not counted as liable persons */
readonly under18Exempt: boolean;
}
/** One booking in one accommodation. */
export interface Stay {
readonly arrival: string;
/** the accommodation-only charge for each night, excluding VAT, meals, parking and other extras */
readonly nightlyCharges: readonly Money[];
/** shared for a campsite pitch or a shared room such as a dormitory */
readonly accommodation: AccommodationType;
/** guests aged 18 or over */
readonly adults: number;
/** guests under 18 on the first night */
readonly children: number;
readonly bookedOn: string;
/** the first payment, deposit included; null if nothing is paid yet */
readonly firstPaidOn: string | null;
}
/** The levy on the stay, and the rule that set it. */
export interface TouristTax {
readonly locality: string;
readonly levy: Money;
readonly leviableNights: number;
/** why no levy is due: not-in-force, booked-and-paid-before-scheme or long-stay; null otherwise */
readonly exemption: string | null;
/** the rule that applied; null when none is in force */
readonly rule: LevyRule | null;
}
Your code names it in one line, in the file that uses it
import { touristTax } from "#fune/hospitality.tourist-tax@^1";
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { addDays } from "./dates_add_days.ts"; ← from dates.add-days ^1.0.0 · built alongside by fune
import { type Money, assertSameCurrency, money } from "./money_amount.ts"; ← from money.amount ^1.0.0 · built alongside by fune
import { applyRate } from "./money_apply_rate.ts"; ← from money.apply-rate ^1.0.0 · built alongside by fune
import { sumMoney } from "./money_sum.ts"; ← from money.sum ^1.0.0 · built alongside by fune
import { VISITOR_LEVIES, VISITOR_LEVIES_HISTORY, type VisitorLevy } from "./hospitality_tourist_tax_data.ts"; ← this capability’s own data, compiled from data/visitor-levies.json into the same file by fune build
import { type LevyRule, type Stay, type TouristTax } from "./hospitality_tourist_tax_types.ts";
function checkRule(rule: LevyRule): void {
if (rule.method !== "percent" && rule.method !== "per-person-night") {
throw new RangeError(`unknown levy method "${rule.method}"`);
}
if (!Number.isInteger(rule.basisPoints) || rule.basisPoints < 0 || rule.basisPoints > 10000) {
throw new RangeError(`basisPoints must be a whole number from 0 to 10000, received ${rule.basisPoints}`);
}
if (rule.amount.minor < 0) throw new RangeError(`amount must not be negative, received ${rule.amount.minor}`);
if (rule.maxNights !== null && (!Number.isInteger(rule.maxNights) || rule.maxNights < 1)) {
throw new RangeError(`maxNights must be at least 1 or null, received ${rule.maxNights}`);
}
if (rule.exemptOverNights !== null && (!Number.isInteger(rule.exemptOverNights) || rule.exemptOverNights < 1)) {
throw new RangeError(`exemptOverNights must be at least 1 or null, received ${rule.exemptOverNights}`);
}
}
function toRule(row: VisitorLevy): LevyRule {
return {
method: row.method as LevyRule["method"],
basisPoints: row.basisPoints,
amount: money(row.amountMinor, row.currency),
maxNights: row.maxNights,
exemptOverNights: row.exemptOverNights,
under18Exempt: row.under18Exempt,
};
}
/**
* The visitor levy on one stay.
*
* The rule is the one in force on the arrival date: Edinburgh does not levy a
* stay that began before its scheme started, even for the nights after. A
* percentage levy is taken of the accommodation charge of the levied nights
* and rounded half-up once; a flat levy is the amount times the liable
* people times the levied nights.
*/
export function touristTax(locality: string, customRule: LevyRule | null, stay: Stay): TouristTax {
const charges = stay.nightlyCharges;
if (charges.length === 0) throw new RangeError("a stay needs at least one night");
const currency = charges[0].currency;
sumMoney(charges, currency);
for (const c of charges) {
if (c.minor < 0) throw new RangeError(`nightly charges must not be negative, received ${c.minor}`);
}
if (stay.accommodation !== "standard" && stay.accommodation !== "shared") {
throw new RangeError(`unknown accommodation type "${stay.accommodation}"`);
}
if (!Number.isInteger(stay.adults) || stay.adults < 0) throw new RangeError(`adults must not be negative, received ${stay.adults}`);
if (!Number.isInteger(stay.children) || stay.children < 0) {
throw new RangeError(`children must not be negative, received ${stay.children}`);
}
addDays(stay.arrival, 0);
addDays(stay.bookedOn, 0);
if (stay.firstPaidOn !== null) addDays(stay.firstPaidOn, 0);
const none = (exemption: string, rule: LevyRule | null): TouristTax => ({
locality,
levy: money(0, currency),
leviableNights: 0,
exemption,
rule,
});
let rule: LevyRule;
if (locality === "custom") {
if (customRule === null) throw new RangeError("locality custom needs a customRule");
checkRule(customRule);
rule = customRule;
} else {
if (customRule !== null) throw new RangeError("customRule is only used with locality custom");
const rows = VISITOR_LEVIES.filter((r) => r.locality === locality);
if (rows.length === 0) throw new RangeError(`no visitor levy data for locality "${locality}"`);
const row = rows.find(
(r) =>
(r.accommodation === "any" || r.accommodation === stay.accommodation) &&
r.validFrom <= stay.arrival &&
(r.validTo === null || stay.arrival <= r.validTo),
);
if (row === undefined) {
if (VISITOR_LEVIES_HISTORY !== "full") {
const earliest = rows.reduce((e, r) => (r.validFrom < e ? r.validFrom : e), rows[0].validFrom);
if (stay.arrival < earliest) {
throw new RangeError(
`no visitor levy rule for ${locality} on ${stay.arrival}: this build was installed with history=${VISITOR_LEVIES_HISTORY}, so it only carries rules from ${earliest}`,
);
}
}
return none("not-in-force", null);
}
rule = toRule(row);
const cutoff = row.paidBookingsExemptBefore;
if (cutoff !== null && stay.bookedOn < cutoff && stay.firstPaidOn !== null && stay.firstPaidOn < cutoff) {
return none("booked-and-paid-before-scheme", rule);
}
}
const nights = charges.length;
if (rule.exemptOverNights !== null && nights > rule.exemptOverNights) return none("long-stay", rule);
const leviableNights = rule.maxNights === null ? nights : Math.min(nights, rule.maxNights);
let levy: Money;
if (rule.method === "percent") {
levy = applyRate(sumMoney(charges.slice(0, leviableNights), currency), rule.basisPoints, "half-up");
} else {
assertSameCurrency(money(0, currency), rule.amount);
const people = stay.adults + (rule.under18Exempt ? 0 : stay.children);
levy = money(rule.amount.minor * people * leviableNights, currency);
}
return { locality, levy, leviableNights, exemption: null, rule };
}Install
fune build
With that line in your source, in a TypeScript project (language typescript in fune.project), fune build resolves it and its 4 dependencies, pins them in fune.lock, downloads only the TypeScript package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:
fune add hospitality.tourist-tax
The manifest, vectors and README with only the TypeScript implementation. Install it without the registry with fune add ./hospitality.tourist-tax-1.0.0-typescript.fune, or fetch it from a terminal with fune pull hospitality.tourist-tax@1.0.0:typescript.
The whole function, every language, is one file too: hospitality.tourist-tax-1.0.0.fune, 53,126 bytes, sha256 b9b7715fc3271365e6a7223046d761509829d32cc436557af815ba0530431232. It installs into a project of any language.
Customise it in your app
The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.
before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.
// fune: before hospitality.tourist-tax
after — your function gets the result and the arguments, and returns the final result.
// fune: after hospitality.tourist-tax
replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.
// fune: replace dates.add-days in hospitality.tourist-tax
// fune: replace money.amount in hospitality.tourist-tax
// fune: replace money.apply-rate in hospitality.tourist-tax
// fune: replace money.sum in hospitality.tourist-tax
step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show hospitality.tourist-tax --steps.
// fune: step hospitality.tourist-tax after <n|label>
Tests
A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.
| Case | Arguments | Expected | |
|---|---|---|---|
| Edinburgh: 5% of three nights' accommodation | GB-EDH, —, arrival 2026-08-10, nightly charges £150.00, £150.00, £150.00, accommodation standard, adults 2, children 0, booked on 2026-03-01, first paid on 2026-03-01 | → | locality GB-EDH, levy £22.50, leviable nights 3, exemption —, rule … |
| Edinburgh: a week is levied on the first five nights only | GB-EDH, —, arrival 2026-08-10, nightly charges £120.00, £120.00, £120.00, £120.00, £120.00, £120.00, £120.00, accommodation standard, adults 2, children 0, booked on 2026-03-01, f… | → | locality GB-EDH, levy £30.00, leviable nights 5, exemption —, rule … |
| Edinburgh: the first five nights' own prices, not an average of the stay | GB-EDH, —, arrival 2026-08-10, nightly charges £100.00, £100.00, £200.00, £200.00, £200.00, £300.00, accommodation standard, adults 2, children 0, booked on 2026-03-01, first paid… | → | locality GB-EDH, levy £40.00, leviable nights 5, exemption —, rule … |
| Edinburgh: rounds half-up, 5% of 123.45 is 6.1725 | GB-EDH, —, arrival 2026-08-10, nightly charges £123.45, accommodation standard, adults 2, children 0, booked on 2026-03-01, first paid on 2026-03-01 | → | locality GB-EDH, levy £6.17, leviable nights 1, exemption —, rule … |
| Edinburgh: an exact half penny rounds up, 5% of 123.50 is 6.175 | GB-EDH, —, arrival 2026-08-10, nightly charges £123.50, accommodation standard, adults 2, children 0, booked on 2026-03-01, first paid on 2026-03-01 | → | locality GB-EDH, levy £6.18, leviable nights 1, exemption —, rule … |
| Edinburgh: the first day of the scheme | GB-EDH, —, arrival 2026-07-24, nightly charges £100.00, accommodation standard, adults 2, children 0, booked on 2026-03-01, first paid on 2026-03-01 | → | locality GB-EDH, levy £5.00, leviable nights 1, exemption —, rule … |
| Edinburgh: a stay that began the day before the scheme is not levied, even after it starts | GB-EDH, —, arrival 2026-07-23, nightly charges £100.00, £100.00, £100.00, accommodation standard, adults 2, children 0, booked on 2026-03-01, first paid on 2026-03-01 | → | locality GB-EDH, levy £0.00, leviable nights 0, exemption not-in-force, rule — |
| Edinburgh: booked and part-paid before 1 October 2025 is exempt | GB-EDH, —, arrival 2026-08-10, nightly charges £100.00, accommodation standard, adults 2, children 0, booked on 2025-09-15, first paid on 2025-09-15 | → | locality GB-EDH, levy £0.00, leviable nights 0, exemption booked-and-paid-before-scheme, rule … |
| Edinburgh: booked before 1 October 2025 but not paid is levied | GB-EDH, —, arrival 2026-08-10, nightly charges £100.00, accommodation standard, adults 2, children 0, booked on 2025-09-15, first paid on — | → | locality GB-EDH, levy £5.00, leviable nights 1, exemption —, rule … |
| Edinburgh: booked before, first paid on 1 October 2025 is levied | GB-EDH, —, arrival 2026-08-10, nightly charges £100.00, accommodation standard, adults 2, children 0, booked on 2025-09-30, first paid on 2025-10-01 | → | locality GB-EDH, levy £5.00, leviable nights 1, exemption —, rule … |
Show the other 20 tests
| Case | Arguments | Expected | |
|---|---|---|---|
| Edinburgh: a hostel bed is levied at the same 5% | GB-EDH, —, arrival 2026-08-10, nightly charges £30.00, £30.00, accommodation shared, adults 1, children 0, booked on 2026-03-01, first paid on 2026-03-01 | → | locality GB-EDH, levy £3.00, leviable nights 2, exemption —, rule … |
| Cardiff: 1.30 per person per night, children included in a hotel | GB-CRF, —, arrival 2027-05-01, nightly charges £90.00, £90.00, £90.00, accommodation standard, adults 2, children 1, booked on 2026-03-01, first paid on 2026-03-01 | → | locality GB-CRF, levy £11.70, leviable nights 3, exemption —, rule … |
| Cardiff: 75p per adult per night in a hostel, under-18s disregarded | GB-CRF, —, arrival 2027-05-01, nightly charges £25.00, £25.00, accommodation shared, adults 2, children 2, booked on 2026-03-01, first paid on 2026-03-01 | → | locality GB-CRF, levy £3.00, leviable nights 2, exemption —, rule … |
| Cardiff: 31 nights is levied on every night | GB-CRF, —, arrival 2027-05-01, nightly charges £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.… | → | locality GB-CRF, levy £40.30, leviable nights 31, exemption —, rule … |
| Cardiff: a stay of more than 31 nights is not levied at all | GB-CRF, —, arrival 2027-05-01, nightly charges £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.00, £50.… | → | locality GB-CRF, levy £0.00, leviable nights 0, exemption long-stay, rule … |
| Cardiff: before 1 April 2027 there is no levy | GB-CRF, —, arrival 2027-03-31, nightly charges £90.00, accommodation standard, adults 2, children 0, booked on 2026-03-01, first paid on 2026-03-01 | → | locality GB-CRF, levy £0.00, leviable nights 0, exemption not-in-force, rule — |
| Cardiff: the first day of the levy | GB-CRF, —, arrival 2027-04-01, nightly charges £90.00, accommodation standard, adults 2, children 0, booked on 2026-03-01, first paid on 2026-03-01 | → | locality GB-CRF, levy £2.60, leviable nights 1, exemption —, rule … |
| a caller's own percentage levy with no caps | custom, method percent, rate 10%, amount £0.00, max nights —, exempt over nights —, under18 exempt false, arrival 2026-08-10, nightly charges £100.00, £100.00, accommodation stand… | → | locality custom, levy £20.00, leviable nights 2, exemption —, rule … |
| a caller's own flat levy capped at 7 nights, in euros | custom, method per-person-night, rate 0%, amount €2.00, max nights 7, exempt over nights —, under18 exempt false, arrival 2026-08-10, nightly charges €80.00, €80.00, €80.00, €80.0… | → | locality custom, levy €42.00, leviable nights 7, exemption —, rule … |
| an unknown locality is an error | GB-LND, —, arrival 2026-08-10, nightly charges £100.00, accommodation standard, adults 2, children 0, booked on 2026-03-01, first paid on 2026-03-01 | → | error: no visitor levy data for locality "GB-LND" |
| custom without a rule is an error | custom, —, arrival 2026-08-10, nightly charges £100.00, accommodation standard, adults 2, children 0, booked on 2026-03-01, first paid on 2026-03-01 | → | error: locality custom needs a customRule |
| a rule with a known locality is an error | GB-EDH, method percent, rate 10%, amount £0.00, max nights —, exempt over nights —, under18 exempt false, arrival 2026-08-10, nightly charges £100.00, accommodation standard, adul… | → | error: customRule is only used with locality custom |
| a stay with no nights is an error | GB-EDH, —, arrival 2026-08-10, nightly charges , accommodation standard, adults 2, children 0, booked on 2026-03-01, first paid on 2026-03-01 | → | error: a stay needs at least one night |
| nights in two currencies are an error | GB-EDH, —, arrival 2026-08-10, nightly charges £100.00, €100.00, accommodation standard, adults 2, children 0, booked on 2026-03-01, first paid on 2026-03-01 | → | error: currency mismatch |
| a flat levy in another currency from the stay is an error | custom, method per-person-night, rate 0%, amount £2.00, max nights —, exempt over nights —, under18 exempt false, arrival 2026-08-10, nightly charges €80.00, accommodation standar… | → | error: currency mismatch |
| an unknown accommodation type is an error | GB-CRF, —, arrival 2027-05-01, nightly charges £90.00, accommodation caravan, adults 2, children 0, booked on 2026-03-01, first paid on 2026-03-01 | → | error: unknown accommodation type "caravan" |
| negative guests are an error | GB-CRF, —, arrival 2027-05-01, nightly charges £90.00, accommodation standard, adults -1, children 0, booked on 2026-03-01, first paid on 2026-03-01 | → | error: adults must not be negative |
| a negative nightly charge is an error | GB-EDH, —, arrival 2026-08-10, nightly charges -£1.00, accommodation standard, adults 2, children 0, booked on 2026-03-01, first paid on 2026-03-01 | → | error: nightly charges must not be negative |
| an impossible booking date is an error | GB-EDH, —, arrival 2026-08-10, nightly charges £100.00, accommodation standard, adults 2, children 0, booked on 2026-02-30, first paid on 2026-03-01 | → | error: is not a real calendar date |
| an unknown levy method is an error | custom, method per-room, rate 0%, amount £2.00, max nights —, exempt over nights —, under18 exempt false, arrival 2026-08-10, nightly charges £100.00, accommodation standard, adul… | → | error: unknown levy method "per-room" |
More from the author
Only levies that are law and have a start date. Each is a dated row in `data/visitor-levies.json`:
| locality | levy | from | rules | |---|---|---|---| | `GB-EDH` City of Edinburgh | 5% of the accommodation charge, net of VAT | 24 July 2026 | first 5 consecutive nights of a booking only; bookings made **and** paid (in part or in full) before 1 October 2025 are exempt; stays that began before 24 July 2026 are not levied | | `GB-CRF` Cardiff | £1.30 per person per night; £0.75 on a campsite pitch or in a shared room (dormitory) | 1 April 2027 | no levy on a stay of more than 31 nights; under-18s are not counted at the lower rate |
Wales's Act lets each council choose whether to charge. As of September 2026, Cardiff is the only council that has decided to. Anglesey, Conwy and Gwynedd have consulted, with decisions due after 24 September 2026. Add a council's rows when its decision is made, as a new version of this package. Other Scottish councils' schemes (Glasgow's, for one) are not in this version.
Locality codes are ISO 3166-2 (`GB-EDH`, `GB-CRF`). Use `custom` with a `customRule` for any other levy: a percentage or a flat amount per person per night, with an optional night cap (`maxNights`), a long-stay exemption (`exemptOverNights`) and whether under-18s count.
## How a stay is levied
- **Which rule:** the one in force on the arrival date, for the stay's accommodation type. Edinburgh's guidance says a stay that began before 24 July 2026 is not liable even for the nights after that date, and this applies the same "by arrival" logic to every locality. - **Percentage levies** are taken of the sum of the levied nights' accommodation charges and rounded half-up once. `nightlyCharges` must be the accommodation-only amount for each night: without VAT, and without meals, drinks, parking, laundry, entertainment or transport, which the Scottish Act (s5(3)) excludes. When a room rate includes breakfast, apportioning it is the caller's job. - **Flat levies** are the amount x liable people x levied nights. `adults` and `children` (under 18 on the first night) are the guests entitled to stay. - **Exemptions** for individuals (Scotland's exemptions for people receiving certain disability benefits, s14 of the 2024 Act; people with no usual residence) are the caller's decision: leave those people out of the count, or do not call this for an exempt booking.
## Not covered
- **VAT on the levy.** Cardiff Council says its levy is subject to VAT. HMRC's treatment is not modelled here. The levy returned is before any VAT on it. - **Cancellations and refunds** (a levy collected on a cancelled booking is refunded, not remitted) and when to collect the levy. - Returns to the council or the Welsh Revenue Authority.
## Dated data and pruning
The rows have `validFrom`/`validTo` and are pruned by `history=`. In a pruned build, an arrival date before a locality's earliest surviving row is refused with an explanation rather than reported as "not in force".
## Sources
- Visitor Levy (Scotland) Act 2024 (asp 8), s5 (calculation on the accommodation portion) and s14 (exemptions): https://www.legislation.gov.uk/asp/2024/8 ; amended by the Visitor Levy (Amendment) (Scotland) Act 2026 (asp 18). - City of Edinburgh Council, *The Scheme for the Edinburgh Visitor Levy*: https://www.edinburgh.gov.uk/business/scheme-edinburgh-visitor-levy/2 (5%, five consecutive nights, stays from 24 July 2026, bookings made on or after 1 October 2025, and the exemption for bookings paid before then). - City of Edinburgh Council, *Edinburgh Visitor Levy Scheme: Information for accommodation providers*, version 2, August 2026: https://www.edinburgh.gov.uk/visitorlevyinformation ("5% of the accommodation only portion of the transaction, net of VAT"; stays commencing before 24 July 2026 not liable; the 5-night cap per chargeable transaction). - Visitor Accommodation (Register and Levy) Etc. (Wales) Act 2025 (asc 5), Part 3: s29 (overnight stays; no levy where every person is entitled to stay more than 31 nights), s31 (leviable nights; under-18s disregarded at the lower rate), s32 (rates £0.75 and £1.30), s33 (which rate applies): https://www.legislation.gov.uk/asc/2025/5/contents - Welsh Government, *Check if a local council charges Visitor Levy in Wales*: https://www.gov.wales/check-if-local-council-charges-visitor-levy-wales (Cardiff from 1 April 2027), and *Rates of the levy*: https://www.gov.wales/visitor-levy-for-visitor-accommodation-providers-technical-guidance-rates-levy-html
Files
| Path | Bytes |
|---|---|
| README.md | 4,865 |
| data/visitor-levies.json | 1,738 |
| impl/python.py | 5,620 |
| impl/rust.rs | 8,089 |
| impl/typescript.ts | 5,364 |
| vectors.json | 18,249 |