n2words
Version:
Convert numbers to words in 70+ languages with zero dependencies. Supports BigInt, decimals, and browser/Node.js environments.
90 lines (75 loc) • 2.69 kB
JavaScript
/**
* Currency value parsing utility.
* Optimized parser for currency conversion - extracts dollars and cents.
* @module parse-currency
*/
import { expandScientificNotation, hasScientificNotation, numberToString } from './expand-scientific.js'
/**
* Parses a value for currency conversion.
* Returns dollars and cents as separate bigints, plus negative flag.
* @param {number|string|bigint} value - The value to parse
* @returns {{isNegative: boolean, dollars: bigint, cents: bigint}} The parsed dollars and cents with a negative flag.
* @throws {TypeError} If value is not number, string, or bigint
* @throws {RangeError} If value is not finite
*/
export function parseCurrencyValue(value) {
const type = typeof value
// BigInt: whole dollars only
if (typeof value === 'bigint') {
return value < 0n
? { isNegative: true, dollars: -value, cents: 0n }
: { isNegative: false, dollars: value, cents: 0n }
}
// Number: fast path for safe integers
if (typeof value === 'number') {
if (!Number.isFinite(value)) {
throw new RangeError('Currency must be a finite number')
}
if (Number.isSafeInteger(value)) {
return value < 0
? { isNegative: true, dollars: BigInt(-value), cents: 0n }
: { isNegative: false, dollars: BigInt(value), cents: 0n }
}
// Non-integer or unsafe: convert to string
return parseCurrencyString(numberToString(value))
}
// String input
if (typeof value === 'string') {
return parseCurrencyString(value)
}
throw new TypeError(
`Invalid value type: expected number, string, or bigint, received ${type}`,
)
}
/**
* Parses a string for currency conversion.
* @param {string} value - The string to parse
* @returns {{isNegative: boolean, dollars: bigint, cents: bigint}} The parsed dollars and cents with a negative flag.
*/
function parseCurrencyString(value) {
let str = value.trim()
if (str.length === 0 || Number.isNaN(Number(str))) {
throw new RangeError(`Invalid currency format: "${value}"`)
}
// Expand scientific notation
if (hasScientificNotation(str)) {
str = expandScientificNotation(str)
}
// Handle negative
const isNegative = str[0] === '-'
if (isNegative) str = str.slice(1)
// Split on decimal
const dotIndex = str.indexOf('.')
if (dotIndex === -1) {
return { isNegative, dollars: BigInt(str), cents: 0n }
}
const dollarStr = str.slice(0, dotIndex) || '0'
const decimalPart = str.slice(dotIndex + 1)
// Truncate to 2 decimal places and pad if needed
const centStr = (decimalPart + '00').slice(0, 2)
return {
isNegative,
dollars: BigInt(dollarStr),
cents: BigInt(centStr),
}
}