Skip to content

Commit 395e705

Browse files
fix: parseNumber with custom format
1 parent c9adfbb commit 395e705

File tree

4 files changed

+95
-53
lines changed

4 files changed

+95
-53
lines changed

src/numbers/custom-number-format.js

Lines changed: 1 addition & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,16 @@
11
import { CURRENCY, PERCENT, LIST_SEPARATOR, GROUP_SEPARATOR, CURRENCY_PLACEHOLDER, PERCENT_PLACEHOLDER, POINT, EMPTY } from '../common/constants';
22
import isNegativeZero from '../common/is-negative-zero';
3-
import formatCurrencySymbol from './format-currency-symbol';
43
import groupInteger from './group-integer';
54
import round from '../common/round';
6-
7-
const PLACEHOLDER = "__??__";
5+
import { setStyleOptions, setFormatLiterals, replaceLiterals } from './utils';
86

97
const SHARP = "#";
108
const ZERO = "0";
119

12-
const literalRegExp = /(\\.)|(['][^']*[']?)|(["][^"]*["]?)/g;
1310
const trailingZerosRegExp = /(\.(?:[0-9]*[1-9])?)0+$/g;
1411
const trailingPointRegExp = /\.$/;
1512
const commaRegExp = /\,/g;
1613

17-
function setFormatLiterals(formatOptions) {
18-
let format = formatOptions.format;
19-
if (format.indexOf("'") > -1 || format.indexOf("\"") > -1 || format.indexOf("\\") > -1) {
20-
const literals = formatOptions.literals = [];
21-
formatOptions.format = format.replace(literalRegExp, function(match) {
22-
const quoteChar = match.charAt(0).replace("\\", EMPTY);
23-
const literal = match.slice(1).replace(quoteChar, EMPTY);
24-
25-
literals.push(literal);
26-
27-
return PLACEHOLDER;
28-
});
29-
}
30-
}
31-
3214
function trimTrailingZeros(value, lastZero) {
3315
let trimRegex;
3416

@@ -119,22 +101,6 @@ function setValueSpecificFormat(formatOptions) {
119101
formatOptions.format = format;
120102
}
121103

122-
function setStyleOptions(formatOptions, info) {
123-
const format = formatOptions.format;
124-
125-
//multiply number if the format has percent
126-
if (format.indexOf(PERCENT_PLACEHOLDER) !== -1) {
127-
formatOptions.style = PERCENT;
128-
formatOptions.symbol = info.numbers.symbols.percentSign;
129-
formatOptions.number *= 100;
130-
}
131-
132-
if (format.indexOf(CURRENCY_PLACEHOLDER) !== -1) {
133-
formatOptions.style = CURRENCY;
134-
formatOptions.symbol = formatCurrencySymbol(info);
135-
}
136-
}
137-
138104
function setGroupOptions(formatOptions) {
139105
formatOptions.hasGroup = formatOptions.format.indexOf(GROUP_SEPARATOR) > -1;
140106
if (formatOptions.hasGroup) {
@@ -187,17 +153,6 @@ function replaceStyleSymbols(number, style, symbol) {
187153
return result;
188154
}
189155

190-
function replaceLiterals(number, literals) {
191-
let result = number;
192-
if (literals) {
193-
const length = literals.length;
194-
for (let idx = 0; idx < length; idx++) {
195-
result = result.replace(PLACEHOLDER, literals[idx]);
196-
}
197-
}
198-
return result;
199-
}
200-
201156
function replacePlaceHolders(formatOptions, info) {
202157
const { start, end, negative, negativeZero, format, decimalIndex, lastZeroIndex, hasNegativeFormat, hasGroup } = formatOptions;
203158
let number = formatOptions.number;

src/numbers/parse-number.js

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { localeInfo, localeCurrency, currencyDisplays } from '../cldr';
22
import { PERCENT, NUMBER_PLACEHOLDER, CURRENCY_PLACEHOLDER, DEFAULT_LOCALE, EMPTY, POINT } from '../common/constants';
3+
import { setStyleOptions, setFormatLiterals } from './utils';
34
import isNumber from '../common/is-number';
45
import isCurrencyStyle from './is-currency-style';
56
import formatOptions from './format-options';
7+
import isString from '../common/is-string';
68

79
const exponentRegExp = /[eE][\-+]?[0-9]+/;
810
const nonBreakingSpaceRegExp = /\u00A0/g;
@@ -55,6 +57,19 @@ function cleanCurrencyNumber(value, info, format) {
5557
};
5658
}
5759

60+
function cleanLiterals(number, formatOptions) {
61+
const literals = formatOptions.literals;
62+
let result = number;
63+
64+
if (literals) {
65+
for (let idx = 0; idx < literals.length; idx++) {
66+
result = result.replace(literals[idx], EMPTY);
67+
}
68+
}
69+
70+
return result;
71+
}
72+
5873
export default function parseNumber(value, locale = DEFAULT_LOCALE, format = {}) {
5974
if (!value && value !== 0) {
6075
return null;
@@ -68,29 +83,39 @@ export default function parseNumber(value, locale = DEFAULT_LOCALE, format = {})
6883
const symbols = info.numbers.symbols;
6984

7085
let number = value.toString();
86+
let formatOptions = format || {};
7187
let isPercent;
7288

89+
if (isString(format)) {
90+
formatOptions = { format: format };
91+
setFormatLiterals(formatOptions);
92+
number = cleanLiterals(number, formatOptions);
93+
94+
setStyleOptions(formatOptions, info);
95+
}
96+
97+
if (formatOptions.style === PERCENT || number.indexOf(symbols.percentSign) > -1) {
98+
number = number.replace(symbols.percentSign, EMPTY);
99+
isPercent = true;
100+
}
101+
73102
if (exponentRegExp.test(number)) {
74103
number = parseFloat(number.replace(symbols.decimal, POINT));
75104
return isNaN(number) ? null : number;
76105
}
77106

107+
const { negative: negativeCurrency, number: currencyNumber } = cleanCurrencyNumber(number, info, formatOptions);
108+
number = String(currencyNumber).trim();
109+
78110
const negativeSignIndex = number.indexOf("-");
79111
if (negativeSignIndex > 0) {
80112
return null;
81113
}
82114

83115
let isNegative = negativeSignIndex > -1;
84-
const { negative: negativeCurrency, number: newNumber } = cleanCurrencyNumber(number, info, format);
85116

86-
number = newNumber;
87117
isNegative = negativeCurrency !== undefined ? negativeCurrency : isNegative;
88118

89-
if (format.style === PERCENT || number.indexOf(symbols.percentSign) > -1) {
90-
number = number.replace(symbols.percentSign, EMPTY);
91-
isPercent = true;
92-
}
93-
94119
number = number.replace("-", EMPTY)
95120
.replace(nonBreakingSpaceRegExp, " ")
96121
.split(symbols.group.replace(nonBreakingSpaceRegExp, " ")).join(EMPTY)

src/numbers/utils.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { PERCENT_PLACEHOLDER, CURRENCY_PLACEHOLDER, CURRENCY, PERCENT, EMPTY } from '../common/constants';
2+
import formatCurrencySymbol from './format-currency-symbol';
3+
4+
const literalRegExp = /(\\.)|(['][^']*[']?)|(["][^"]*["]?)/g;
5+
const PLACEHOLDER = "__??__";
6+
7+
export function setStyleOptions(formatOptions, info) {
8+
const format = formatOptions.format;
9+
10+
//multiply number if the format has percent
11+
if (format.indexOf(PERCENT_PLACEHOLDER) !== -1) {
12+
formatOptions.style = PERCENT;
13+
formatOptions.symbol = info.numbers.symbols.percentSign;
14+
formatOptions.number *= 100;
15+
}
16+
17+
if (format.indexOf(CURRENCY_PLACEHOLDER) !== -1) {
18+
formatOptions.style = CURRENCY;
19+
formatOptions.symbol = formatCurrencySymbol(info);
20+
}
21+
}
22+
23+
export function setFormatLiterals(formatOptions) {
24+
let format = formatOptions.format;
25+
if (format.indexOf("'") > -1 || format.indexOf("\"") > -1 || format.indexOf("\\") > -1) {
26+
const literals = formatOptions.literals = [];
27+
formatOptions.format = format.replace(literalRegExp, function(match) {
28+
const quoteChar = match.charAt(0).replace("\\", EMPTY);
29+
const literal = match.slice(1).replace(quoteChar, EMPTY);
30+
31+
literals.push(literal);
32+
33+
return PLACEHOLDER;
34+
});
35+
}
36+
}
37+
38+
export function replaceLiterals(number, literals) {
39+
let result = number;
40+
if (literals) {
41+
const length = literals.length;
42+
for (let idx = 0; idx < length; idx++) {
43+
result = result.replace(PLACEHOLDER, literals[idx]);
44+
}
45+
}
46+
return result;
47+
}

test/numbers.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,21 @@ describe('parseNumber', () => {
736736
expect(parseNumber("-1 123 112,13 лв.", "bg")).toEqual(-1123112.13);
737737
});
738738

739+
it("parses currency with custom format", () => {
740+
expect(parseNumber("$12", 'en', '$#.#')).toEqual(12);
741+
expect(parseNumber("-$12", 'en', '$#.#')).toEqual(-12);
742+
});
743+
744+
it("parses percent with custom format", () => {
745+
expect(parseNumber("% 12", 'en', '% #.#')).toEqual(0.12);
746+
expect(parseNumber("% -12", 'en', '% #.#')).toEqual(-0.12);
747+
});
748+
749+
it("parses number with custom format literals", () => {
750+
expect(parseNumber("foo12", 'en', '"foo"#.#')).toEqual(12);
751+
expect(parseNumber("T12", 'en', '\\T#.#')).toEqual(12);
752+
});
753+
739754
it("parses currency numbers with negative format", () => {
740755
loadCustom({ currencyPattern: "¤#,##0.00;(¤#,##0.00)", currencies: { USD: { symbol: "$" }}});
741756
expect(parseNumber("($1,123,112.13)", "custom", { style: "currency", currency: "USD" })).toEqual(-1123112.13);

0 commit comments

Comments
 (0)