diff --git a/.eslintrc b/.eslintrc index 9a87470..9144e6d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -13,6 +13,7 @@ "newline-per-chained-call": "off" }, "globals": { + "BigInt": true, "console": true, "Set": true, "require": true, diff --git a/lib/formatNumber.js b/lib/formatNumber.js index ec8733e..03b0c52 100644 --- a/lib/formatNumber.js +++ b/lib/formatNumber.js @@ -38,7 +38,7 @@ export function formatColor (value, parseData, opts) { const parts = parseData.partitions; let part = parts[3]; let color = null; - if (typeof value === 'number' && isFinite(value)) { + if ((typeof value === 'number' || typeof value === 'bigint') && isFinite(value)) { part = getPart(value, parts); } if (part && part.color) { @@ -62,11 +62,12 @@ export function formatValue (value, parseData, opts) { if (value == null) { return ''; } - if (typeof value !== 'number') { + const n = typeof value === 'bigint'; + if (typeof value !== 'number' && !n) { return runPart(value, text_part, opts, l10n); } // guard against non-finite numbers: - if (!isFinite(value)) { + if (!n && !isFinite(value)) { const loc = l10n || defaultLocale; if (isNaN(value)) { return loc.nan; } return (value < 0 ? loc.negative : '') + loc.infinity; diff --git a/lib/index.js b/lib/index.js index fb9d096..dafc8f6 100644 --- a/lib/index.js +++ b/lib/index.js @@ -102,6 +102,9 @@ function prepareFormatterData (pattern, shouldThrow = false) { * @param {boolean} [options.dateErrorNumber=true] * Should the formatter switch to a General number format when trying to * format a date that is out of bounds? + * @param {boolean} [options.bigintErrorNumber=false] + * Should the formatter switch to a plain string number format when trying to + * format a bigint that is out of bounds? * @param {boolean} [options.dateSpanLarge=true] * Extends the allowed range of dates from Excel bounds (1900–9999) to * Google Sheet bounds (0–99999). diff --git a/lib/options.js b/lib/options.js index 7e84f70..0bae4f8 100644 --- a/lib/options.js +++ b/lib/options.js @@ -3,8 +3,10 @@ export const defaultOptions = { overflow: '######', // dateErrorThrow needs to be off! [prev in locale] // Should it throw when there is an overflow error? dateErrorThrows: false, - // Should it emit a number is an overflow error? (Sheets does this) + // Should it emit a number when date has an overflow error? (Sheets does this) dateErrorNumber: true, // dateErrorThrow needs to be off! + // Should it emit a number when bigint has an is an overflow error? + bigintErrorNumber: false, // Sheets mode (see #3) dateSpanLarge: true, // Simulate the Lotus 1-2-3 leap year bug diff --git a/lib/runPart.js b/lib/runPart.js index 07885f2..f48401c 100644 --- a/lib/runPart.js +++ b/lib/runPart.js @@ -37,7 +37,21 @@ export function runPart (value, part, opts, l10n_) { let integer = ''; let exp = 0; - let date = value | 0; + let date = 0; + if (typeof value === 'bigint') { + if (value <= Number.MAX_SAFE_INTEGER && value >= Number.MIN_SAFE_INTEGER) { + value = Number(value); + } + else { + return opts.bigintErrorNumber + ? String(value) + : opts.overflow; + } + date = value; + } + else { + date = Math.trunc(value); + } let time = 0; let year = 0; let month = 1; diff --git a/test/bigint-test.js b/test/bigint-test.js new file mode 100644 index 0000000..810f983 --- /dev/null +++ b/test/bigint-test.js @@ -0,0 +1,30 @@ +import test from './utils.js'; + +test('bigint', t => { + t.format('0', Number.MAX_SAFE_INTEGER, String(Number.MAX_SAFE_INTEGER)); + t.format('0', 10n, '10'); + + t.format('General', 10n, '10'); + t.format('General', 9007199254740991n, '9.0072E+15'); + + t.format('0.0', 9007199254740991n, '9007199254740990.0'); + + t.format('#,##0.0', 9007199254740991n, '9,007,199,254,740,990.0'); + t.format('#,##0.0', 9007199254750000n, '######'); + t.format('#,##0.0', -9007199254750000n, '######'); + + t.format('#0-000-00', 9007199254750000n, '######'); + t.format('0%', 9007199254750000n, '######'); + + t.format('#0-000-00', 9007199254750000n, '9007199254750000', { bigintErrorNumber: true }); + t.format('0%', 9007199254750000n, '9007199254750000', { bigintErrorNumber: true }); + + // preferably we should support bigint throughout: + // t.format('#0-000-00', 9007199254750000n, '90071992547-500-00'); + // t.format('0%', 9007199254750000n, '900719925475000000%'); + + t.format('0.000E+00', 999990000, '1.000E+09'); + t.format('0.000E+00', 999990000n, '1.000E+09'); + + t.end(); +}); diff --git a/test/utils.js b/test/utils.js index 23b8a58..9788511 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,4 +1,3 @@ -// tests converted from SSF import test, { Test } from 'tape'; import { format, formatColor, getFormatDateInfo, getFormatInfo } from '../lib/index.js'; import fs from 'fs'; @@ -84,7 +83,8 @@ Test.prototype.runTable = function runSSFTable (pathToTable) { function formatMessage (pattern, value, options) { let message = pattern; - message += '\x1b[36m <=> ' + value + ''; + const suffix = (typeof value === 'bigint') ? 'n' : ''; + message += '\x1b[36m <=> ' + value + suffix; const o = JSON.stringify(options); if (o !== '{}') { message += '\x1b[2m\x1b[33m [ OPTIONS=' + o + ' ]';