Хелперы для работы с датами + polyfill для свежего API 2025 года
This commit is contained in:
parent
b0bd04b8d6
commit
a6bccea822
6 changed files with 134 additions and 17 deletions
|
|
@ -10,12 +10,12 @@ module.exports = {
|
||||||
plugins: ['@typescript-eslint'],
|
plugins: ['@typescript-eslint'],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
ecmaVersion: 2020,
|
ecmaVersion: 2024,
|
||||||
extraFileExtensions: ['.svelte']
|
extraFileExtensions: ['.svelte']
|
||||||
},
|
},
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
es2017: true,
|
es2024: true,
|
||||||
node: true
|
node: true
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
|
|
|
||||||
51
package-lock.json
generated
51
package-lock.json
generated
|
|
@ -7,6 +7,9 @@
|
||||||
"": {
|
"": {
|
||||||
"name": "teasanctuary-ru",
|
"name": "teasanctuary-ru",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
|
"dependencies": {
|
||||||
|
"@formatjs/intl-durationformat": "^0.7.6"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify/svelte": "^4.2.0",
|
"@iconify/svelte": "^4.2.0",
|
||||||
"@react2svelte/swipeable": "^0.1.4",
|
"@react2svelte/swipeable": "^0.1.4",
|
||||||
|
|
@ -619,6 +622,47 @@
|
||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@formatjs/ecma402-abstract": {
|
||||||
|
"version": "2.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz",
|
||||||
|
"integrity": "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@formatjs/fast-memoize": "2.2.7",
|
||||||
|
"@formatjs/intl-localematcher": "0.6.2",
|
||||||
|
"decimal.js": "^10.4.3",
|
||||||
|
"tslib": "^2.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@formatjs/fast-memoize": {
|
||||||
|
"version": "2.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz",
|
||||||
|
"integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@formatjs/intl-durationformat": {
|
||||||
|
"version": "0.7.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/intl-durationformat/-/intl-durationformat-0.7.6.tgz",
|
||||||
|
"integrity": "sha512-jatAN3E84X6aP2UOGK1jTrwD1a7BiG3qWUSEDAhtyNd1BgYeS5wQPtXlnuGF1QRx0DjnwwNOIssyd7oQoRlQeg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@formatjs/ecma402-abstract": "2.3.6",
|
||||||
|
"@formatjs/intl-localematcher": "0.6.2",
|
||||||
|
"tslib": "^2.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@formatjs/intl-localematcher": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz",
|
||||||
|
"integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@humanfs/core": {
|
"node_modules/@humanfs/core": {
|
||||||
"version": "0.19.1",
|
"version": "0.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||||
|
|
@ -1960,6 +2004,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/decimal.js": {
|
||||||
|
"version": "10.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
|
||||||
|
"integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/deep-is": {
|
"node_modules/deep-is": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
|
||||||
|
|
@ -4097,7 +4147,6 @@
|
||||||
"version": "2.8.1",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
"dev": true,
|
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
|
|
|
||||||
|
|
@ -41,5 +41,8 @@
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"vite": "^6.2.0"
|
"vite": "^6.2.0"
|
||||||
},
|
},
|
||||||
"type": "module"
|
"type": "module",
|
||||||
|
"dependencies": {
|
||||||
|
"@formatjs/intl-durationformat": "^0.7.6"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
src/app.d.ts
vendored
13
src/app.d.ts
vendored
|
|
@ -1,4 +1,17 @@
|
||||||
|
import type {
|
||||||
|
DurationFormatConstructor,
|
||||||
|
DurationFormatOptions as _DurationFormatOptions,
|
||||||
|
DurationInput as _DurationInput,
|
||||||
|
} from '@formatjs/intl-durationformat/src/types';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
// rndtrash: Терпим. https://github.com/microsoft/TypeScript/issues/60608
|
||||||
|
namespace Intl {
|
||||||
|
const DurationFormat: DurationFormatConstructor;
|
||||||
|
type DurationFormatOptions = _DurationFormatOptions;
|
||||||
|
type DurationInput = _DurationInput;
|
||||||
|
}
|
||||||
|
|
||||||
namespace App {
|
namespace App {
|
||||||
interface Route {
|
interface Route {
|
||||||
label: string;
|
label: string;
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,27 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Icon from '@iconify/svelte';
|
import Icon from '@iconify/svelte';
|
||||||
|
import { dateFormatLong, dateFormatShort } from '$lib/util/Dates';
|
||||||
|
|
||||||
let className: string = '';
|
let {
|
||||||
export { className as class };
|
dateString,
|
||||||
|
type = undefined,
|
||||||
|
highlight = false,
|
||||||
|
showTime = false,
|
||||||
|
class: className = ''
|
||||||
|
}: {
|
||||||
|
dateString?: string;
|
||||||
|
type?: 'published' | 'updated';
|
||||||
|
highlight?: boolean;
|
||||||
|
showTime?: boolean;
|
||||||
|
class?: string;
|
||||||
|
} = $props();
|
||||||
|
|
||||||
export let dateString: string | undefined;
|
const typeToIcon = {
|
||||||
export let type: 'published' | 'updated' = 'published';
|
published: 'material-symbols:calendar-today',
|
||||||
export let highlight = false;
|
updated: 'material-symbols:update'
|
||||||
|
};
|
||||||
|
|
||||||
const icon =
|
const icon = type ? typeToIcon[type] : undefined;
|
||||||
type == 'published' ? 'material-symbols:calendar-today' : 'material-symbols:update';
|
|
||||||
|
|
||||||
const highlightClasses = (classes: string) => (highlight ? classes : '');
|
const highlightClasses = (classes: string) => (highlight ? classes : '');
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -19,14 +31,12 @@
|
||||||
{highlightClasses(type == 'published' ? 'bg-amber-600' : 'bg-purple-600')}
|
{highlightClasses(type == 'published' ? 'bg-amber-600' : 'bg-purple-600')}
|
||||||
{highlightClasses('text-slate-50')}"
|
{highlightClasses('text-slate-50')}"
|
||||||
>
|
>
|
||||||
|
{#if icon}
|
||||||
<Icon {icon} width={28} height={28} />
|
<Icon {icon} width={28} height={28} />
|
||||||
|
{/if}
|
||||||
<span class="text-nowrap">
|
<span class="text-nowrap">
|
||||||
{dateString
|
{dateString
|
||||||
? new Date(dateString).toLocaleString(undefined, {
|
? (showTime ? dateFormatLong : dateFormatShort).format(new Date(dateString))
|
||||||
month: 'short',
|
|
||||||
day: 'numeric',
|
|
||||||
year: 'numeric'
|
|
||||||
})
|
|
||||||
: 'Не опубликован!'}
|
: 'Не опубликован!'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
42
src/lib/util/Dates.ts
Normal file
42
src/lib/util/Dates.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { shouldPolyfill } from '@formatjs/intl-durationformat/should-polyfill'
|
||||||
|
if (shouldPolyfill()) {
|
||||||
|
import('@formatjs/intl-durationformat/polyfill-force');
|
||||||
|
}
|
||||||
|
|
||||||
|
export const dateFormatShort = new Intl.DateTimeFormat(undefined, {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric'
|
||||||
|
});
|
||||||
|
|
||||||
|
export const dateFormatLong = new Intl.DateTimeFormat(undefined, {
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
});
|
||||||
|
|
||||||
|
export const dateDurationLong = new Intl.DurationFormat(undefined, {
|
||||||
|
style: "long"
|
||||||
|
});
|
||||||
|
|
||||||
|
export const dateDurationLongBackup = new Intl.DurationFormat(undefined, {
|
||||||
|
style: "long",
|
||||||
|
seconds: "long"
|
||||||
|
});
|
||||||
|
|
||||||
|
export const durationHumanReadable = (a: Date, b: Date) => durationHumanReadableMillis(b.valueOf() - a.valueOf());
|
||||||
|
|
||||||
|
export function durationHumanReadableMillis(dur: number): string {
|
||||||
|
dur = Math.max(dur, 0);
|
||||||
|
const seconds = dur / 1000;
|
||||||
|
const minutes = dur / (1000 * 60);
|
||||||
|
const hours = dur / (1000 * 60 * 60);
|
||||||
|
const days = dur / (1000 * 60 * 60 * 24);
|
||||||
|
|
||||||
|
const formatObject = { days: Math.floor(days), hours: Math.floor(hours) % 24, minutes: Math.floor(minutes) % 60, seconds: Math.floor(seconds) % 60 };
|
||||||
|
const result = dateDurationLong.format(formatObject);
|
||||||
|
// Если на выходе получаем пустую строку, то хоть выведем 0 секунд
|
||||||
|
return result === '' ? dateDurationLongBackup.format(formatObject) : result;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue