SkillAgentSearch skills...

NodaMoney

NodaMoney provides a library that treats Money as a first class citizen and handles all the ugly bits like currencies and formatting.

Install / Use

/learn @RemyDuijkeren/NodaMoney
About this skill

Quality Score

0/100

Category

Design

Supported Platforms

Universal

README

NodaMoney

NuGet NuGet Pre-release NuGet CI

Overview

NodaMoney is a small, focused .NET library that treats money as a first‑class citizen. It gives you type‑safe money and currency types, correct rounding, parsing/formatting, and currency‑aware arithmetic so you don’t have to reinvent the tricky bits.

Why not just decimal? The built‑in decimal is great for precision but has no concept of currency, minor units (cents), symbols or culture‑aware formatting, nor common money behaviors (like splitting amounts without losing a cent). NodaMoney provides these domain concepts and behaviors so you can write clearer and safer code.

NodaMoney is inspired by the Java library JodaMoney (just like NodaTime is inspired by JodaTime). It aims to be a solid base layer with clear, minimal APIs and ISO‑4217 currency data.

What you get

  • Money: immutable, currency‑aware value type with safe operators and culture‑aware formatting/parsing
  • FastMoney: high‑throughput alternative using 64‑bit integer arithmetic (fixed 4‑decimal scale)
  • Currency and CurrencyInfo: ISO 4217 catalog with metadata; supports custom currencies
  • Rounding and MoneyContext: configurable rounding/scale and default currency; DI support and named contexts
  • ExchangeRate: represent currency pairs and convert between currencies
  • Serialization: XML, System.Text.Json and Newtonsoft.Json converters; type converters
  • Dependency Injection package: Microsoft.Extensions.* integration for configuring MoneyContext
  • AOT‑friendly builds and multi‑TFM support (see Compatibility below)

Installation

dotnet add package NodaMoney
dotnet add package NodaMoney.DependencyInjection # optional DI integration

Quick start

using NodaMoney;

var price = new Money(12.99m, "USD");
var tax = price * 0.21m;      // currency‑safe arithmetic
var total = price + tax;      // auto‑rounded to minor unit

string text = total.ToString("C", new System.Globalization.CultureInfo("en-US"));
// e.g. "$15.72"

// Parse
var parsed = Money.Parse("$15.72", Currency.FromCode("USD"));

// Split without losing cents
var shares = total.Split(3);  // e.g. [$5.24, $5.24, $5.24]

Main building blocks

  • Money: An immutable structure that represents money in a specified currency
  • FastMoney: A high‑performance immutable structure optimized for arithmetic
  • Currency: A compact immutable structure that represents a currency unit
  • CurrencyInfo: Currency metadata (ISO 4217 + custom); implicitly converts to Currency
  • MoneyContext: Configurable rounding, scale and default currency; supports DI and named contexts
  • ExchangeRate: Represents a currency pair to convert between currencies

Usage

Money type is based on decimal and has the same 28-digit precision (±1.0 × 10^-28 to ±7.9 × 10^28). Money also has the same size as a decimal, even with the extra Currency and MoneyContext information. By default, Money is always rounded to the currency minor unit (use MoneyContext to override), which is executed after every arithmetic operation.

Initializing money

// Define money with explicit currency
Money euros = new Money(6.54m, "EUR");
Money euros = new (6.54m, "EUR");
Money euros = new Money(6.54m, Currency.FromCode("EUR"));
Money euros = new Money(6.54m, CurrencyInfo.FromCode("EUR"));

// From existing money
Money dollars = euros with { Currency = CurrencyInfo.FromCode("USD") };
Money myEuros = euros with { Amount = 10.12m };

// Define money explicit using helper method for most used currencies in the world
Money euros = Money.Euro(6.54m);
Money dollars = Money.USDollar(6.54m);
Money pounds = Money.PoundSterling(6.54m);
Money yens = Money.Yen(6);

// Implicit Currency based on current culture/region.
// When culture is 'NL-nl' code below results in Euros.
Money euros = new Money(6.54m);
Money euros = new (6.54m);
Money euros = (Money)6.54m;

// Auto-rounding to the minor unit will take place with MidpointRounding.ToEven
// also known as banker's rounding
Money euro = new Money(765.425m, "EUR"); // EUR 765.42
Money euro = new Money(765.425m, "EUR", MidpointRounding.AwayFromZero); // EUR 765.43

// Deconstruct money
Money money = new Money(10m, "EUR");
var (amount, currency) = money;

Money operations

Money euro10 = Money.Euro(10);
Money euro20 = Money.Euro(20);
Money dollar10 = Money.USDollar(10);
Money zeroDollar = Money.USDollar(0);

// Compare money
euro10 == euro20; // false
euro10 != euro20; // true;
euro10 == dollar10; // false;
euro20 > euro10; // true;
euro10 <= dollar10; // throws InvalidCurrencyException!
zeroEuro == zeroDollar; // true; special zero handling

