Skip to content

Commit

Permalink
new feature: currency XCG
Browse files Browse the repository at this point in the history
  • Loading branch information
karczk-dnv committed Dec 11, 2023
1 parent f64f076 commit 86cceb8
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 45 deletions.
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,31 @@
# Changelog
Strictly follows [Semantic Versioning 2.0.0.](https://semver.org/)

## v1.14.0
`2023-12-11`\
\
:rocket: Features:
- support for `XCG` currency (future replacement for `ANG`): [`findIso4217Currency()`](DOCUMENTATION.md#findIso4217Currency) and [`findIso4217CurrencyForIso3166Country()`](DOCUMENTATION.md#findIso4217CurrencyForIso3166Country)

```typescript
{
alpha3Code: "ANG",
currencyName: "Netherlands Antillean Guilder",
numericCode: 532,
minorUnit: 2,
historicalFrom: "2025-07-01" // CHANGED (before: undefined)
},
// ... other currencies
{
alpha3Code: "XCG", // NEW
currencyName: "Caribbean Guilder", // NEW
numericCode: 532, // NEW
minorUnit: 2, // NEW
introducedIn: "2025-03-31" // NEW
},
```
- [`findIso4217CurrencyForIso3166Country()`](DOCUMENTATION.md#findIso4217CurrencyForIso3166Country): new argument `statusForTheDay` has been introduced

## v1.13.0
`2023-02-21`\
\
Expand Down
5 changes: 4 additions & 1 deletion DOCUMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ findIso3166Country("XX"); // returns undefined
```

### findIso4217Currency()
Returns specific currency information. Please check [`getIso4217Currencies()`](DOCUMENTATION.md#getIso4217Currencies) to learn more about the list of currencies.
Returns specific currency information. Please check [`getIso4217Currencies()`](DOCUMENTATION.md#getIso4217Currencies) to learn more about the list of currencies. Please be aware that only `alpha3Code` is unique for all currencies (e.g. `numericCode` is the same for `AND` and `XCG` currencies).

```typescript
import { findIso4217Currency } from '@dnvgl/i18n';
Expand Down Expand Up @@ -178,6 +178,9 @@ findIso4217CurrencyForIso3166Country("usa"); // returns undefined; invalid code
findIso4217CurrencyForIso3166Country("ATA"); // returns undefined; ATA = Antarctica; no universal currency
findIso4217CurrencyForIso3166Country("pl"); // returns undefined
findIso4217CurrencyForIso3166Country("XX"); // returns undefined
findIso4217CurrencyForIso3166Country("HRV"); // returns { alpha3Code: "EUR", currencyName: "Euro... }
findIso4217CurrencyForIso3166Country("HRV", "2022-05-05"); // returns { alpha3Code: "HRK", currencyName: "Kuna... }

```

### formatCountry()
Expand Down
7 changes: 7 additions & 0 deletions __tests__/findIso4217Currency.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,11 @@ describe('findIso4217Currency', () => {
])('does not find currency by %p code', (code) => {
expect(findIso4217Currency(code)).toBeUndefined();
});

test('finds currency for numeric code that has two currencies', () => {
expect(findIso4217Currency(532)).toMatchObject({
numericCode: 532,
minorUnit: 2
});
});
});
35 changes: 31 additions & 4 deletions __tests__/findIso4217CurrencyForIso3166Country.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,45 @@ describe('findIso4217CurrencyForIso3166Country.', () => {
expect(findIso4217CurrencyForIso3166Country(code)).toBeUndefined();
});

test('finds old currency for Croatia', () => {
expect(findIso4217CurrencyForIso3166Country("HRV")).toMatchObject( {
alpha3Code: "EUR",
currencyName: "Euro",
numericCode: 978,
minorUnit: 2
});

expect(findIso4217CurrencyForIso3166Country("HRV", "2023-01-01")).toMatchObject( {
alpha3Code: "EUR",
currencyName: "Euro",
numericCode: 978,
minorUnit: 2
});
});

test('finds new currency for Croatia', () => {
expect(findIso4217CurrencyForIso3166Country("HRV", "2022-05-05")).toMatchObject( {
alpha3Code: "HRK",
currencyName: "Kuna",
numericCode: 191,
minorUnit: 2
});
});

test('Map: all countries are covered', () => {
expect(iso3166CountryToIso4217Currency.size).toEqual(iso3166Countries.length);
});

test('Map: all currencies are found', () => {
iso3166Countries.forEach(country => {
const currencyCode = iso3166CountryToIso4217Currency.get(country.alpha3Code);
const resolver = iso3166CountryToIso4217Currency.get(country.alpha3Code);

if (currencyCode) {
const currency = findIso4217Currency(currencyCode);
if (resolver) {
const currency = findIso4217Currency(typeof resolver === "string" ? resolver : resolver(new Date()));
expect(currency).toBeDefined();
expect(currency?.historicalFrom).toBeUndefined();
if (!!currency?.historicalFrom) {
expect(new Date(currency.historicalFrom).getTime()).toBeGreaterThan(new Date().getTime());
}
}
})
});
Expand Down
33 changes: 24 additions & 9 deletions __tests__/getIso4217Currencies.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { getIso4217Currencies, Iso4217Currency } from "../src";
import { getIso4217Currencies } from "../src";
import { iso4217Currencies } from "../src/internal/iso4217Currencies";

const isArrayUnique = (arr: any[]) => Array.isArray(arr)
&& new Set(arr).size === arr.length;

const nameof = <T>(name: keyof T) => name;

describe('getIso4217Currencies', () => {
test('all properties are properly defined', () => {
const countries = getIso4217Currencies("2022-12-31");
Expand All @@ -24,22 +22,39 @@ describe('getIso4217Currencies', () => {
expect(countries.find(x => x.alpha3Code === "HRK")).toBeUndefined();
});

test('Kuna currency is returned', () => {
test('Kuna (HRK) currency is returned', () => {
const countries = getIso4217Currencies("2022-12-31");
expect(countries).toHaveLength(181);
expect(countries.find(x => x.alpha3Code === "HRK")).toBeDefined();
});

test('Caribbean Guilder (XCG) and Netherlands Antillean Guilder (ANG) currencies are returned', () => {
const countries = getIso4217Currencies("2025-03-31");
expect(countries).toHaveLength(181);
expect(countries.find(x => x.alpha3Code === "XCG")).toBeDefined();
expect(countries.find(x => x.alpha3Code === "ANG")).toBeDefined();
});

test('Netherlands Antillean Guilder (ANG) currency is no longer returned', () => {
const countries = getIso4217Currencies("2025-07-01");
expect(countries).toHaveLength(180);
expect(countries.find(x => x.alpha3Code === "ANG")).toBeUndefined();
});

test('function should return the same reference', () => {
const countries1 = getIso4217Currencies("2022-12-31");
const countries2 = getIso4217Currencies("2022-12-31");
expect(countries1 === countries2).toBeTruthy();
});

test.each([
[nameof<Iso4217Currency>("alpha3Code")],
[nameof<Iso4217Currency>("numericCode")],
])('no duplicates for property %p', (propName) => {
expect(isArrayUnique(iso4217Currencies.map(x => x[propName]))).toBeTruthy();
test('no duplicates for property alpha3Code', () => {
expect(isArrayUnique(iso4217Currencies.map(x => x.alpha3Code))).toBeTruthy();
});

test('no duplicates for property numericCode', () => {
const currenciesWithoutKnownNumericCodeDuplicates = iso4217Currencies
.filter(x => x.alpha3Code !== "ANG" && x.alpha3Code !== "XCG");

expect(isArrayUnique(currenciesWithoutKnownNumericCodeDuplicates.map(x => x.numericCode))).toBeTruthy();
});
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@dnvgl/i18n",
"version": "1.13.0",
"version": "1.14.0",
"description": "A set of functions to support multiple languages/cultures in a browser or Node.js",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
Expand Down
19 changes: 19 additions & 0 deletions src/findIso4217Currency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@ import { iso4217Currencies } from "./internal/iso4217Currencies";
import { Iso4217Alpha3Code, Iso4217Currency, Iso4217NumericCode } from "./types/iso4217";

export function findIso4217Currency(code: Iso4217Alpha3Code | Iso4217NumericCode): Iso4217Currency | undefined {
if (code === 532) {
// HINT: edge case with duplicated numeric code (for some period both are active currencies)
const xcgCurrency = iso4217Currencies.find(x => x.alpha3Code === "XCG")!;
const angCurrency = iso4217Currencies.find(x => x.alpha3Code === "ANG")!;
const now = new Date();

if (now >= new Date(xcgCurrency.introducedIn!)) {
if (new Date(angCurrency.historicalFrom!) <= now) {
console?.warn?.("In the current period there are two active currencies with the same currency numerical code."
+ " The new XCG currency is returned for this period, which will ultimately replace ANG."
+ " To make sure you select the intended currency please use alpha 3 code.");
}

return xcgCurrency;
}

return angCurrency;
}

const predicate: (value: Iso4217Currency) => boolean =
typeof code === "string"
? x => x.alpha3Code === code
Expand Down
19 changes: 15 additions & 4 deletions src/findIso4217CurrencyForIso3166Country.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { convertToDate } from "./internal/convertToDate";
import { iso3166Countries } from "./internal/iso3166Countries";
import { iso3166CountryToIso4217Currency } from "./internal/iso3166CountryToIso4217Currency";
import { iso4217Currencies } from "./internal/iso4217Currencies";
import { DateIsoString } from "./types/dateIsoString";
import { Iso3166Alpha2Code, Iso3166Alpha3Code, Iso3166NumericCode } from "./types/iso3166";
import { Iso4217Currency } from "./types/iso4217";

export function findIso4217CurrencyForIso3166Country(code: Iso3166Alpha2Code | Iso3166Alpha3Code | Iso3166NumericCode): Iso4217Currency | undefined {
export function findIso4217CurrencyForIso3166Country(
code: Iso3166Alpha2Code | Iso3166Alpha3Code | Iso3166NumericCode,
statusForTheDay?: Date | DateIsoString): Iso4217Currency | undefined {
const countryAlpha3Code = typeof code === "string"
? code.length === 3
? code
Expand All @@ -15,11 +19,18 @@ export function findIso4217CurrencyForIso3166Country(code: Iso3166Alpha2Code | I
return undefined;
}

const currencyAlpha3Code = iso3166CountryToIso4217Currency.get(countryAlpha3Code);
const resolver = iso3166CountryToIso4217Currency.get(countryAlpha3Code);

if (!currencyAlpha3Code) {
if (!resolver) {
return undefined;
}

return iso4217Currencies.find(x => x.alpha3Code === currencyAlpha3Code);
if (typeof resolver === "string") {
return iso4217Currencies.find(x => x.alpha3Code === resolver);
}

const pointInTime = statusForTheDay ? convertToDate(statusForTheDay) : new Date();
const resolvedCurrencyAlpha3CodeByDate = resolver(pointInTime);

return iso4217Currencies.find(x => x.alpha3Code === resolvedCurrencyAlpha3CodeByDate);
}
11 changes: 7 additions & 4 deletions src/internal/iso3166CountryToIso4217Currency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { Iso3166Alpha3Code } from "../types/iso3166";
import { Iso4217Alpha3Code } from "../types/iso4217";

/** @internal */
export const iso3166CountryToIso4217Currency = new Map<Iso3166Alpha3Code, Iso4217Alpha3Code|undefined>([
export type Iso4217Alpha3CodeResolver = Iso4217Alpha3Code |undefined | ((d: Date) => Iso4217Alpha3Code);

/** @internal */
export const iso3166CountryToIso4217Currency = new Map<Iso3166Alpha3Code, Iso4217Alpha3CodeResolver>([
["AFG", "AFN"],
["ALA", "EUR"],
["ALB", "ALL"],
Expand Down Expand Up @@ -58,9 +61,9 @@ export const iso3166CountryToIso4217Currency = new Map<Iso3166Alpha3Code, Iso421
["COK", "NZD"],
["CRI", "CRC"],
["CIV", "XOF"],
["HRV", "EUR"],
["HRV", (d: Date) => d >= new Date("2023-01-01") ? "EUR" : "HRK"],
["CUB", "CUP"],
["CUW", "ANG"],
["CUW", (d: Date) => d >= new Date("2025-03-31") ? "XCG" : "ANG"],
["CYP", "EUR"],
["CZE", "CZK"],
["DNK", "DKK"],
Expand Down Expand Up @@ -204,7 +207,7 @@ export const iso3166CountryToIso4217Currency = new Map<Iso3166Alpha3Code, Iso421
["SYC", "SCR"],
["SLE", "SLE"],
["SGP", "SGD"],
["SXM", "ANG"],
["SXM", (d: Date) => d >= new Date("2025-03-31") ? "XCG" : "ANG"],
["SVK", "EUR"],
["SVN", "EUR"],
["SLB", "SBD"],
Expand Down
10 changes: 9 additions & 1 deletion src/internal/iso4217Currencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export const iso4217Currencies: Iso4217Currency[] = [
alpha3Code: "ANG",
currencyName: "Netherlands Antillean Guilder",
numericCode: 532,
minorUnit: 2
minorUnit: 2,
historicalFrom: "2025-07-01"
},
{
alpha3Code: "AOA",
Expand Down Expand Up @@ -1019,6 +1020,13 @@ export const iso4217Currencies: Iso4217Currency[] = [
numericCode: 951,
minorUnit: 2
},
{
alpha3Code: "XCG",
currencyName: "Caribbean Guilder",
numericCode: 532,
minorUnit: 2,
introducedIn: "2025-03-31"
},
{
alpha3Code: "XDR",
currencyName: "SDR (Special Drawing Right)",
Expand Down
37 changes: 16 additions & 21 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -609,9 +609,9 @@
integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==

"@tsconfig/node16@^1.0.2":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
version "1.0.4"
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9"
integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==

"@types/babel__core@^7.1.14":
version "7.20.4"
Expand Down Expand Up @@ -697,9 +697,9 @@
undici-types "~5.26.4"

"@types/node@^20.10.3":
version "20.10.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.3.tgz#4900adcc7fc189d5af5bb41da8f543cea6962030"
integrity sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==
version "20.10.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.4.tgz#b246fd84d55d5b1b71bf51f964bd514409347198"
integrity sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==
dependencies:
undici-types "~5.26.4"

Expand Down Expand Up @@ -744,20 +744,15 @@ acorn-walk@^8.0.2:
integrity sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==

acorn-walk@^8.1.1:
version "8.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
version "8.3.1"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.1.tgz#2f10f5b69329d90ae18c58bf1fa8fccd8b959a43"
integrity sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==

acorn@^8.1.0, acorn@^8.8.1:
acorn@^8.1.0, acorn@^8.4.1, acorn@^8.8.1:
version "8.11.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b"
integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==

acorn@^8.4.1:
version "8.8.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==

agent-base@6:
version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
Expand Down Expand Up @@ -2584,9 +2579,9 @@ ts-jest@^29.1.1:
yargs-parser "^21.0.1"

ts-node@^10.8.0:
version "10.9.1"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
version "10.9.2"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f"
integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==
dependencies:
"@cspotcode/source-map-support" "^0.8.0"
"@tsconfig/node10" "^1.0.7"
Expand All @@ -2613,9 +2608,9 @@ type-fest@^0.21.3:
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==

typescript@^5.3.2:
version "5.3.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.2.tgz#00d1c7c1c46928c5845c1ee8d0cc2791031d4c43"
integrity sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==
version "5.3.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37"
integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==

undici-types@~5.26.4:
version "5.26.5"
Expand Down

0 comments on commit 86cceb8

Please sign in to comment.