Skip to content

Releases

2.9.0

Choose a tag to compare

@github-actions github-actions released this 10 Oct 05:36

Changed

  • Breaking: StandardRounding and NoRounding are now sealed. IRoundingStrategy remains the extension point
    for custom rounding.
  • Money.Abs returns a value in the operand's MoneyContext instead of the current thread context.
  • Split returns shares in the source value's MoneyContext instead of the current thread context, and rounds shares to
    the context's MaxScale when set, falling back to the currency's decimal digits. Splitting EUR in a context with
    MaxScale 4 now yields shares at 4 decimals. The shares still sum to the original amount.
  • The Saudi riyal (SAR) symbol is the Unicode 17.0 sign ⃁. ر.س. and SR are alternative symbols.
  • FastMoney arithmetic that overflows, including ++ and --, throws OverflowException with the message "Value was
    either too large or too small for a FastMoney." and the arithmetic overflows as an inner exception.
  • FastMoney construction accepts the full range, -922,337,203,685,477.5808 to 922,337,203,685,477.5807. Amounts in the
    last fraction above 922,337,203,685,477 no longer throw ArgumentOutOfRangeException.
  • CurrencyInfo.GetFormat(typeof(NumberFormatInfo)) returns a shared, read-only NumberFormatInfo when the source
    culture is read-only. Mutating it throws InvalidOperationException. A mutable culture or a caller-owned
    NumberFormatInfo still gets a per-call clone.
  • Improved Money performance: construction 73% faster with no allocation, addition and subtraction 21% faster,
    ToInt32/ToInt64 about 89% faster, MoneyContext.CurrentContext over 83% faster.
  • Improved FastMoney performance: construction 45% faster, division by a non-integer decimal 68% faster, SqlMoney
    conversion over 92% faster, conversion to and from Money about 50% faster.
  • Improved formatting and parsing: formatting allocates only the result string (90% less memory) and
    TryFormat(Span<char>) is allocation-free for C, G, I, N and F; parsing is allocation-free on .NET 9 and
    later and TryParse no longer throws internally.

2.8.0

Choose a tag to compare

@github-actions github-actions released this 02 Sep 07:18

Added

  • GetCurrencyInfo() extension methods on CultureInfo and RegionInfo for easy access to CurrencyInfo
    #32.
  • Context is now settable in an object initializer or with expression on Money and FastMoney, so an existing
    value can be moved to another MoneyContext #120. The stored amount
    is kept as-is and is not re-rounded, so money with { Context = noRounding } reinterprets the value instead of
    recalculating it. FastMoney validates the new context and rejects a MaxScale above 4 or a Precision above 19.
  • MoneyJsonConverter has a constructor taking a MoneyContext. Register such an instance in
    JsonSerializerOptions.Converters to control the context that deserialized values get. Create the context once and
    reuse it, because creating one scans the registered contexts for an equivalent set of options.

Changed

  • Breaking: Deserializing Money now uses the current MoneyContext instead of a no-rounding context, so a
    deserialized value behaves like a constructed one in arithmetic #119.
    Amounts with more decimals than the currency allows are now rounded while reading. To keep the exact serialized amount,
    deserialize inside a scope with the NoRounding strategy, or register a MoneyJsonConverter configured with such a
    context in JsonSerializerOptions.Converters. Only System.Text.Json has that per-serializer opt-out; for
    Newtonsoft.Json, XML, DataContractSerializer and BinaryFormatter a scope is the only escape, so stored amounts with
    more decimals than the current context allows now change value when read (EUR 0.005 reads back as EUR 0.00).
  • FastMoney now validates the currency when it is set in an object initializer or with expression, throwing
    InvalidCurrencyException for a currency needing more than 4 decimal places. Previously only the constructor checked.
  • MoneyJsonConverter no longer claims Money? itself and lets System.Text.Json handle it through its own nullable
    converter. A JSON null read directly as Money still throws JsonException.

2.7.0

Choose a tag to compare

@RemyDuijkeren RemyDuijkeren released this 21 Feb 23:23

Added

  • FromMinorUnits and ToMinorUnits methods on Money and FastMoney #118.

Changed

  • Improved performance for Money creation, arithmetic, and formatting (up to 30%) by encoding MinorUnit = 2 hint
    directly into the Currency struct's 15th bit for faster rounding and lookups.
  • IRoundingStrategy.Round() uses Currency as a parameter instead of CurrencyInfo.
  • Fixed IntroductionOn date for SLE currency #117 by @General-66.
  • Compact format use given digits as significant digits where the default is 2,
    values below 1000 are displayed as normal currency values where > 100 without decimals #111,

Removed

  • Currency.IsIso4217 is removed, use CurrencyInfo.IsIso4217 instead.

Full Changelog: 2.6.0...2.7.0

2.6.0

Choose a tag to compare

@RemyDuijkeren RemyDuijkeren released this 17 Nov 02:00

