@leaderty/utils 2.0.1 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +114 -0
- package/dist/format.d.ts +81 -0
- package/dist/format.js +157 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +7 -5
- package/package.json +3 -2
- package/dist/index.test.d.ts +0 -1
- package/dist/index.test.js +0 -7
package/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# @leaderty/utils
|
|
2
|
+
|
|
3
|
+
Argentine number and date formatting. Every function returns the same output on any system: none of them uses `Intl`, `toLocaleString()` or the system time zone, so a WebView in English still shows `350.000` and `dd/mm/aaaa`.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @leaderty/utils
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Functions
|
|
12
|
+
|
|
13
|
+
### `numberToMoney(amount, options?)`
|
|
14
|
+
|
|
15
|
+
Formats a money amount with `.` for thousands and `,` for decimals, optionally prefixed with the currency.
|
|
16
|
+
|
|
17
|
+
**Behavior:**
|
|
18
|
+
- Rounds half away from zero on the decimal value: `1.005` gives `1,01`, and float noise such as `2309.9999999999995` gives `2.310,00`.
|
|
19
|
+
- Puts the minus sign before the currency (`-$ 1.234,50`). An amount that rounds to zero has no sign.
|
|
20
|
+
- Returns `'-'` for `null`, `undefined`, `NaN` and infinite amounts.
|
|
21
|
+
|
|
22
|
+
**Parameters:**
|
|
23
|
+
- `amount` - `number | null | undefined`
|
|
24
|
+
- `options.decimals` - `0 | 2`. Defaults to `0`.
|
|
25
|
+
- `options.currency` - `'ARS' | 'USD'`. Omit it for a bare number.
|
|
26
|
+
- `options.currencyDisplay` - `'code'` (default) prints `ARS` / `USD`; `'symbol'` prints `$` / `US$`. Ignored without `currency`.
|
|
27
|
+
|
|
28
|
+
**Returns:** `string`
|
|
29
|
+
|
|
30
|
+
**Examples:**
|
|
31
|
+
```typescript
|
|
32
|
+
numberToMoney(350000) // "350.000"
|
|
33
|
+
numberToMoney(350000, { currency: 'ARS' }) // "ARS 350.000"
|
|
34
|
+
numberToMoney(350000, { currency: 'ARS', currencyDisplay: 'symbol' }) // "$ 350.000"
|
|
35
|
+
numberToMoney(350000, { currency: 'ARS', currencyDisplay: 'symbol', decimals: 2 }) // "$ 350.000,00"
|
|
36
|
+
numberToMoney(1500, { currency: 'USD', currencyDisplay: 'symbol', decimals: 2 }) // "US$ 1.500,00"
|
|
37
|
+
numberToMoney(-1234.5, { decimals: 2 }) // "-1.234,50"
|
|
38
|
+
numberToMoney(undefined, { currency: 'ARS' }) // "-"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### `dateToShort(value, options?)`
|
|
42
|
+
|
|
43
|
+
Formats a date as `dd/mm/aaaa`, or `dd/mm/aaaa hh:mm` (24-hour) with `time: true`.
|
|
44
|
+
|
|
45
|
+
**Behavior:**
|
|
46
|
+
- `'aaaa-mm-dd'` strings, and date-time strings without a time zone (`'2026-10-01T00:00:00'`), are read as written. `'2026-10-11'` gives `11/10/2026`, even though `new Date('2026-10-11')` is midnight UTC, which is still October 10 in Argentina.
|
|
47
|
+
- `Date` objects and ISO strings with a time zone (`Z` or `±hh:mm`, as in `'2026-10-22T22:05:36.036Z'`) are shown in `America/Argentina/Buenos_Aires` time (UTC-3, no daylight saving time).
|
|
48
|
+
- Any other string gives `'-'` (`'11/10/2026'`, `'2026-10'`, `'Oct 11 2026'`): `new Date()` would read it in the system time zone or guess the day/month order.
|
|
49
|
+
- Also returns `'-'` for `null`, `undefined`, invalid dates and impossible dates such as `'2026-02-30'`.
|
|
50
|
+
|
|
51
|
+
**Parameters:**
|
|
52
|
+
- `value` - `Date | string | null | undefined`
|
|
53
|
+
- `options.time` - Appends the time. Defaults to `false`.
|
|
54
|
+
|
|
55
|
+
**Returns:** `string`
|
|
56
|
+
|
|
57
|
+
**Examples:**
|
|
58
|
+
```typescript
|
|
59
|
+
dateToShort('2026-10-11') // "11/10/2026"
|
|
60
|
+
dateToShort('2026-10-01T00:00:00') // "01/10/2026"
|
|
61
|
+
dateToShort(new Date('2026-10-22T22:05:36Z')) // "22/10/2026"
|
|
62
|
+
dateToShort('2026-10-22T02:05:36Z', { time: true }) // "21/10/2026 23:05"
|
|
63
|
+
dateToShort('11/10/2026') // "-"
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### `dateToYMD(value)`
|
|
67
|
+
|
|
68
|
+
Formats a date as `aaaa-mm-dd`, the value of an `<input type="date">`. Reads `value` like `dateToShort`, so `'2026-10-11'` stays the 11th and a `Date` is taken in Buenos Aires time.
|
|
69
|
+
|
|
70
|
+
**Parameters:**
|
|
71
|
+
- `value` - `Date | string | null | undefined`
|
|
72
|
+
|
|
73
|
+
**Returns:** `string`. `''` (not `'-'`, so a date input stays empty) when `value` is missing or not a valid date.
|
|
74
|
+
|
|
75
|
+
**Examples:**
|
|
76
|
+
```typescript
|
|
77
|
+
dateToYMD('2026-10-11') // "2026-10-11"
|
|
78
|
+
dateToYMD(new Date('2026-10-22T02:05:36Z')) // "2026-10-21"
|
|
79
|
+
dateToYMD('11/10/2026') // ""
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### `dateToMonthYearLong(value)`
|
|
83
|
+
|
|
84
|
+
Formats the month and year in Spanish, lowercase. Also accepts `'aaaa-mm'` strings (the `MonthSelector` value of `@leaderty/react-month-selector`); otherwise reads `value` like `dateToShort`.
|
|
85
|
+
|
|
86
|
+
**Parameters:**
|
|
87
|
+
- `value` - `Date | string | null | undefined`
|
|
88
|
+
|
|
89
|
+
**Returns:** `string`, or `'-'` when the date is missing or invalid
|
|
90
|
+
|
|
91
|
+
**Examples:**
|
|
92
|
+
```typescript
|
|
93
|
+
dateToMonthYearLong('2026-10') // "octubre de 2026"
|
|
94
|
+
dateToMonthYearLong('2026-10-11') // "octubre de 2026"
|
|
95
|
+
dateToMonthYearLong('2026-10-01') // "octubre de 2026" (not September)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### `numberToDecimalString(numero)`
|
|
99
|
+
|
|
100
|
+
Formats a number with two decimals and thousands separators. Rounds like `numberToMoney(n, { decimals: 2 })`, so `1.005` gives `1,01`. Formerly `numberToStringWithDecimals`, which stays as a deprecated alias.
|
|
101
|
+
|
|
102
|
+
**Parameters:**
|
|
103
|
+
- `numero` - `number`
|
|
104
|
+
|
|
105
|
+
**Returns:** `string`
|
|
106
|
+
|
|
107
|
+
**Examples:**
|
|
108
|
+
```typescript
|
|
109
|
+
numberToDecimalString(1234.56) // "1.234,56"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Types
|
|
113
|
+
|
|
114
|
+
`Currency`, `NumberToMoneyOptions` and `DateToShortOptions` are exported for consumers.
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Argentine display formatters that never depend on the system locale or `Intl`:
|
|
3
|
+
* the WebView formats `toLocaleString()` with the OS language, so an English
|
|
4
|
+
* system shows "350,000" where an Argentine user reads 350 pesos.
|
|
5
|
+
*/
|
|
6
|
+
export type Currency = 'ARS' | 'USD';
|
|
7
|
+
export interface NumberToMoneyOptions {
|
|
8
|
+
/** Decimal places. Defaults to `0`. */
|
|
9
|
+
decimals?: 0 | 2;
|
|
10
|
+
/** Prefixes the amount with the currency. Omit it for a bare number. */
|
|
11
|
+
currency?: Currency;
|
|
12
|
+
/** `'code'` (default) prints `ARS` / `USD`; `'symbol'` prints `$` / `US$`. Ignored without `currency`. */
|
|
13
|
+
currencyDisplay?: 'code' | 'symbol';
|
|
14
|
+
}
|
|
15
|
+
export interface DateToShortOptions {
|
|
16
|
+
/** Appends the 24-hour time as `hh:mm`. Defaults to `false`. */
|
|
17
|
+
time?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Formats a money amount the Argentine way: `.` for thousands and `,` for decimals.
|
|
21
|
+
* Rounds half away from zero on the decimal value, so `1.005` gives `1,01` and float noise
|
|
22
|
+
* such as `2309.9999999999995` gives `2.310,00`.
|
|
23
|
+
*
|
|
24
|
+
* @param amount - Amount to format
|
|
25
|
+
* @param options - Decimals and currency prefix
|
|
26
|
+
* @returns The formatted amount, or `'-'` when `amount` is `null`, `undefined`, `NaN` or infinite
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* numberToMoney(350000) // "350.000"
|
|
30
|
+
* numberToMoney(350000, { currency: 'ARS' }) // "ARS 350.000"
|
|
31
|
+
* numberToMoney(350000, { currency: 'ARS', currencyDisplay: 'symbol', decimals: 2 }) // "$ 350.000,00"
|
|
32
|
+
* numberToMoney(-1234.5, { currency: 'USD', currencyDisplay: 'symbol', decimals: 2 }) // "-US$ 1.234,50"
|
|
33
|
+
*/
|
|
34
|
+
export declare function numberToMoney(amount: number | null | undefined, options?: NumberToMoneyOptions): string;
|
|
35
|
+
/**
|
|
36
|
+
* Formats a date as `dd/mm/aaaa`, optionally with the time as `dd/mm/aaaa hh:mm`.
|
|
37
|
+
*
|
|
38
|
+
* - `'aaaa-mm-dd'` strings, and date-time strings without a time zone (`'2026-10-01T00:00:00'`),
|
|
39
|
+
* are read as written: `'2026-10-11'` is the 11th, never the 10th
|
|
40
|
+
* (`new Date('2026-10-11')` is midnight UTC, which is still the 10th in Argentina).
|
|
41
|
+
* - `Date` objects and ISO strings with a time zone (`'2026-10-22T22:05:36.036Z'`) are shown in
|
|
42
|
+
* `America/Argentina/Buenos_Aires` time.
|
|
43
|
+
* - Any other string (`'11/10/2026'`, `'2026-10'`, `'Oct 11 2026'`) gives `'-'`: the engine would parse it
|
|
44
|
+
* in the system time zone, or guess the day/month order.
|
|
45
|
+
*
|
|
46
|
+
* @param value - Date to format
|
|
47
|
+
* @param options - Whether to append the time
|
|
48
|
+
* @returns The formatted date, or `'-'` when `value` is missing or not a valid date
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* dateToShort('2026-10-11') // "11/10/2026"
|
|
52
|
+
* dateToShort(new Date('2026-10-22T22:05:36Z')) // "22/10/2026"
|
|
53
|
+
* dateToShort('2026-10-22T02:05:36Z', { time: true }) // "21/10/2026 23:05"
|
|
54
|
+
*/
|
|
55
|
+
export declare function dateToShort(value: Date | string | null | undefined, options?: DateToShortOptions): string;
|
|
56
|
+
/**
|
|
57
|
+
* Formats a date as `aaaa-mm-dd`, the value of an `<input type="date">`. Reads `value` like {@link dateToShort}.
|
|
58
|
+
*
|
|
59
|
+
* @param value - Date to format
|
|
60
|
+
* @returns The date as `aaaa-mm-dd`, or `''` (not `'-'`, so a date input stays empty) when `value` is missing or not a valid date
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* dateToYMD('2026-10-11') // "2026-10-11"
|
|
64
|
+
* dateToYMD('2026-10-11T00:00:00') // "2026-10-11"
|
|
65
|
+
* dateToYMD(new Date('2026-10-22T02:05:36Z')) // "2026-10-21"
|
|
66
|
+
* dateToYMD('11/10/2026') // ""
|
|
67
|
+
*/
|
|
68
|
+
export declare function dateToYMD(value: Date | string | null | undefined): string;
|
|
69
|
+
/**
|
|
70
|
+
* Formats the month and year of a date in Spanish, lowercase: `octubre de 2026`.
|
|
71
|
+
* Also accepts `'aaaa-mm'` strings (the `MonthSelector` value). Otherwise reads `value` like {@link dateToShort}.
|
|
72
|
+
*
|
|
73
|
+
* @param value - Date to format
|
|
74
|
+
* @returns The month and year, or `'-'` when `value` is missing or not a valid date
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* dateToMonthYearLong('2026-10') // "octubre de 2026"
|
|
78
|
+
* dateToMonthYearLong('2026-10-11') // "octubre de 2026"
|
|
79
|
+
* dateToMonthYearLong(new Date(2027, 0, 11)) // "enero de 2027"
|
|
80
|
+
*/
|
|
81
|
+
export declare function dateToMonthYearLong(value: Date | string | null | undefined): string;
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Argentine display formatters that never depend on the system locale or `Intl`:
|
|
3
|
+
* the WebView formats `toLocaleString()` with the OS language, so an English
|
|
4
|
+
* system shows "350,000" where an Argentine user reads 350 pesos.
|
|
5
|
+
*/
|
|
6
|
+
/** Shown instead of a value that is missing or not a valid number/date. */
|
|
7
|
+
const PLACEHOLDER = '-';
|
|
8
|
+
const CURRENCY_SYMBOLS = { ARS: '$', USD: 'US$' };
|
|
9
|
+
/** Argentina is UTC-3 all year (no daylight saving time since 2009). */
|
|
10
|
+
const BUENOS_AIRES_OFFSET_MS = -3 * 60 * 60 * 1000;
|
|
11
|
+
/** `aaaa-mm-dd`, optionally followed by a time and no time zone: a calendar date or Argentine wall-clock time. */
|
|
12
|
+
const WALL_CLOCK_PATTERN = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::\d{2}(?:\.\d+)?)?)?$/;
|
|
13
|
+
/** ISO 8601 date-time with an explicit time zone (`Z` or `±hh:mm`): an instant, the same in any system time zone. */
|
|
14
|
+
const ZONED_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
15
|
+
/** `aaaa-mm`, the month format of `@leaderty/react-month-selector` (same pattern as its `YEAR_MONTH_PATTERN`). */
|
|
16
|
+
const YEAR_MONTH_PATTERN = /^(\d{4})-(0[1-9]|1[0-2])$/;
|
|
17
|
+
const MONTH_NAMES = [
|
|
18
|
+
'enero', 'febrero', 'marzo', 'abril', 'mayo', 'junio',
|
|
19
|
+
'julio', 'agosto', 'septiembre', 'octubre', 'noviembre', 'diciembre',
|
|
20
|
+
];
|
|
21
|
+
/**
|
|
22
|
+
* Formats a money amount the Argentine way: `.` for thousands and `,` for decimals.
|
|
23
|
+
* Rounds half away from zero on the decimal value, so `1.005` gives `1,01` and float noise
|
|
24
|
+
* such as `2309.9999999999995` gives `2.310,00`.
|
|
25
|
+
*
|
|
26
|
+
* @param amount - Amount to format
|
|
27
|
+
* @param options - Decimals and currency prefix
|
|
28
|
+
* @returns The formatted amount, or `'-'` when `amount` is `null`, `undefined`, `NaN` or infinite
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* numberToMoney(350000) // "350.000"
|
|
32
|
+
* numberToMoney(350000, { currency: 'ARS' }) // "ARS 350.000"
|
|
33
|
+
* numberToMoney(350000, { currency: 'ARS', currencyDisplay: 'symbol', decimals: 2 }) // "$ 350.000,00"
|
|
34
|
+
* numberToMoney(-1234.5, { currency: 'USD', currencyDisplay: 'symbol', decimals: 2 }) // "-US$ 1.234,50"
|
|
35
|
+
*/
|
|
36
|
+
export function numberToMoney(amount, options = {}) {
|
|
37
|
+
if (amount === null || amount === undefined || !Number.isFinite(amount))
|
|
38
|
+
return PLACEHOLDER;
|
|
39
|
+
const { decimals = 0, currency, currencyDisplay = 'code' } = options;
|
|
40
|
+
const absolute = Math.abs(amount);
|
|
41
|
+
// Shifting the exponent in the string avoids binary noise: 1.005 * 100 === 100.49999999999999
|
|
42
|
+
const shifted = String(absolute).includes('e')
|
|
43
|
+
? absolute * 10 ** decimals
|
|
44
|
+
: Number(`${absolute}e${decimals}`);
|
|
45
|
+
const digits = String(Math.round(shifted)).padStart(decimals + 1, '0');
|
|
46
|
+
const integerPart = digits.slice(0, digits.length - decimals).replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
|
47
|
+
const decimalPart = digits.slice(digits.length - decimals);
|
|
48
|
+
const number = decimals > 0 ? `${integerPart},${decimalPart}` : integerPart;
|
|
49
|
+
// An amount that rounds to zero has no sign: -0.001 gives "0,00", not "-0,00"
|
|
50
|
+
const sign = amount < 0 && /[1-9]/.test(digits) ? '-' : '';
|
|
51
|
+
if (!currency)
|
|
52
|
+
return `${sign}${number}`;
|
|
53
|
+
const prefix = currencyDisplay === 'symbol' ? CURRENCY_SYMBOLS[currency] : currency;
|
|
54
|
+
return `${sign}${prefix} ${number}`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Formats a date as `dd/mm/aaaa`, optionally with the time as `dd/mm/aaaa hh:mm`.
|
|
58
|
+
*
|
|
59
|
+
* - `'aaaa-mm-dd'` strings, and date-time strings without a time zone (`'2026-10-01T00:00:00'`),
|
|
60
|
+
* are read as written: `'2026-10-11'` is the 11th, never the 10th
|
|
61
|
+
* (`new Date('2026-10-11')` is midnight UTC, which is still the 10th in Argentina).
|
|
62
|
+
* - `Date` objects and ISO strings with a time zone (`'2026-10-22T22:05:36.036Z'`) are shown in
|
|
63
|
+
* `America/Argentina/Buenos_Aires` time.
|
|
64
|
+
* - Any other string (`'11/10/2026'`, `'2026-10'`, `'Oct 11 2026'`) gives `'-'`: the engine would parse it
|
|
65
|
+
* in the system time zone, or guess the day/month order.
|
|
66
|
+
*
|
|
67
|
+
* @param value - Date to format
|
|
68
|
+
* @param options - Whether to append the time
|
|
69
|
+
* @returns The formatted date, or `'-'` when `value` is missing or not a valid date
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* dateToShort('2026-10-11') // "11/10/2026"
|
|
73
|
+
* dateToShort(new Date('2026-10-22T22:05:36Z')) // "22/10/2026"
|
|
74
|
+
* dateToShort('2026-10-22T02:05:36Z', { time: true }) // "21/10/2026 23:05"
|
|
75
|
+
*/
|
|
76
|
+
export function dateToShort(value, options = {}) {
|
|
77
|
+
const parts = toDateParts(value);
|
|
78
|
+
if (!parts)
|
|
79
|
+
return PLACEHOLDER;
|
|
80
|
+
const date = `${pad(parts.day)}/${pad(parts.month)}/${parts.year}`;
|
|
81
|
+
return options.time ? `${date} ${pad(parts.hours)}:${pad(parts.minutes)}` : date;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Formats a date as `aaaa-mm-dd`, the value of an `<input type="date">`. Reads `value` like {@link dateToShort}.
|
|
85
|
+
*
|
|
86
|
+
* @param value - Date to format
|
|
87
|
+
* @returns The date as `aaaa-mm-dd`, or `''` (not `'-'`, so a date input stays empty) when `value` is missing or not a valid date
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* dateToYMD('2026-10-11') // "2026-10-11"
|
|
91
|
+
* dateToYMD('2026-10-11T00:00:00') // "2026-10-11"
|
|
92
|
+
* dateToYMD(new Date('2026-10-22T02:05:36Z')) // "2026-10-21"
|
|
93
|
+
* dateToYMD('11/10/2026') // ""
|
|
94
|
+
*/
|
|
95
|
+
export function dateToYMD(value) {
|
|
96
|
+
const parts = toDateParts(value);
|
|
97
|
+
if (!parts)
|
|
98
|
+
return '';
|
|
99
|
+
return `${parts.year}-${pad(parts.month)}-${pad(parts.day)}`;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Formats the month and year of a date in Spanish, lowercase: `octubre de 2026`.
|
|
103
|
+
* Also accepts `'aaaa-mm'` strings (the `MonthSelector` value). Otherwise reads `value` like {@link dateToShort}.
|
|
104
|
+
*
|
|
105
|
+
* @param value - Date to format
|
|
106
|
+
* @returns The month and year, or `'-'` when `value` is missing or not a valid date
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* dateToMonthYearLong('2026-10') // "octubre de 2026"
|
|
110
|
+
* dateToMonthYearLong('2026-10-11') // "octubre de 2026"
|
|
111
|
+
* dateToMonthYearLong(new Date(2027, 0, 11)) // "enero de 2027"
|
|
112
|
+
*/
|
|
113
|
+
export function dateToMonthYearLong(value) {
|
|
114
|
+
const yearMonth = typeof value === 'string' ? YEAR_MONTH_PATTERN.exec(value) : null;
|
|
115
|
+
const parts = yearMonth ? { year: Number(yearMonth[1]), month: Number(yearMonth[2]) } : toDateParts(value);
|
|
116
|
+
if (!parts)
|
|
117
|
+
return PLACEHOLDER;
|
|
118
|
+
return `${MONTH_NAMES[parts.month - 1]} de ${parts.year}`;
|
|
119
|
+
}
|
|
120
|
+
function toDateParts(value) {
|
|
121
|
+
if (value === null || value === undefined)
|
|
122
|
+
return null;
|
|
123
|
+
if (typeof value === 'string') {
|
|
124
|
+
const match = WALL_CLOCK_PATTERN.exec(value);
|
|
125
|
+
if (match) {
|
|
126
|
+
const [year, month, day, hours, minutes] = match.slice(1).map((part) => Number(part ?? 0));
|
|
127
|
+
if (!isCalendarDate(year, month, day) || hours > 23 || minutes > 59)
|
|
128
|
+
return null;
|
|
129
|
+
return { year, month, day, hours, minutes };
|
|
130
|
+
}
|
|
131
|
+
if (!ZONED_PATTERN.test(value))
|
|
132
|
+
return null;
|
|
133
|
+
// The engine rolls 2026-02-30T10:00Z over to March 2
|
|
134
|
+
const [year, month, day] = value.slice(0, 10).split('-').map(Number);
|
|
135
|
+
if (!isCalendarDate(year, month, day))
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const time = new Date(value).getTime();
|
|
139
|
+
if (Number.isNaN(time))
|
|
140
|
+
return null;
|
|
141
|
+
const buenosAires = new Date(time + BUENOS_AIRES_OFFSET_MS);
|
|
142
|
+
return {
|
|
143
|
+
year: buenosAires.getUTCFullYear(),
|
|
144
|
+
month: buenosAires.getUTCMonth() + 1,
|
|
145
|
+
day: buenosAires.getUTCDate(),
|
|
146
|
+
hours: buenosAires.getUTCHours(),
|
|
147
|
+
minutes: buenosAires.getUTCMinutes(),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/** Rejects impossible dates such as 2026-02-30, which Date.UTC would roll over to March. */
|
|
151
|
+
function isCalendarDate(year, month, day) {
|
|
152
|
+
const check = new Date(Date.UTC(year, month - 1, day));
|
|
153
|
+
return check.getUTCFullYear() === year && check.getUTCMonth() === month - 1 && check.getUTCDate() === day;
|
|
154
|
+
}
|
|
155
|
+
function pad(value) {
|
|
156
|
+
return String(value).padStart(2, '0');
|
|
157
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Formatea un número a una cadena con dos decimales y separadores de miles.
|
|
3
|
+
* Redondea igual que `numberToMoney(numero, { decimals: 2 })`: `1.005` da `1,01`.
|
|
3
4
|
*/
|
|
4
|
-
export declare function
|
|
5
|
+
export declare function numberToDecimalString(numero: number): string;
|
|
6
|
+
/** @deprecated Renamed to {@link numberToDecimalString}. */
|
|
7
|
+
export declare const numberToStringWithDecimals: typeof numberToDecimalString;
|
|
8
|
+
export { numberToMoney, dateToShort, dateToYMD, dateToMonthYearLong, type Currency, type NumberToMoneyOptions, type DateToShortOptions, } from './format.js';
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { numberToMoney } from './format.js';
|
|
1
2
|
/**
|
|
2
3
|
* Formatea un número a una cadena con dos decimales y separadores de miles.
|
|
4
|
+
* Redondea igual que `numberToMoney(numero, { decimals: 2 })`: `1.005` da `1,01`.
|
|
3
5
|
*/
|
|
4
|
-
export function
|
|
5
|
-
|
|
6
|
-
const [integerPart, decimalPart] = formatted.split('.');
|
|
7
|
-
const withThousandsSeparator = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
|
|
8
|
-
return `${withThousandsSeparator},${decimalPart}`;
|
|
6
|
+
export function numberToDecimalString(numero) {
|
|
7
|
+
return numberToMoney(numero, { decimals: 2 });
|
|
9
8
|
}
|
|
9
|
+
/** @deprecated Renamed to {@link numberToDecimalString}. */
|
|
10
|
+
export const numberToStringWithDecimals = numberToDecimalString;
|
|
11
|
+
export { numberToMoney, dateToShort, dateToYMD, dateToMonthYearLong, } from './format.js';
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leaderty/utils",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"files": [
|
|
8
|
-
"dist"
|
|
8
|
+
"dist",
|
|
9
|
+
"!dist/**/*.test*"
|
|
9
10
|
],
|
|
10
11
|
"publishConfig": {
|
|
11
12
|
"access": "public"
|
package/dist/index.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/index.test.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { numberToStringWithDecimals } from "./index";
|
|
3
|
-
describe("numberToStringWithDecimals", () => {
|
|
4
|
-
it("should format number with two decimals and thousand separators", () => {
|
|
5
|
-
expect(numberToStringWithDecimals(1234.56)).toBe("1.234,56");
|
|
6
|
-
});
|
|
7
|
-
});
|