Skip to content

Latest commit

 

History

History
186 lines (135 loc) · 7.09 KB

File metadata and controls

186 lines (135 loc) · 7.09 KB

⚠️ Important Warnings

This document details important limitations and considerations you should be aware of when using the Currency.js library.

Table of Contents

1. Two Decimal Places Limitation

The Currency.js library has been optimized to work with monetary values containing up to two decimal places. Using the library with values that have more than two decimal places may result in inaccuracies.

// ✅ Recommended: values with up to 2 decimal places
const price1 = Money(19.99);
const price2 = Money(100.5);

// ⚠️ Not recommended: values with more than 2 decimal places
const price3 = Money(19.999);  // Will be rounded to 20.00
const price4 = Money(100.505); // Will be rounded to 100.51

2. Internal Rounding Behavior

The library performs internal rounding to preserve the precision of operations. It's important to be aware of these rounding strategies when performing calculations in sequence.

// Example of internal rounding
const price = Money(10.565); // Internally rounded to 10.57

// Rounding in mathematical operations
const resultA = Money(10.51).times(0.33);  // 3.47 (not 3.4683)
const resultB = Money(99.99).dividedBy(3); // 33.33 (not 33.33...)

// To visualize the rounding behavior
const value = Money(10.516);
console.log(value.value); // 10.52 (rounded)
console.log(value.cents); // 1052 (value in cents after rounding)

3. Preference for the allocate Method

To distribute values with greater precision (such as in payment installments), always prefer to use the allocate method instead of dividedBy. The allocate method ensures that the sum of the parts is exactly equal to the original value, avoiding rounding problems.

// ❌ Less precise: using dividedBy for installments
const totalAmount = Money(100);
const installments = 3;
const eachInstallment = totalAmount.dividedBy(installments); // 33.33
const totalSum = eachInstallment.times(installments);        // 99.99 (loss of 0.01)

// ✅ More precise: using allocate for installments
const totalAmount = Money(100);
const installments = totalAmount.allocate(3); // [33.34, 33.33, 33.33]
console.log(installments[0].format()); // $33.34
console.log(installments[1].format()); // $33.33
console.log(installments[2].format()); // $33.33

// The sum of the installments is exactly equal to the original value
const totalSum = installments.reduce(
  (sum, amount) => sum.plus(amount),
  Money(0)
); // 100.00

4. Chained Operations Processing

When chaining multiple operations, be aware that each operation creates a new Money instance and that rounding is applied at each step:

// Example of chained operations with intermediate rounding
const result = Money(33.33)
  .times(1.05)    // 34.9965 -> rounded to 35.00
  .minus(0.5)     // 34.50
  .dividedBy(3);  // 11.50

// For critical operations requiring absolute precision,
// consider reducing the number of chained operations

5. Compatibility with Different Locales

When working with different regional settings (locales), make sure to provide both the correct currency code and locale to ensure proper formatting:

// Incorrect combinations may lead to inaccurate formatting
const amount = Money(1234.56);

// ✅ Correct: matching currency and locale
amount.format({ currencyCode: 'BRL', locale: 'pt-BR' }); // R$ 1.234,56

// ⚠️ Be careful: different currency and locale
amount.format({ currencyCode: 'USD', locale: 'pt-BR' }); // US$ 1.234,56

6. Float Precision Issues

JavaScript's floating-point limitations remain a concern even with dedicated money handling libraries. Currency.js mitigates these issues by storing values as integers (cents), but be aware of potential precision problems with very large numbers:

// Be careful with very large monetary values
const veryLargeAmount = Money(9007199254740991); // Maximum safe integer in JavaScript
const doubledAmount = veryLargeAmount.times(2);  // May cause precision issues

// For extremely large values, consider alternative solutions or use BigInt-based libraries

7. Immutability Side Effects

While immutability is beneficial for preventing unintended modifications, it can lead to increased memory usage when performing many operations in succession:

// Each operation creates a new Money instance
let result = Money(100);

// This creates 1000 different Money instances
for (let i = 0; i < 1000; i++) {
  result = result.plus(0.01);
}

// For intensive calculations with many iterations, consider using
// the Calculator API instead of chaining many operations

8. Currency Symbol Inconsistencies

Currency symbols may be displayed differently depending on the user's browser and operating system:

// The same code might produce different displays on different systems
const price = Money(99.99, { currencyCode: 'USD', locale: 'en-US' });

// On some systems: $99.99
// On some systems: US$99.99
// On some systems: 99.99 USD

// For critical display requirements, always test on target platforms

9. Time Zone Considerations

When working with date-based financial calculations (like interest or prorated billing), be aware of time zone differences:

// Calculation that might be affected by time zones
const subscriptionStartDate = new Date('2025-05-15T00:00:00Z');
const today = new Date();

// Days calculation might vary depending on the user's time zone
const daysSinceStart = Math.floor((today - subscriptionStartDate) / (1000 * 60 * 60 * 24));

const dailyRate = Money(30).dividedBy(30);
const proRatedAmount = dailyRate.times(daysSinceStart);

// Always normalize dates to a specific time zone for financial calculations

10. Internationalization Testing

Always test your application with various locale settings to ensure proper formatting:

// Test with multiple locales to ensure proper formatting
const amount = Money(1234.56);

// Test various formats and check for unexpected behaviors
const formats = [
  amount.format({ currencyCode: 'USD', locale: 'en-US' }),
  amount.format({ currencyCode: 'EUR', locale: 'de-DE' }),
  amount.format({ currencyCode: 'JPY', locale: 'ja-JP' }),
  amount.format({ currencyCode: 'BRL', locale: 'pt-BR' }),
  amount.format({ currencyCode: 'INR', locale: 'en-IN' }),
  amount.format({ currencyCode: 'AED', locale: 'ar-AE' }), // Right-to-left language
];

// Verify that all formats display correctly in your application