Files
cinny/src/app/utils/datetimeInput.ts
T

60 lines
2.3 KiB
TypeScript
Raw Normal View History

import { CSSProperties } from 'react';
import { color as foldsColor, config as foldsConfig } from 'folds';
import { TimestampPrefs, dayWord, formatDate, formatTime } from './formatTimestamp';
const pad = (n: number): string => String(n).padStart(2, '0');
// Format a Date as the value string expected by <input type="date"> in the
// user's LOCAL timezone. (toISOString would shift to UTC and can land on the
// wrong calendar day.)
export function toLocalDate(date: Date): string {
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
}
// Format a Date as the value string expected by <input type="time"> (local, HH:mm).
export function toLocalTime(date: Date): string {
return `${pad(date.getHours())}:${pad(date.getMinutes())}`;
}
// Parse the local date + time <input> values back into a Date. Returns null when
// either value is missing or the combination is invalid (e.g. a partial entry).
export function parseLocalDateTime(dateValue: string, timeValue: string): Date | null {
if (!dateValue || !timeValue) return null;
const dt = new Date(`${dateValue}T${timeValue}:00`);
return Number.isNaN(dt.getTime()) ? null : dt;
}
// Human-friendly absolute time for scheduled things: "Today at 03:00 PM",
// "Tomorrow at 09:00 AM", "Mon at 09:00", or "12 Jan 2026 at 09:00" — the
// shared day-word rules and the user's clock/date preferences (#139). `now` is
// injectable so the relative-day logic is testable.
export function formatFriendlyDateTime(
ts: number,
prefs: TimestampPrefs,
now: number = Date.now(),
): string {
const day = dayWord(ts, now) ?? formatDate(ts, prefs);
return `${day} at ${formatTime(ts, prefs)}`;
}
// Shared style for date/time <input>s — matches the app's surface tokens and
// hints a dark-mode calendar/clock popup via colorScheme.
export function pickerInputStyle(
c: typeof foldsColor = foldsColor,
cfg: typeof foldsConfig = foldsConfig,
): CSSProperties {
return {
background: c.SurfaceVariant.Container,
color: c.SurfaceVariant.OnContainer,
border: `${cfg.borderWidth.B300} solid ${c.SurfaceVariant.ContainerLine}`,
borderRadius: cfg.radii.R300,
padding: `${cfg.space.S200} ${cfg.space.S300}`,
fontSize: '0.875rem',
width: '100%',
boxSizing: 'border-box',
outline: 'none',
fontFamily: 'inherit',
colorScheme: 'dark',
};
}