feat(shared): add signed percent and percentage-point formatters (WealthLedger/main#54)
This commit is contained in:
@@ -3,12 +3,22 @@ import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
formatDate,
|
||||
formatMoney,
|
||||
formatPercentagePoints,
|
||||
formatSignedPercent,
|
||||
formatSnapshotDay,
|
||||
formatSnapshotTime,
|
||||
instrumentLogoUrl,
|
||||
} from "./display";
|
||||
|
||||
describe("shared display helpers", () => {
|
||||
it("formats signed rates as percentages and percentage points without negative zero", () => {
|
||||
expect(formatSignedPercent("0.151")).toBe("+15,1 %");
|
||||
expect(formatSignedPercent("-0.15149")).toBe("−15,1 %");
|
||||
expect(formatSignedPercent("0")).toBe("0,0 %");
|
||||
expect(formatSignedPercent("-0.00049")).toBe("0,0 %");
|
||||
expect(formatPercentagePoints("0.043")).toBe("+4,3 п.п.");
|
||||
expect(formatPercentagePoints("-0.0435")).toBe("−4,4 п.п.");
|
||||
});
|
||||
it("uses padded Russian dates and preserves the UTC date of timestamps", () => {
|
||||
expect(formatDate("2027-09-10")).toBe("10.09.2027");
|
||||
expect(formatDate("2024-02-29")).toBe("29.02.2024");
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { decimalFromString } from "./decimal";
|
||||
|
||||
const DECIMAL_PART_COUNT = 2;
|
||||
const DEFAULT_MINOR_UNITS = 2;
|
||||
|
||||
@@ -99,6 +101,29 @@ function formatMoney(
|
||||
return value.startsWith("-") ? `−${formatted.slice(1)}` : `+${formatted}`;
|
||||
}
|
||||
|
||||
function formatPercentagePoints(rate: string): string {
|
||||
return formatRate(rate, "п.п.");
|
||||
}
|
||||
|
||||
function formatRate(rate: string, suffix: string): string {
|
||||
const { scale, value } = decimalFromString(rate);
|
||||
const abs = value < 0n ? -value : value;
|
||||
const radix = 10n;
|
||||
const half = 2n;
|
||||
const tenthsPerUnit = 1000n;
|
||||
const denominator = radix ** BigInt(scale);
|
||||
const tenths = (abs * tenthsPerUnit + denominator / half) / denominator;
|
||||
let sign = "";
|
||||
if (tenths !== 0n) sign = value < 0n ? "−" : "+";
|
||||
const formatted = `${formatDecimal(String(tenths / radix))},${tenths % radix}`;
|
||||
|
||||
return `${sign}${formatted} ${suffix}`;
|
||||
}
|
||||
|
||||
function formatSignedPercent(rate: string): string {
|
||||
return formatRate(rate, "%");
|
||||
}
|
||||
|
||||
function isZeroDecimal(value: string) {
|
||||
const unsigned = value.startsWith("-") ? value.slice(1) : value;
|
||||
|
||||
@@ -171,6 +196,8 @@ export {
|
||||
formatDate,
|
||||
formatDecimal,
|
||||
formatMoney,
|
||||
formatPercentagePoints,
|
||||
formatSignedPercent,
|
||||
formatSnapshotDay,
|
||||
formatSnapshotTime,
|
||||
instrumentLogoUrl,
|
||||
|
||||
@@ -12,6 +12,8 @@ export {
|
||||
formatDate,
|
||||
formatDecimal,
|
||||
formatMoney,
|
||||
formatPercentagePoints,
|
||||
formatSignedPercent,
|
||||
formatSnapshotDay,
|
||||
formatSnapshotTime,
|
||||
instrumentLogoUrl,
|
||||
|
||||
Reference in New Issue
Block a user