mirror of
https://github.com/ente-io/ente.git
synced 2025-06-01 15:08:58 +00:00
105 lines
3.2 KiB
TypeScript
105 lines
3.2 KiB
TypeScript
/**
|
|
* @file Various date formatters.
|
|
*
|
|
* Note that we rely on the current behaviour of a full reload on changing the
|
|
* language. See: [Note: Changing locale causes a full reload].
|
|
*/
|
|
import i18n, { t } from "i18next";
|
|
|
|
const _dateFormat = new Intl.DateTimeFormat(i18n.language, {
|
|
weekday: "short",
|
|
day: "numeric",
|
|
month: "short",
|
|
year: "numeric",
|
|
});
|
|
|
|
const _dateWithoutYearFormat = new Intl.DateTimeFormat(i18n.language, {
|
|
weekday: "short",
|
|
day: "numeric",
|
|
month: "short",
|
|
});
|
|
|
|
const _timeFormat = new Intl.DateTimeFormat(i18n.language, {
|
|
timeStyle: "short",
|
|
});
|
|
|
|
/**
|
|
* Return a locale aware formatted date from the given {@link Date}.
|
|
*
|
|
* The behaviour depends upon whether the given {@link date} falls within the
|
|
* current calendar year.
|
|
*
|
|
* - For dates in the current year, year is omitted, e.g, "Fri, 21 Feb".
|
|
*
|
|
* - Otherwise, the year is included, e.g., "Fri, 21 Feb 2025".
|
|
*/
|
|
export const formattedDate = (date: Date) =>
|
|
(isSameYear(date) ? _dateWithoutYearFormat : _dateFormat).format(date);
|
|
|
|
const isSameYear = (date: Date) =>
|
|
new Date().getFullYear() === date.getFullYear();
|
|
|
|
/**
|
|
* Return a locale aware formatted time from the given {@link Date}.
|
|
*
|
|
* Example: "11:51 AM"
|
|
*/
|
|
export const formattedTime = (date: Date) => _timeFormat.format(date);
|
|
|
|
/**
|
|
* Return a locale aware formatted date and time from the given {@link Date},
|
|
* using the year omission behavior as documented in {@link formattedDate}.
|
|
*
|
|
* Example:
|
|
* - If within year: "Fri, 21 Feb at 11:51 AM".
|
|
* - Otherwise: "Fri, 21 Feb 2025 at 11:51 AM"
|
|
*
|
|
* @param dateOrEpochMicroseconds A JavaScript Date or a numeric epoch
|
|
* microseconds value.
|
|
*
|
|
* As a convenience, this function can be either be directly passed a JavaScript
|
|
* date, or it can be given the raw epoch microseconds value and it'll convert
|
|
* internally.
|
|
*
|
|
* See: [Note: Remote timestamps are epoch microseconds]
|
|
*/
|
|
export const formattedDateTime = (dateOrEpochMicroseconds: Date | number) =>
|
|
_formattedDateTime(toDate(dateOrEpochMicroseconds));
|
|
|
|
const _formattedDateTime = (date: Date) =>
|
|
[formattedDate(date), t("at"), formattedTime(date)].join(" ");
|
|
|
|
const toDate = (dm: Date | number) =>
|
|
typeof dm == "number" ? new Date(dm / 1000) : dm;
|
|
|
|
let _relativeTimeFormat: Intl.RelativeTimeFormat | undefined;
|
|
|
|
export const formattedDateRelative = (date: Date) => {
|
|
const units: [Intl.RelativeTimeFormatUnit, number][] = [
|
|
["year", 24 * 60 * 60 * 1000 * 365],
|
|
["month", (24 * 60 * 60 * 1000 * 365) / 12],
|
|
["day", 24 * 60 * 60 * 1000],
|
|
["hour", 60 * 60 * 1000],
|
|
["minute", 60 * 1000],
|
|
["second", 1000],
|
|
];
|
|
|
|
// Math.abs accounts for both past and future scenarios.
|
|
const elapsed = Math.abs(date.getTime() - Date.now());
|
|
|
|
// Lazily created, then cached, instance of RelativeTimeFormat.
|
|
const relativeTimeFormat = (_relativeTimeFormat ??=
|
|
new Intl.RelativeTimeFormat(i18n.language, {
|
|
localeMatcher: "best fit",
|
|
numeric: "always",
|
|
style: "short",
|
|
}));
|
|
|
|
for (const [u, d] of units) {
|
|
if (elapsed > d)
|
|
return relativeTimeFormat.format(Math.round(elapsed / d), u);
|
|
}
|
|
|
|
return relativeTimeFormat.format(Math.round(elapsed / 1000), "second");
|
|
};
|