// Add and Subtract
Money euro30 = euro10 + euro20;
Money euro10 = euro20 - euro10;
Money m = euro10 + dollar10; // throws InvalidCurrencyException!
Money euro10 = euro10 + zeroDollar; // doesn't throw when adding zero
euro20 += euro10; // EUR 30
euro20 -= euro10; // EUR 10

// Add and Substract with implied Currency Context
Money euro30 = euro10 + 20m; // decimal value is assumed to have the same currency context
Money euro10 = euro20 - 10m; // decimal value is assumed to have the same currency context

// Decrement and Increment by minor unit
Money yen = new Money(765m, "JPY"); // the smallest unit is 1 yen
Money euro = new Money(765.43m, "EUR"); // the smallest unit is 1 cent (1EUR = 100 cent)
++yen; // JPY 766
--yen; // JPY 765
++euro; // EUR 765.44
--euro; // EUR 765.43

// Multiply
Money m = euro10 * euro20; // doesn't compile!
Money euro20 = euro10 * 2;
Money discount = euro10 * 0.15m;

// Divide
decimal ratio = euro20 / euro10;
Money euro5 = euro10 / 2;

// Divide without losing money
Money total = new Money(101m, "USD");
IEnumerable<Money> inShares = total.Split(4); // [USD 25, USD 25, USD 25, USD 26]
IEnumerable<Money> byRatio = total.Split([2,1,3]); // [USD 33.67, USD 16.83, USD 50.50]

// Modulus / Remainder
Money total = new Money(105.50m, "USD");
Money unitPrice = new Money(20.00m, "USD"); // USD 20 * 5 = USD 100
Money remainder = total % unitPrice; // USD 5.50

Money formatting

| Format | Meaning | Example (USD, en-US, 12 345.67) | |--------|----------------------------------|---------------------------------| | C | Local currency symbol (default) | $12,345.67 | | c | Compact + local symbol | $12.3K | | G | ISO currency code | USD 12,345.67 | | g | Compact + ISO code | USD 12.3K | | I | International currency symbol | US$ 12,345.67 | | i | Compact + international symbol | US$ 12.3K | | L | English currency name | 12,345.67 US dollar | | l | Compact + English currency name | 12.3K US dollar | | N | Number format (no currency) | 12,345.67 | | F | Fixed-point number (no currency) | 12345.67 | | R | Round-trip (“CODE amount”) | USD 12345.67 |

Notes:

  • G, L, N, F, R keep their existing semantics as much as possible.
  • Lowercase variants (c, g, i, l) apply compact formatting to the numeric part but keep the same identifier style as their uppercase counterpart.
Money yen = new Money(2765m, "JPY");
Money euro = new Money(2765.43m, "EUR");
Money dollar = new Money(2765.43m, "USD");
Money dinar = new Money(2765.432m, "BHD");

// Implicit when current culture is 'en-US'
yen.ToString();    // "¥2,765"
euro.ToString();   // "€2,765.43"
dollar.ToString(); // "$2,765.43"
dinar.ToString();  // "BD2,765.432"

// Implicit when current culture is 'nl-BE'
yen.ToString();    // "¥ 2.765"
euro.ToString();   // "€ 2.765,43"
dollar.ToString(); // "$ 2.765,43"
dinar.ToString();  // "BD 2.765,432"

// Implicit when current culture is 'fr-BE'
yen.ToString();    // "2.765 ¥"
euro.ToString();   // "2.765,43 €"
dollar.ToString(); // "2.765,43 $"
dinar.ToString();  // "2.765,432 BD"

// Explicit format for culture 'nl-NL'
var ci = new CultureInfo("nl-NL");

yen.ToString(ci);    // "¥ 2.765"
euro.ToString(ci);   // "€ 2.765,43"
dollar.ToString(ci); // "$ 2.765,43"
dinar.ToString(ci);  // "BD 2.765,432"

// Standard Formats when currenct culture is 'nl-NL'
dollar.ToString("C"); // "$ 2.765,43"      Currency symbol format
dollar.ToString("C0");// "$ 2.765"         Currency symbol format with precision specifier
dollar.ToString("c"); // "$ 2.8K"          Compact Currency symbol format
dollar.ToString("I"); // "US$ 2.765,43"    International Currency symbol format
dollar.ToString("i"); // "US$ 2.8K"        Compact International Currency symbol format
dollar.ToString("G"); // "USD 2.765,43"    ISO currency code format (= C but with currency code)
dollar.ToString("g"); // "USD 2.8K"        Compact ISO currency code format (international)
dollar.ToString("L"); // "2.765,43 dollar" English name format
dollar.ToString("l"); // "2.8K dollar"     Compact English name format
dollar.ToString("R"); // "USD 2,765.43"    Round-trip format
dollar.ToString("N"); // "2,765.43"        Number format (no currency)
dollar.ToString("F"); // "2765,43"         Fixed point format (no currency)

**Money p

Related Skills

View on GitHub
GitHub Stars253
CategoryDesign
Updated3d ago
Forks50

Languages

C#

Security Score

100/100

Audited on Mar 23, 2026

No findings