Added

  • Support .NET 10.0
  • Lower-case formats, like 'c/g/i' will do compact notation, like USD 1.2K, €3.4M (#111, #113)
  • Formatting format 'I' will use the international currency symbol (US$), where 'C' will use the local currency code ($).

Changed

  • ISO 4217 AMENDMENT NUMBER 180, Bulgaria will use EUR from 1 January 2026, and BGN will move to the historic list.

Full Changelog: 2.5.0...2.6.0

2.5.0

Choose a tag to compare

@RemyDuijkeren RemyDuijkeren released this 24 Sep 06:25

This is a big update where MoneyContext is added. Every created Money object now has the context in which it was created, and this MoneyContext can be configured to your specific context, like rounding, scales, etc. This would solve the issue that some users didn't want to round Money automatically.

But before using the MoneyContext, maybe the new FastMoney type is enough for your use case. FastMoney is a smaller type, based on long, with a fixed scale of four decimals.

And the final big change is the internal size of Money: Money is now the same size as a Decimal, a 16 bytes struct!

See the README for more info.

Added

  • Add MoneyContext to configure money behavior like rounding, scale and precision. This can be specified globally,
    per thread or by money instance. This should solve much of the discussion in issue #27 about internal rounding.
  • Add extra constructors on Money to create with a given MoneyContext, instead of MidpointRounding.
  • Add MoneyContext property on Money.
  • Add Scale property on Money.
  • Add NodaMoney.DependencyInjection package to register MoneyContext .NET DI.
  • Add the FastMoney struct, based on long, for a smaller footprint and faster operations with a default MoneyContext of { Precision = 19, MaxScale = 4, RoundingStrategy = new StandardRounding(MidpointRounding.ToEven) }.

Changed

  • Optimized Money struct size. This was 18 bytes (padded 24 bytes), but is now 16 bytes (padded 16 bytes) (33% less).
    This means Money is the same size as Decimal struct!!!
  • Improved Parsing: memory allocation (75% less) and performance (2x)
  • Improved Increment(++) and Decrement(--): zero memory allocation (was 32 B) and performance (3x)

Removed

  • IConvertible implementation for Money. Methods that don't make sense to convert from and to Money are removed, like ToBoolean, ToDateTime, etc.
  • Removed From-methods and numeric casts on Money where constructors exist or are more appropriate, like FromDecimal, FromInt32 FromInt64, etc.
  • Remove Money constructors and factory methods with MidpointRounding param for non-decimal numeric types, like new Money(double, Currency, MidpointRounding).

Full Changelog: 2.3.0...2.5.0

2.3.0

Choose a tag to compare

@RemyDuijkeren RemyDuijkeren released this 19 Aug 05:46

Added

  • CurrencyInfo.TryFromCode method to get CurrencyInfo from code without throwing an exception.

Full Changelog: 2.2.1...2.3.0

2.2.1

Choose a tag to compare

@RemyDuijkeren RemyDuijkeren released this 17 Jun 23:52

Changed

  • Fixed #107 When subtracting from $0, the result is negated

Full Changelog: 2.2.0...2.2.1

2.2.0

Choose a tag to compare

@RemyDuijkeren RemyDuijkeren released this 08 May 02:46

Added

  • ISO 4217 AMENDMENT NUMBER 179, add new currency XAD for the Finance Department Arab Monetary Fund (AMF)

Changed

  • Allow ExchangeRate to have the same currency as both base and quote by @gliljas in #103
  • No rounding for Currencies where MinorUnit is NotApplicable, like Currency(Info).NoCurrency (Breaking Change).
  • Updated System.Text.Json dependency to allow versions from 4.7.2 and up for better compatibility and flexibility.

Removed

  • Removed NumberStyle param for Parse- and TryParse-methods (Breaking Change). Money expects to parse a Currency number. Pre-parse with Decimal.Parse() if more fine-grained control is needed.
  • CurrencyInfo.MinorUnitAsExponentOfBase10 changed to internal (Breaking Change)

Full Changelog: 2.1.1...2.2.0

2.1.1

Choose a tag to compare

@RemyDuijkeren RemyDuijkeren released this 19 Mar 06:13

Added

  • Allow creating Money using 'with' expression
  • Add format N, Number format (e.g., "2,765.43")
  • Add format F, Fixed point format (e.g., "2765,43")
  • Add compatibility with Native AOT and Trimming

Changed

  • Aligned the expiration date of the Netherlands Antillean guilder (ANG) with the amended timeline (ISO 4217 AMENDMENT NUMBER 176.)
  • More defensive parsing in JSON converters by @gliljas in #102
  • Migrate from SLN to SLNX solution file format

Removed

  • Removed Microsoft.Bcl.HashCode dependency for .NET Standard 2.0

Full Changelog: 2.1.0...2.1.1

2.1.0

Choose a tag to compare

@RemyDuijkeren RemyDuijkeren released this 02 Mar 11:28

Added

  • ISpanFormattable and IUtf8SpanFormattable implemented
  • Partially implemented INumber by adding Radix, Zero, One, NegativeOne,
    Abs(), IsNegative(), IsPositive(), IsZero(), MinMagnitude(), MaxMagnitude()

Changed

  • Rename SafeDivide to Split and move in NodaMoney namespace (should have been in v2.0.0)
  • NotNullWhenAttribute is now internal by @gliljas in #101

Full Changelog: 2.0.0...2.1.0