Add functionality for time-less date formatting

This commit is contained in:
Sam Becker 2025-01-22 19:31:47 -06:00
parent b02dc50b88
commit 59399bd703
3 changed files with 64 additions and 33 deletions

View File

@ -10,11 +10,13 @@ export default function ResponsiveDate({
className, className,
titleLabel, titleLabel,
timezone: timezoneFromProps, timezone: timezoneFromProps,
hideTime,
}: { }: {
date: Date date: Date
className?: string className?: string
titleLabel?: string titleLabel?: string
timezone?: Timezone timezone?: Timezone
hideTime?: boolean,
}) { }) {
const [timezone, setTimezone] = useState(timezoneFromProps); const [timezone, setTimezone] = useState(timezoneFromProps);
@ -24,23 +26,30 @@ export default function ResponsiveDate({
} }
}, [timezoneFromProps]); }, [timezoneFromProps]);
const showPlaceholderContent = timezone === undefined; const showPlaceholder = timezone === undefined;
const titleDateFormatted = formatDate(date, undefined, timezone) const titleDateFormatted = formatDate({ date, timezone })
.toLocaleUpperCase(); .toLocaleUpperCase();
const title = titleLabel const title = titleLabel
? `${titleLabel}: ${titleDateFormatted}` ? `${titleLabel}: ${titleDateFormatted}`
: titleDateFormatted; : titleDateFormatted;
const contentClass = showPlaceholderContent && 'opacity-0 select-none'; const contentClass = showPlaceholder && 'opacity-0 select-none';
const formatDateProps = {
date,
timezone,
showPlaceholder,
hideTime,
} as const;
return ( return (
<span <span
title={showPlaceholderContent ? 'LOADING LOCAL TIME' : title} title={showPlaceholder ? 'LOADING LOCAL TIME' : title}
className={clsx( className={clsx(
'uppercase rounded-md transition-colors', 'uppercase rounded-md transition-colors',
showPlaceholderContent && 'bg-dim', showPlaceholder && 'bg-dim',
className, className,
)} )}
> >
@ -49,20 +58,20 @@ export default function ResponsiveDate({
className={clsx('xs:hidden', contentClass)} className={clsx('xs:hidden', contentClass)}
aria-hidden aria-hidden
> >
{formatDate(date, 'short', timezone, showPlaceholderContent)} {formatDate({ ...formatDateProps, length: 'short' })}
</span> </span>
{/* Medium */} {/* Medium */}
<span <span
className={clsx('hidden xs:inline-block sm:hidden', contentClass)} className={clsx('hidden xs:inline-block sm:hidden', contentClass)}
aria-hidden aria-hidden
> >
{formatDate(date, 'medium', timezone,showPlaceholderContent)} {formatDate({ ...formatDateProps, length: 'medium' })}
</span> </span>
{/* Large */} {/* Large */}
<span <span
className={clsx('hidden sm:inline-block', contentClass)} className={clsx('hidden sm:inline-block', contentClass)}
> >
{formatDate(date, undefined, timezone, showPlaceholderContent)} {formatDate(formatDateProps)}
</span> </span>
</span> </span>
); );

View File

@ -212,7 +212,10 @@ export const titleForPhoto = (
if (photo.title) { if (photo.title) {
return photo.title; return photo.title;
} else if (preferDateOverUntitled && (photo.takenAt || photo.createdAt)) { } else if (preferDateOverUntitled && (photo.takenAt || photo.createdAt)) {
return formatDate(photo.takenAt || photo.createdAt, 'tiny'); return formatDate({
date: photo.takenAt || photo.createdAt,
length: 'tiny',
});
} else { } else {
return 'Untitled'; return 'Untitled';
} }

View File

@ -23,38 +23,57 @@ type AmbiguousTimestamp = number | string;
type Length = 'tiny' | 'short' | 'medium' | 'long'; type Length = 'tiny' | 'short' | 'medium' | 'long';
export const formatDate = ( export const formatDate = ({
date,
length = 'long',
timezone,
hideTime,
showPlaceholder,
}: {
date: Date, date: Date,
length: Length = 'long', length?: Length,
timezone?: Timezone, timezone?: Timezone,
hideTime?: boolean,
showPlaceholder?: boolean, showPlaceholder?: boolean,
) => { }) => {
switch (length) { let formatString = !hideTime
case 'tiny': return showPlaceholder ? DATE_STRING_FORMAT_LONG
? DATE_STRING_FORMAT_TINY_PLACEHOLDER : DATE_STRING_FORMAT_SHORT;
: timezone let placeholderString = !hideTime
? formatInTimeZone(date, timezone, DATE_STRING_FORMAT_TINY)
: format(date, DATE_STRING_FORMAT_TINY);
case 'short': return showPlaceholder
? DATE_STRING_FORMAT_SHORT_PLACEHOLDER
: timezone
? formatInTimeZone(date, timezone, DATE_STRING_FORMAT_SHORT)
: format(date, DATE_STRING_FORMAT_SHORT);
case 'medium': return showPlaceholder
? DATE_STRING_FORMAT_MEDIUM_PLACEHOLDER
: timezone
? formatInTimeZone(date, timezone, DATE_STRING_FORMAT_MEDIUM)
: format(date, DATE_STRING_FORMAT_MEDIUM);
default: return showPlaceholder
? DATE_STRING_FORMAT_LONG_PLACEHOLDER ? DATE_STRING_FORMAT_LONG_PLACEHOLDER
: timezone : DATE_STRING_FORMAT_SHORT_PLACEHOLDER;
? formatInTimeZone(date, timezone, DATE_STRING_FORMAT_LONG)
: format(date, DATE_STRING_FORMAT_LONG); switch (length) {
case 'tiny':
formatString = DATE_STRING_FORMAT_TINY;
placeholderString = DATE_STRING_FORMAT_TINY_PLACEHOLDER;
break;
case 'short':
formatString = DATE_STRING_FORMAT_SHORT;
placeholderString = DATE_STRING_FORMAT_SHORT_PLACEHOLDER;
break;
case 'medium':
formatString = !hideTime
? DATE_STRING_FORMAT_MEDIUM
: DATE_STRING_FORMAT_TINY;
placeholderString = !hideTime
? DATE_STRING_FORMAT_MEDIUM_PLACEHOLDER
: DATE_STRING_FORMAT_TINY_PLACEHOLDER;
break;
} }
return showPlaceholder
? placeholderString
: timezone
? formatInTimeZone(date, timezone, formatString)
: format(date, formatString);
}; };
export const formatDateFromPostgresString = (date: string, length?: Length) => export const formatDateFromPostgresString = (date: string, length?: Length) =>
formatDate(parse(date, DATE_STRING_FORMAT_POSTGRES, new Date()), length); formatDate({
date: parse(date, DATE_STRING_FORMAT_POSTGRES, new Date()),
length,
});
export const formatDateForPostgres = (date: Date) => export const formatDateForPostgres = (date: Date) =>
date.toISOString().replace( date.toISOString().replace(