Хелперы для работы с датами + polyfill для свежего API 2025 года

This commit is contained in:
Иван Кузьменко 2025-11-16 06:24:33 +03:00
parent b0bd04b8d6
commit a6bccea822
6 changed files with 134 additions and 17 deletions

13
src/app.d.ts vendored
View file

@ -1,4 +1,17 @@
import type {
DurationFormatConstructor,
DurationFormatOptions as _DurationFormatOptions,
DurationInput as _DurationInput,
} from '@formatjs/intl-durationformat/src/types';
declare global {
// rndtrash: Терпим. https://github.com/microsoft/TypeScript/issues/60608
namespace Intl {
const DurationFormat: DurationFormatConstructor;
type DurationFormatOptions = _DurationFormatOptions;
type DurationInput = _DurationInput;
}
namespace App {
interface Route {
label: string;

View file

@ -1,15 +1,27 @@
<script lang="ts">
import Icon from '@iconify/svelte';
import { dateFormatLong, dateFormatShort } from '$lib/util/Dates';
let className: string = '';
export { className as class };
let {
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;
export let type: 'published' | 'updated' = 'published';
export let highlight = false;
const typeToIcon = {
published: 'material-symbols:calendar-today',
updated: 'material-symbols:update'
};
const icon =
type == 'published' ? 'material-symbols:calendar-today' : 'material-symbols:update';
const icon = type ? typeToIcon[type] : undefined;
const highlightClasses = (classes: string) => (highlight ? classes : '');
</script>
@ -19,14 +31,12 @@
{highlightClasses(type == 'published' ? 'bg-amber-600' : 'bg-purple-600')}
{highlightClasses('text-slate-50')}"
>
<Icon {icon} width={28} height={28} />
{#if icon}
<Icon {icon} width={28} height={28} />
{/if}
<span class="text-nowrap">
{dateString
? new Date(dateString).toLocaleString(undefined, {
month: 'short',
day: 'numeric',
year: 'numeric'
})
? (showTime ? dateFormatLong : dateFormatShort).format(new Date(dateString))
: 'Не опубликован!'}
</span>
</div>

42
src/lib/util/Dates.ts Normal file
View 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;
}