Skip to content

Commit 58a0fe4

Browse files
fix: parse accounting numbers with negative format
1 parent 28a31e2 commit 58a0fe4

File tree

4 files changed

+78
-56
lines changed

4 files changed

+78
-56
lines changed

src/numbers/format-number.js

Lines changed: 6 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,8 @@
11
import { localeInfo } from '../cldr';
2-
import { CURRENCY, ACCOUNTING, DECIMAL, PERCENT, SCIENTIFIC, DEFAULT_LOCALE, NUMBER_PLACEHOLDER, EMPTY } from '../common/constants';
3-
import isString from '../common/is-string';
2+
import { DECIMAL, DEFAULT_LOCALE, NUMBER_PLACEHOLDER, EMPTY } from '../common/constants';
43
import standardNumberFormat from './standard-number-format';
54
import customNumberFormat from './custom-number-format';
6-
7-
const standardFormatRegExp = /^(n|c|p|e|a)(\d*)$/i;
8-
9-
function standardFormatOptions(format) {
10-
const formatAndPrecision = standardFormatRegExp.exec(format);
11-
12-
if (formatAndPrecision) {
13-
const options = {
14-
style: DECIMAL
15-
};
16-
17-
let style = formatAndPrecision[1].toLowerCase();
18-
19-
if (style === "c") {
20-
options.style = CURRENCY;
21-
} else if (style === "a") {
22-
options.style = ACCOUNTING;
23-
} else if (style === "p") {
24-
options.style = PERCENT;
25-
} else if (style === "e") {
26-
options.style = SCIENTIFIC;
27-
}
28-
29-
if (formatAndPrecision[2]) {
30-
options.minimumFractionDigits = options.maximumFractionDigits = parseInt(formatAndPrecision[2], 10);
31-
}
32-
33-
return options;
34-
}
35-
}
36-
37-
function getFormatOptions(format) {
38-
let formatOptions;
39-
if (isString(format)) {
40-
formatOptions = standardFormatOptions(format);
41-
} else {
42-
formatOptions = format;
43-
}
44-
45-
return formatOptions;
46-
}
5+
import formatOptions from './format-options';
476

487
export default function formatNumber(number, format = NUMBER_PLACEHOLDER, locale = DEFAULT_LOCALE) {
498
if (number === undefined || number === null) {
@@ -55,12 +14,12 @@ export default function formatNumber(number, format = NUMBER_PLACEHOLDER, locale
5514
}
5615

5716
const info = localeInfo(locale);
58-
const formatOptions = getFormatOptions(format);
17+
const options = formatOptions(format);
5918

6019
let result;
61-
if (formatOptions) {
62-
const style = (formatOptions || {}).style || DECIMAL;
63-
result = standardNumberFormat(number, Object.assign({}, info.numbers[style], formatOptions), info);
20+
if (options) {
21+
const style = (options || {}).style || DECIMAL;
22+
result = standardNumberFormat(number, Object.assign({}, info.numbers[style], options), info);
6423
} else {
6524
result = customNumberFormat(number, format, info);
6625
}

src/numbers/format-options.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { CURRENCY, ACCOUNTING, DECIMAL, PERCENT, SCIENTIFIC } from '../common/constants';
2+
import isString from '../common/is-string';
3+
4+
const standardFormatRegExp = /^(n|c|p|e|a)(\d*)$/i;
5+
6+
function standardFormatOptions(format) {
7+
const formatAndPrecision = standardFormatRegExp.exec(format);
8+
9+
if (formatAndPrecision) {
10+
const options = {
11+
style: DECIMAL
12+
};
13+
14+
let style = formatAndPrecision[1].toLowerCase();
15+
16+
if (style === "c") {
17+
options.style = CURRENCY;
18+
} else if (style === "a") {
19+
options.style = ACCOUNTING;
20+
} else if (style === "p") {
21+
options.style = PERCENT;
22+
} else if (style === "e") {
23+
options.style = SCIENTIFIC;
24+
}
25+
26+
if (formatAndPrecision[2]) {
27+
options.minimumFractionDigits = options.maximumFractionDigits = parseInt(formatAndPrecision[2], 10);
28+
}
29+
30+
return options;
31+
}
32+
}
33+
34+
export default function formatOptions(format) {
35+
let options;
36+
if (isString(format)) {
37+
options = standardFormatOptions(format);
38+
} else {
39+
options = format;
40+
}
41+
42+
return options;
43+
}

src/numbers/parse-number.js

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,27 @@ import { localeInfo, localeCurrency, currencyDisplays } from '../cldr';
22
import { PERCENT, NUMBER_PLACEHOLDER, CURRENCY_PLACEHOLDER, DEFAULT_LOCALE, EMPTY, POINT } from '../common/constants';
33
import isNumber from '../common/is-number';
44
import isCurrencyStyle from './is-currency-style';
5+
import formatOptions from './format-options';
56

67
const exponentRegExp = /[eE][\-+]?[0-9]+/;
78
const nonBreakingSpaceRegExp = /\u00A0/g;
89

10+
function cleanNegativePattern(number, patterns) {
11+
if (patterns.length > 1) {
12+
const parts = (patterns[1] || EMPTY).replace(CURRENCY_PLACEHOLDER, EMPTY).split(NUMBER_PLACEHOLDER);
13+
if (number.indexOf(parts[0]) > -1 && number.indexOf(parts[1]) > -1) {
14+
return number.replace(parts[0], EMPTY).replace(parts[1], EMPTY);
15+
}
16+
}
17+
}
18+
919
function cleanCurrencyNumber(value, info, format) {
10-
let isCurrency = isCurrencyStyle(format.style);
20+
const options = formatOptions(format) || {};
21+
let isCurrency = isCurrencyStyle(options.style);
1122
let number = value;
1223
let negative;
1324

14-
const currency = format.currency || localeCurrency(info, isCurrency);
25+
const currency = options.currency || localeCurrency(info, isCurrency);
1526

1627
if (currency) {
1728
const displays = currencyDisplays(info, currency, isCurrency);
@@ -27,14 +38,14 @@ function cleanCurrencyNumber(value, info, format) {
2738
}
2839

2940
if (isCurrency) {
30-
const patterns = info.numbers.currency.patterns;
31-
if (patterns.length > 1) {
32-
const parts = (patterns[1] || EMPTY).replace(CURRENCY_PLACEHOLDER, EMPTY).split(NUMBER_PLACEHOLDER);
33-
if (number.indexOf(parts[0]) > -1 && number.indexOf(parts[1]) > -1) {
34-
number = number.replace(parts[0], EMPTY).replace(parts[1], EMPTY);
35-
negative = true;
36-
}
41+
const cleanNumber = cleanNegativePattern(number, info.numbers.currency.patterns) ||
42+
cleanNegativePattern(number, info.numbers.accounting.patterns);
43+
44+
if (cleanNumber) {
45+
negative = true;
46+
number = cleanNumber;
3747
}
48+
3849
}
3950
}
4051

test/numbers.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,15 @@ describe('parseNumber', () => {
674674
expect(parseNumber("€12", "en", { currency: "EUR"})).toEqual(12);
675675
});
676676

677+
it("parses accounting numbers", () => {
678+
expect(parseNumber("$12", 'en', 'a')).toEqual(12);
679+
expect(parseNumber("$12", 'en', { style: 'accounting' })).toEqual(12);
680+
});
681+
682+
it("parses accounting numbers with negative pattern", () => {
683+
expect(parseNumber("($12)", 'en', 'a')).toEqual(-12);
684+
});
685+
677686
it("parses percent numbers", () => {
678687
expect(parseNumber("% 12")).toEqual(0.12);
679688
});

0 commit comments

Comments
 (0)