crawlnicle/frontend/js/localTimeController.ts

88 lines
3.2 KiB
TypeScript

// This file is used to convert UTC timestamps from the server to human-readable local time in the browser.
//
// Usage: Add a time element with a `datetime` attribute and a `data-local-time` attribute to the HTML.
//
// `data-local-time` can be either 'relative' or 'date'.
// 'relative' will show the time in a human-readable format, e.g. "2 hours from now".
// 'date' will show the date in a human-readable format, e.g. "January 1, 2022".
// Any other value will be ignored and time will be shown as-is from the server as a UTC timestamp.
function pluralize(count: number, singular: string, plural: string): string {
return count === 1 ? singular : plural;
}
function formatRelativeTime(utcTime: Date): string {
const now = new Date();
const diffInSeconds = (utcTime.getTime() - now.getTime()) / 1000;
if (diffInSeconds < -86400) {
const days = Math.round(Math.abs(diffInSeconds) / 86400);
return `${days} ${pluralize(days, 'day', 'days')} ago`;
} else if (diffInSeconds < -3600) {
const hours = Math.round(Math.abs(diffInSeconds) / 3600);
return `${hours} ${pluralize(Math.abs(hours), 'hour', 'hours')} ago`;
} else if (diffInSeconds < -60) {
const minutes = Math.round(Math.abs(diffInSeconds) / 60);
return `${minutes} ${pluralize(minutes, 'minute', 'minutes')} ago`;
} else if (diffInSeconds < 0) {
const seconds = Math.abs(diffInSeconds);
return `${seconds} ${pluralize(seconds, 'second', 'seconds')} ago`;
} else if (diffInSeconds < 60) {
return `${diffInSeconds} ${pluralize(
diffInSeconds,
'second',
'seconds'
)} from now`;
} else if (diffInSeconds < 3600) {
const minutes = Math.round(diffInSeconds / 60);
return `${minutes} ${pluralize(minutes, 'minute', 'minutes')} from now`;
} else if (diffInSeconds < 86400) {
const hours = Math.round(diffInSeconds / 3600);
return `${hours} ${pluralize(hours, 'hour', 'hours')} from now`;
} else {
const days = Math.round(diffInSeconds / 86400);
return `${days} ${pluralize(days, 'day', 'days')} from now`;
}
}
function convertTimeElements(): void {
const timeElements = document.querySelectorAll('time[data-local-time]');
timeElements.forEach((element) => {
const utcString = element.getAttribute('datetime');
if (utcString !== null) {
const utcTime = new Date(utcString);
const localTimeType = element.getAttribute('data-local-time');
if (localTimeType === 'relative') {
element.textContent = formatRelativeTime(utcTime);
} else if (localTimeType === 'date') {
element.textContent = utcTime.toLocaleDateString(
window.navigator.language,
{
year: 'numeric',
month: 'long',
day: 'numeric',
}
);
} else {
console.error(
'Unrecognized data-local-time attribute value on time[data-local-time] element. Local time cannot be displayed.',
element
);
}
} else {
console.error(
'Missing datetime attribute on time[data-local-time] element',
element
);
}
});
}
document.addEventListener('DOMContentLoaded', function () {
convertTimeElements();
});
document.body.addEventListener('htmx:afterSwap', function () {
convertTimeElements();
});