Skip to content

Commit

Permalink
fix(toDecimal): preserve negative sign for leading zeros (#693)
Browse files Browse the repository at this point in the history
Change #690 fixed the general case of formatting negative unit values,
but was one remaining case where formatting was still incorrect: when
the first unit is -0, the resulting string didn't include the leading
negative sign.

This change identifies that exact case (negative value, leading zero)
and pads the resulting string with a leading negative sign.

Fixes #692

Co-authored-by: Sarah Dayan <5370675+sarahdayan@users.noreply.github.com>
  • Loading branch information
jparise and sarahdayan committed Dec 19, 2022
1 parent 06e7ff2 commit e6f290d
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 17 deletions.
33 changes: 16 additions & 17 deletions packages/core/src/api/toDecimal.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NON_DECIMAL_CURRENCY_MESSAGE } from '../checks';
import { assert } from '../helpers';
import type { Calculator, Dinero, Formatter, Transformer } from '../types';
import { absolute, computeBase, equal, isArray } from '../utils';
import { absolute, computeBase, equal, isArray, lessThan } from '../utils';

import { toUnits } from './toUnits';

Expand Down Expand Up @@ -49,23 +49,22 @@ function getDecimal<TAmount>(
formatter: Formatter<TAmount>
) {
const absoluteFn = absolute(calculator);
const equalFn = equal(calculator);
const lessThanFn = lessThan(calculator);
const zero = calculator.zero();

return (units: readonly TAmount[], scale: TAmount) => {
return units
.map((unit, index) => {
const isFirst = index === 0;
const isLast = units.length - 1 === index;

const unitAsString = formatter.toString(
isFirst ? unit : absoluteFn(unit)
);

if (isLast) {
return unitAsString.padStart(formatter.toNumber(scale), '0');
}

return unitAsString;
})
.join('.');
const whole = formatter.toString(units[0]);
const fractional = formatter.toString(absoluteFn(units[1]));

const scaleNumber = formatter.toNumber(scale);
const decimal = `${whole}.${fractional.padStart(scaleNumber, '0')}`;

const leadsWithZero = equalFn(units[0], zero);
const isNegative = lessThanFn(units[1], zero);

// A leading negative zero is a special case because the `toString`
// formatter won't preserve its negative sign (since 0 === -0).
return leadsWithZero && isNegative ? `-${decimal}` : decimal;
};
}
30 changes: 30 additions & 0 deletions packages/dinero.js/src/api/__tests__/toDecimal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ describe('toDecimal', () => {

expect(toDecimal(d)).toEqual('-10.50');
});
it('returns the negative amount with a leading zero in decimal format', () => {
const d = dinero({ amount: -1, currency: USD });

expect(toDecimal(d)).toEqual('-0.01');
});
it('returns negative zero amount as a positive value in decimal format', () => {
const d = dinero({ amount: -0, currency: USD });

expect(toDecimal(d)).toEqual('0.00');
});
it('uses a custom transformer', () => {
const d = dinero({ amount: 1050, currency: USD });

Expand Down Expand Up @@ -118,6 +128,16 @@ describe('toDecimal', () => {

expect(toDecimal(d)).toEqual('-10.50');
});
it('returns the negative amount with a leading zero in decimal format', () => {
const d = dinero({ amount: -1n, currency: bigintUSD });

expect(toDecimal(d)).toEqual('-0.01');
});
it('returns negative zero amount as a positive value in decimal format', () => {
const d = dinero({ amount: -0n, currency: bigintUSD });

expect(toDecimal(d)).toEqual('0.00');
});
it('uses a custom transformer', () => {
const d = dinero({ amount: 1050n, currency: bigintUSD });

Expand Down Expand Up @@ -198,6 +218,16 @@ describe('toDecimal', () => {

expect(toDecimal(d)).toEqual('-10.05');
});
it('returns the negative amount with a leading zero in decimal format', () => {
const d = dinero({ amount: new Big(-1), currency: bigjsUSD });

expect(toDecimal(d)).toEqual('-0.01');
});
it('returns negative zero amount as a positive value in decimal format', () => {
const d = dinero({ amount: new Big(-0), currency: bigjsUSD });

expect(toDecimal(d)).toEqual('0.00');
});
it('uses a custom transformer', () => {
const d = dinero({ amount: new Big(1050), currency: bigjsUSD });

Expand Down

1 comment on commit e6f290d

@vercel
Copy link

@vercel vercel bot commented on e6f290d Dec 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

dinerojs – ./

v2.dinerojs.com
dinerojs-git-main-dinerojs.vercel.app
dinerojs-dinerojs.vercel.app

Please sign in to comment.