From 5fd8d0ac233bf7bd22aa14d61c162d1c13b44f4e Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Sat, 14 May 2022 11:27:32 -0700 Subject: [PATCH] fix: invalid date strings (#2145) * fix: invalid date strings Fixes #2113 * fix: polls without expiry date Fixes #2112 --- src/routes/_components/status/StatusPoll.html | 3 +- src/routes/_intl/formatTimeagoDate.js | 6 ++++ src/routes/_utils/formatters.js | 32 +++++++++++++++---- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/routes/_components/status/StatusPoll.html b/src/routes/_components/status/StatusPoll.html index aebf7d9e..839e944b 100644 --- a/src/routes/_components/status/StatusPoll.html +++ b/src/routes/_components/status/StatusPoll.html @@ -306,7 +306,8 @@ multiple: ({ poll }) => poll.multiple, expired: ({ poll }) => poll.expired, expiresAt: ({ poll }) => poll.expires_at, - expiresAtTS: ({ expiresAt }) => new Date(expiresAt).getTime(), + // Misskey can have polls that never end. These give expiresAt as null + expiresAtTS: ({ expiresAt }) => typeof expiresAt === 'number' ? new Date(expiresAt).getTime() : null, expiresAtTimeagoFormatted: ({ expiresAtTS, expired, $now }) => ( expired ? formatTimeagoDate(expiresAtTS, $now) : formatTimeagoFutureDate(expiresAtTS, $now) ), diff --git a/src/routes/_intl/formatTimeagoDate.js b/src/routes/_intl/formatTimeagoDate.js index fbcbae42..b92026cc 100644 --- a/src/routes/_intl/formatTimeagoDate.js +++ b/src/routes/_intl/formatTimeagoDate.js @@ -3,6 +3,9 @@ import { mark, stop } from '../_utils/marks.js' // Format a date in the past export function formatTimeagoDate (date, now) { + if (typeof date !== 'number') { // means "never" in Misskey + return 'intl.never' + } mark('formatTimeagoDate') // use Math.min() to avoid things like "in 10 seconds" when the timestamps are slightly off const res = format(Math.min(0, date - now)) @@ -12,6 +15,9 @@ export function formatTimeagoDate (date, now) { // Format a date in the future export function formatTimeagoFutureDate (date, now) { + if (typeof date !== 'number') { // means "never" in Misskey + return 'intl.never' + } mark('formatTimeagoFutureDate') // use Math.max() for same reason as above const res = format(Math.max(0, date - now)) diff --git a/src/routes/_utils/formatters.js b/src/routes/_utils/formatters.js index 49ea0df3..c8f07cfa 100644 --- a/src/routes/_utils/formatters.js +++ b/src/routes/_utils/formatters.js @@ -1,24 +1,44 @@ import { LOCALE } from '../_static/intl.js' import { thunk } from './thunk.js' -export const absoluteDateFormatter = thunk(() => new Intl.DateTimeFormat(LOCALE, { +const safeFormatter = (formatter) => { + return { + format (date) { + if (typeof date !== 'number') { + return 'intl.never' // null means "never" in Misskey + } + try { + return formatter.format(date) + } catch (e) { + if (e instanceof RangeError) { + // The fediverse is wild, so invalid dates may exist. Don't fail with a fatal error in that case. + // https://github.com/nolanlawson/pinafore/issues/2113 + return 'intl.never' + } + throw e + } + } + } +} + +export const absoluteDateFormatter = thunk(() => safeFormatter(new Intl.DateTimeFormat(LOCALE, { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' -})) +}))) -export const shortAbsoluteDateFormatter = thunk(() => new Intl.DateTimeFormat(LOCALE, { +export const shortAbsoluteDateFormatter = thunk(() => safeFormatter(new Intl.DateTimeFormat(LOCALE, { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' -})) +}))) -export const dayOnlyAbsoluteDateFormatter = thunk(() => new Intl.DateTimeFormat(LOCALE, { +export const dayOnlyAbsoluteDateFormatter = thunk(() => safeFormatter(new Intl.DateTimeFormat(LOCALE, { year: 'numeric', month: 'short', day: 'numeric' -})) +})))