diff --git a/app/components/BuildCard.tsx b/app/components/BuildCard.tsx index ae37a5669..26c9f6ed8 100644 --- a/app/components/BuildCard.tsx +++ b/app/components/BuildCard.tsx @@ -128,7 +128,7 @@ export function BuildCard({ build, owner, canEdit = false }: BuildProps) { {isHydrated ? formatDate(databaseTimestampToDate(updatedAt), { day: "numeric", - month: "long", + month: "numeric", year: "numeric", }) : "t"} diff --git a/app/components/Chart.tsx b/app/components/Chart.tsx index f83dd8cce..3fe9fc542 100644 --- a/app/components/Chart.tsx +++ b/app/components/Chart.tsx @@ -2,7 +2,6 @@ import clsx from "clsx"; import * as React from "react"; import { type AxisOptions, Chart as ReactChart } from "react-charts"; import type { TooltipRendererProps } from "react-charts/types/components/TooltipRenderer"; -import { useTranslation } from "react-i18next"; import { Theme, useTheme } from "~/features/theme/core/provider"; import { useHydrated } from "~/hooks/useHydrated"; import { useTimeFormat } from "~/hooks/useTimeFormat"; @@ -23,9 +22,9 @@ export default function Chart({ valueSuffix?: string; xAxis: "linear" | "localTime"; }) { - const { i18n } = useTranslation(); const theme = useTheme(); const isHydrated = useHydrated(); + const { formatDate } = useTimeFormat(); const primaryAxis = React.useMemo< AxisOptions<(typeof options)[number]["data"][number]> @@ -38,7 +37,7 @@ export default function Chart({ formatters: { scale: (val: any) => { if (val instanceof Date) { - return val.toLocaleDateString(i18n.language, { + return formatDate(val, { day: "numeric", month: "numeric", }); @@ -48,7 +47,7 @@ export default function Chart({ }, }, }), - [i18n.language, xAxis], + [formatDate, xAxis], ); const secondaryAxes = React.useMemo< @@ -117,7 +116,7 @@ function ChartTooltip({ return formatDate(primaryValue, { weekday: "short", day: "numeric", - month: "long", + month: "numeric", }); } diff --git a/app/components/EventsList.tsx b/app/components/EventsList.tsx index 693141947..a8b57200b 100644 --- a/app/components/EventsList.tsx +++ b/app/components/EventsList.tsx @@ -2,6 +2,7 @@ import { isToday, isTomorrow } from "date-fns"; import { useTranslation } from "react-i18next"; import type { SidebarEvent } from "~/features/sidebar/core/sidebar.server"; import { useHydrated } from "~/hooks/useHydrated"; +import { useTimeFormat } from "~/hooks/useTimeFormat"; import styles from "./EventsList.module.css"; import { Placeholder } from "./Placeholder"; import { ListLink } from "./SideNav"; @@ -14,6 +15,7 @@ export function EventsList({ onClick?: () => void; }) { const { t, i18n } = useTranslation(["front"]); + const { formatDate, formatTime } = useTimeFormat(); const isHydrated = useHydrated(); if (events.length === 0) { @@ -48,20 +50,13 @@ export function EventsList({ const str = rtf.format(1, "day"); return str.charAt(0).toUpperCase() + str.slice(1); } - return date.toLocaleDateString(i18n.language, { + return formatDate(date, { weekday: "long", - month: "short", + month: "numeric", day: "numeric", }); }; - const formatTime = (date: Date) => { - return date.toLocaleTimeString(i18n.language, { - hour: "numeric", - minute: "2-digit", - }); - }; - const groupedEvents = events.reduce>( (acc, event) => { const key = getDayKey(event.startTime); diff --git a/app/components/RelativeTime.tsx b/app/components/RelativeTime.tsx index d86ad3eeb..bbfff6edc 100644 --- a/app/components/RelativeTime.tsx +++ b/app/components/RelativeTime.tsx @@ -20,7 +20,7 @@ export function RelativeTime({ hour: "numeric", minute: "numeric", day: "numeric", - month: "long", + month: "numeric", timeZoneName: "short", }) : undefined diff --git a/app/components/StreamListItems.tsx b/app/components/StreamListItems.tsx index da3743d45..cc1917c71 100644 --- a/app/components/StreamListItems.tsx +++ b/app/components/StreamListItems.tsx @@ -5,8 +5,8 @@ import { useTranslation } from "react-i18next"; import { useFetcher } from "react-router"; import type { SidebarStream } from "~/features/core/streams/streams.server"; import { useHydrated } from "~/hooks/useHydrated"; -import type { LanguageCode } from "~/modules/i18n/config"; -import { databaseTimestampToDate, formatDistanceToNow } from "~/utils/dates"; +import { useTimeFormat } from "~/hooks/useTimeFormat"; +import { databaseTimestampToDate } from "~/utils/dates"; import { navIconUrl, tournamentRegisterPage } from "~/utils/urls"; import { Image } from "./Image"; import { ListLink } from "./SideNav"; @@ -25,14 +25,12 @@ export function StreamListItems({ savedTournamentIds?: number[]; }) { const { t, i18n } = useTranslation(["front"]); + const { formatDateTime, formatTime, formatDistanceToNow } = useTimeFormat(); const isHydrated = useHydrated(); const formatRelativeDate = (timestamp: number) => { const date = new Date(timestamp * 1000); - const timeStr = date.toLocaleTimeString(i18n.language, { - hour: "numeric", - minute: "2-digit", - }); + const timeStr = formatTime(date); if (isToday(date)) { const rtf = new Intl.RelativeTimeFormat(i18n.language, { @@ -49,8 +47,8 @@ export function StreamListItems({ return `${dayStr.charAt(0).toUpperCase() + dayStr.slice(1)}, ${timeStr}`; } - return date.toLocaleDateString(i18n.language, { - month: "short", + return formatDateTime(date, { + month: "numeric", day: "numeric", hour: "numeric", minute: "2-digit", @@ -101,7 +99,6 @@ export function StreamListItems({ ) : ( formatDistanceToNow(startsAtDate, { addSuffix: true, - language: i18n.language as LanguageCode, }) ) } diff --git a/app/components/TimePopover.tsx b/app/components/TimePopover.tsx index c309ced9d..259353792 100644 --- a/app/components/TimePopover.tsx +++ b/app/components/TimePopover.tsx @@ -16,7 +16,7 @@ export default function TimePopover({ minute: "numeric", hour: "numeric", day: "numeric", - month: "long", + month: "numeric", }, underline = true, className, diff --git a/app/components/elements/TournamentSearch.tsx b/app/components/elements/TournamentSearch.tsx index e6e3efde9..db4d6da1d 100644 --- a/app/components/elements/TournamentSearch.tsx +++ b/app/components/elements/TournamentSearch.tsx @@ -1,5 +1,5 @@ import clsx from "clsx"; -import { format, sub } from "date-fns"; +import { sub } from "date-fns"; import { ChevronsUpDown, Search, X } from "lucide-react"; import * as React from "react"; import { @@ -21,6 +21,7 @@ import { useDebounce } from "react-use"; import { SendouBottomTexts } from "~/components/elements/BottomTexts"; import { SendouLabel } from "~/components/elements/Label"; import type { TournamentSearchLoaderData } from "~/features/tournament/routes/to.search"; +import { useTimeFormat } from "~/hooks/useTimeFormat"; import { databaseTimestampToDate } from "~/utils/dates"; import selectStyles from "./Select.module.css"; @@ -150,6 +151,7 @@ function TournamentItem({ }; }) { const { t } = useTranslation(["common"]); + const { formatDate } = useTimeFormat(); if (typeof item.id === "string") { return ( @@ -167,7 +169,11 @@ function TournamentItem({ const additionalText = () => { const date = databaseTimestampToDate(item.startTime); - return format(date, "MMM d, yyyy"); + return formatDate(date, { + day: "numeric", + month: "numeric", + year: "numeric", + }); }; return ( @@ -185,11 +191,9 @@ function TournamentItem({
{item.name} - {additionalText() ? ( -
- {additionalText()} -
- ) : null} +
+ {additionalText()} +
); diff --git a/app/components/layout/ChatSidebar.tsx b/app/components/layout/ChatSidebar.tsx index f3826ecff..d5c314bb4 100644 --- a/app/components/layout/ChatSidebar.tsx +++ b/app/components/layout/ChatSidebar.tsx @@ -114,7 +114,7 @@ function RoomList({ onClose }: { onClose?: () => void }) { > {resolveDatePlaceholders(room.header, (d) => formatDateTime(d, { - month: "short", + month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", @@ -202,7 +202,7 @@ function ChatView({ onClose }: { onClose?: () => void }) { room?.header ?? t("common:chat.sidebar.title"), (d) => formatDateTime(d, { - month: "short", + month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", diff --git a/app/components/layout/index.tsx b/app/components/layout/index.tsx index 9474f3f75..19743203a 100644 --- a/app/components/layout/index.tsx +++ b/app/components/layout/index.tsx @@ -25,6 +25,7 @@ import { useUser } from "~/features/auth/core/user"; import { useChatContext } from "~/features/chat/useChatContext"; import { FriendMenu } from "~/features/friends/components/FriendMenu"; import { useHydrated } from "~/hooks/useHydrated"; +import { useTimeFormat } from "~/hooks/useTimeFormat"; import type { RootLoaderData } from "~/root"; import type { Breadcrumb, SendouRouteHandle } from "~/utils/remix.server"; import { @@ -55,12 +56,9 @@ import { TopRightButtons } from "./TopRightButtons"; const MAX_DESKTOP_FRIENDS = 4; -function useTimeFormat() { +function useRelativeDayFormat() { const { i18n } = useTranslation(); - - const formatTime = (date: Date, options: Intl.DateTimeFormatOptions) => { - return date.toLocaleTimeString(i18n.language, options); - }; + const { formatTime, formatDateTime } = useTimeFormat(); const formatRelativeDay = (daysFromToday: number) => { const rtf = new Intl.RelativeTimeFormat(i18n.language, { numeric: "auto" }); @@ -70,7 +68,7 @@ function useTimeFormat() { const formatRelativeDate = (timestamp: number) => { const date = new Date(timestamp * 1000); - const timeStr = formatTime(date, { hour: "numeric", minute: "2-digit" }); + const timeStr = formatTime(date); if (isToday(date)) { return `${formatRelativeDay(0)}, ${timeStr}`; @@ -79,15 +77,15 @@ function useTimeFormat() { return `${formatRelativeDay(1)}, ${timeStr}`; } - return date.toLocaleDateString(i18n.language, { - month: "short", + return formatDateTime(date, { + month: "numeric", day: "numeric", hour: "numeric", minute: "2-digit", }); }; - return { formatTime, formatRelativeDate }; + return { formatRelativeDate }; } function useBreadcrumbData() { @@ -215,7 +213,7 @@ export function Layout({ const setChatSidebarOpen = chatContext?.setChatOpen ?? (() => {}); const { t } = useTranslation(["front", "common"]); - const { formatRelativeDate } = useTimeFormat(); + const { formatRelativeDate } = useRelativeDayFormat(); const isHydrated = useHydrated(); const location = useLocation(); const headerRef = React.useRef(null); diff --git a/app/features/art/components/ArtGrid.tsx b/app/features/art/components/ArtGrid.tsx index 3031d13e5..379e0867a 100644 --- a/app/features/art/components/ArtGrid.tsx +++ b/app/features/art/components/ArtGrid.tsx @@ -95,7 +95,7 @@ function BigImageDialog({ close, art }: { close: () => void; art: ListedArt }) { >, "content"> & { slug: string; - dateString: string; } > = []; for (const file of files) { @@ -25,11 +24,6 @@ export async function mostRecentArticles(count: number) { articles.push({ date, slug: file.replace(".md", ""), - dateString: date.toLocaleDateString("en-US", { - day: "2-digit", - month: "long", - year: "numeric", - }), authors: normalizeAuthors(restParsed.author), title: restParsed.title, }); @@ -37,6 +31,5 @@ export async function mostRecentArticles(count: number) { return articles .sort((a, b) => b.date.getTime() - a.date.getTime()) - .slice(0, count) - .map(({ date: _date, ...rest }) => rest); + .slice(0, count); } diff --git a/app/features/articles/routes/a.$slug.tsx b/app/features/articles/routes/a.$slug.tsx index a8961c551..3ca9fa532 100644 --- a/app/features/articles/routes/a.$slug.tsx +++ b/app/features/articles/routes/a.$slug.tsx @@ -3,6 +3,7 @@ import type { MetaFunction } from "react-router"; import { Link, useLoaderData } from "react-router"; import { Main } from "~/components/Main"; import { Markdown } from "~/components/Markdown"; +import { useTimeFormat } from "~/hooks/useTimeFormat"; import invariant from "~/utils/invariant"; import type { SendouRouteHandle } from "~/utils/remix.server"; import { @@ -52,12 +53,20 @@ export const meta: MetaFunction = (args) => { export default function ArticlePage() { const data = useLoaderData(); + const { formatDate } = useTimeFormat(); return (

{data.title}

- by + by •{" "} +
{contentWithoutLeadingTitle(data.content, data.title)} diff --git a/app/features/articles/routes/a.tsx b/app/features/articles/routes/a.tsx index 510f33c7f..04f0f1259 100644 --- a/app/features/articles/routes/a.tsx +++ b/app/features/articles/routes/a.tsx @@ -2,6 +2,7 @@ import { useTranslation } from "react-i18next"; import type { MetaFunction } from "react-router"; import { Link, useLoaderData } from "react-router"; import { Main } from "~/components/Main"; +import { useTimeFormat } from "~/hooks/useTimeFormat"; import type { SendouRouteHandle } from "~/utils/remix.server"; import { ARTICLES_MAIN_PAGE, articlePage, navIconUrl } from "~/utils/urls"; import { metaTags } from "../../../utils/remix"; @@ -30,6 +31,7 @@ export const meta: MetaFunction = (args) => { export default function ArticlesMainPage() { const { t, i18n } = useTranslation(["common"]); + const { formatDate } = useTimeFormat(); const data = useLoaderData(); return ( @@ -46,7 +48,14 @@ export default function ArticlesMainPage() { style: "short", }).format(article.authors.map((a) => a.name)), })}{" "} - • + •{" "} + ))} diff --git a/app/features/ban/routes/suspended.tsx b/app/features/ban/routes/suspended.tsx index f0f1f7d6f..eda104133 100644 --- a/app/features/ban/routes/suspended.tsx +++ b/app/features/ban/routes/suspended.tsx @@ -25,7 +25,7 @@ export default function SuspendedPage() {
Ends:{" "} {formatDateTime(ends, { - month: "long", + month: "numeric", day: "numeric", year: "numeric", hour: "numeric", diff --git a/app/features/builds/components/FilterSection.tsx b/app/features/builds/components/FilterSection.tsx index fa4d7bfda..281a27471 100644 --- a/app/features/builds/components/FilterSection.tsx +++ b/app/features/builds/components/FilterSection.tsx @@ -236,7 +236,7 @@ function DateFilter({ {patch} ( {formatDate(date, { day: "numeric", - month: "long", + month: "numeric", year: "numeric", })} ) diff --git a/app/features/calendar/components/TournamentCard.tsx b/app/features/calendar/components/TournamentCard.tsx index 6682fa624..be39964c5 100644 --- a/app/features/calendar/components/TournamentCard.tsx +++ b/app/features/calendar/components/TournamentCard.tsx @@ -47,7 +47,7 @@ export function TournamentCard({ } return formatDateTimeSmartMinutes(date, { - month: "short", + month: "numeric", day: "numeric", hour: "numeric", weekday: "short", diff --git a/app/features/calendar/routes/calendar.$id.tsx b/app/features/calendar/routes/calendar.$id.tsx index 1ed6d362a..dbd6a1a13 100644 --- a/app/features/calendar/routes/calendar.$id.tsx +++ b/app/features/calendar/routes/calendar.$id.tsx @@ -105,7 +105,7 @@ export default function CalendarEventPage() { hour: "numeric", minute: "numeric", day: "numeric", - month: "long", + month: "numeric", weekday: "long", year: "numeric", }) diff --git a/app/features/calendar/routes/calendar.module.css b/app/features/calendar/routes/calendar.module.css index 5c531d916..45cd5539c 100644 --- a/app/features/calendar/routes/calendar.module.css +++ b/app/features/calendar/routes/calendar.module.css @@ -8,6 +8,7 @@ .navigateButtonsContainer { display: flex; + flex-wrap: wrap; gap: var(--s-4); width: 100%; } @@ -31,6 +32,9 @@ &:not(.navigateArrowButton) { justify-content: center; + flex-basis: 100%; + background-color: var(--color-bg); + min-height: 30px; } & svg { @@ -50,7 +54,26 @@ } } +.navigateArrowButtonRange { + font-variant-numeric: tabular-nums; + color: var(--color-text-high); + font-size: var(--font-2xs); +} + +@container (width >= 640px) { + .navigateArrowButton { + min-width: 7.25rem; + flex-basis: auto; + } + + .navigateButton:not(.navigateArrowButton) { + flex-basis: auto; + } +} + .navigateArrowButton { + gap: var(--s-1); + & svg { margin-inline-start: -5px; } diff --git a/app/features/calendar/routes/calendar.tsx b/app/features/calendar/routes/calendar.tsx index e811f0740..885475495 100644 --- a/app/features/calendar/routes/calendar.tsx +++ b/app/features/calendar/routes/calendar.tsx @@ -144,17 +144,16 @@ function NavigateButton({ daysInterval: ReturnType["shown"]; filters?: CalendarLoaderData["filters"]; }) { - const { formatDate } = useTimeFormat(); + const { formatDateRange } = useTimeFormat(); const lowestDate = daysInterval[0]; const highestDate = daysInterval[daysInterval.length - 1]; - const dateToString = ( - day: ReturnType["shown"][number], - ) => - formatDate(new Date(new Date().getFullYear(), day.month, day.day), { - day: "numeric", - month: "short", - }); + const year = new Date().getFullYear(); + const rangeString = formatDateRange( + new Date(year, lowestDate.month, lowestDate.day), + new Date(year, highestDate.month, highestDate.day), + { day: "numeric", month: "numeric" }, + ); return (
{children}
-
- {dateToString(lowestDate)} - {dateToString(highestDate)} -
+
{rangeString}
); @@ -264,7 +261,7 @@ function DayHeader(props: { date: number; month: number; year: number }) { > {formatDate(date, { day: "numeric", - month: "long", + month: "numeric", })}
{formatDate(date, { diff --git a/app/features/friends/components/FriendMenu.tsx b/app/features/friends/components/FriendMenu.tsx index 5de8280eb..1402cb141 100644 --- a/app/features/friends/components/FriendMenu.tsx +++ b/app/features/friends/components/FriendMenu.tsx @@ -11,6 +11,7 @@ import { } from "~/components/elements/Menu"; import { ListButton } from "~/components/SideNav"; import { SENDOUQ_ACTIVITY_LABEL } from "~/features/friends/friends-constants"; +import { useTimeFormat } from "~/hooks/useTimeFormat"; import { databaseTimestampToDate } from "~/utils/dates"; import { SENDOUQ_LOOKING_PAGE, tournamentSubsPage } from "~/utils/urls"; @@ -38,12 +39,17 @@ export function FriendMenu({ onNavigate?: () => void; }) { const { t } = useTranslation(["common", "friends"]); + const { formatDate } = useTimeFormat(); const fetcher = useFetcher(); const [confirmOpen, setConfirmOpen] = React.useState(false); const friendSinceText = friendshipCreatedAt ? t("friends:friendsList.friendSince", { - date: databaseTimestampToDate(friendshipCreatedAt).toLocaleDateString(), + date: formatDate(databaseTimestampToDate(friendshipCreatedAt), { + day: "numeric", + month: "numeric", + year: "numeric", + }), }) : null; diff --git a/app/features/front-page/routes/index.tsx b/app/features/front-page/routes/index.tsx index a4f1b672e..d180a7bc5 100644 --- a/app/features/front-page/routes/index.tsx +++ b/app/features/front-page/routes/index.tsx @@ -71,8 +71,8 @@ function SeasonDates({ return isHydrated ? (
- {formatDate(season.starts, { month: "long", day: "numeric" })} -{" "} - {formatDate(season.ends, { month: "long", day: "numeric" })} + {formatDate(season.starts, { month: "numeric", day: "numeric" })} -{" "} + {formatDate(season.ends, { month: "numeric", day: "numeric" })}
) : (
X
diff --git a/app/features/lfg/components/LFGPost.tsx b/app/features/lfg/components/LFGPost.tsx index 4754c9867..657fbcfd1 100644 --- a/app/features/lfg/components/LFGPost.tsx +++ b/app/features/lfg/components/LFGPost.tsx @@ -272,7 +272,7 @@ function PostTime({ return (
{formatDate(createdAtDate, { - month: "long", + month: "numeric", day: "numeric", })}{" "} {overDayDifferenceBetween ? ( diff --git a/app/features/plus-voting/routes/plus.voting.results.tsx b/app/features/plus-voting/routes/plus.voting.results.tsx index 4ed84bab6..d911a8e4b 100644 --- a/app/features/plus-voting/routes/plus.voting.results.tsx +++ b/app/features/plus-voting/routes/plus.voting.results.tsx @@ -1,6 +1,7 @@ import clsx from "clsx"; import type { MetaFunction } from "react-router"; import { Link, useLoaderData } from "react-router"; +import { useTimeFormat } from "~/hooks/useTimeFormat"; import { metaTags, type SerializeFrom } from "~/utils/remix"; import { PLUS_SERVER_DISCORD_URL, userPage } from "~/utils/urls"; @@ -21,12 +22,22 @@ export const meta: MetaFunction = (args) => { export default function PlusVotingResultsPage() { const data = useLoaderData(); + const { formatDate } = useTimeFormat(); return (

- Voting results for {data.lastCompletedVoting.month + 1}/ - {data.lastCompletedVoting.year} + Voting results for{" "} + {formatDate( + new Date( + data.lastCompletedVoting.year, + data.lastCompletedVoting.month, + ), + { + month: "numeric", + year: "numeric", + }, + )}

{data.ownScores && data.ownScores.length > 0 ? ( <> diff --git a/app/features/scrims/components/ScrimCard.tsx b/app/features/scrims/components/ScrimCard.tsx index 9b02e5d9e..d73c334dc 100644 --- a/app/features/scrims/components/ScrimCard.tsx +++ b/app/features/scrims/components/ScrimCard.tsx @@ -405,7 +405,7 @@ function ScrimActionButtons({ hour: "numeric", minute: "2-digit", day: "numeric", - month: "long", + month: "numeric", })}
diff --git a/app/features/scrims/routes/scrims.$id.tsx b/app/features/scrims/routes/scrims.$id.tsx index 661e48977..e314648af 100644 --- a/app/features/scrims/routes/scrims.$id.tsx +++ b/app/features/scrims/routes/scrims.$id.tsx @@ -139,7 +139,7 @@ function ScrimHeader() { options={{ weekday: "long", year: "numeric", - month: "long", + month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", diff --git a/app/features/scrims/routes/scrims.tsx b/app/features/scrims/routes/scrims.tsx index 17ec84c37..d27a4e1d9 100644 --- a/app/features/scrims/routes/scrims.tsx +++ b/app/features/scrims/routes/scrims.tsx @@ -216,7 +216,7 @@ function ScrimsDaySection({

{formatDate(databaseTimestampToDate(posts[0].at), { day: "numeric", - month: "long", + month: "numeric", weekday: "long", })}

@@ -342,7 +342,7 @@ function ScrimsDaySeparatedOwnedCards({ posts }: { posts: ScrimPost[] }) {

{formatDate(databaseTimestampToDate(posts![0].at), { day: "numeric", - month: "long", + month: "numeric", weekday: "long", })}

@@ -411,7 +411,7 @@ function ScrimsDaySeparatedBookedCards({ posts }: { posts: ScrimPost[] }) {

{formatDate(databaseTimestampToDate(posts![0].at), { day: "numeric", - month: "long", + month: "numeric", weekday: "long", })}

diff --git a/app/features/sendouq/components/GroupCard.tsx b/app/features/sendouq/components/GroupCard.tsx index 0d1fcc02b..a2a7e9942 100644 --- a/app/features/sendouq/components/GroupCard.tsx +++ b/app/features/sendouq/components/GroupCard.tsx @@ -320,7 +320,7 @@ function GroupMember({ hour: "numeric", minute: "numeric", day: "numeric", - month: "long", + month: "numeric", year: "numeric", }, )} diff --git a/app/features/sendouq/routes/q.tsx b/app/features/sendouq/routes/q.tsx index a23ac42f0..536cdc547 100644 --- a/app/features/sendouq/routes/q.tsx +++ b/app/features/sendouq/routes/q.tsx @@ -18,6 +18,7 @@ import { useUser } from "~/features/auth/core/user"; import type * as Seasons from "~/features/mmr/core/Seasons"; import { useAutoRerender } from "~/hooks/useAutoRerender"; import { useHydrated } from "~/hooks/useHydrated"; +import { useTimeFormat } from "~/hooks/useTimeFormat"; import { useHasRole } from "~/modules/permissions/hooks"; import { metaTags, type SerializeFrom } from "~/utils/remix"; import type { SendouRouteHandle } from "~/utils/remix.server"; @@ -64,6 +65,7 @@ export const meta: MetaFunction = (args) => { export default function QPage() { const { t } = useTranslation(["q"]); + const { formatDateTime } = useTimeFormat(); const [dialogOpen, setDialogOpen] = React.useState(true); const user = useUser(); const data = useLoaderData(); @@ -132,9 +134,9 @@ export default function QPage() { > As a fresh account please wait before joining the queue. You can join{" "} - {queueJoinStatus.toLocaleString("en-US", { + {formatDateTime(queueJoinStatus, { day: "numeric", - month: "long", + month: "numeric", hour: "numeric", minute: "numeric", })} @@ -184,37 +186,16 @@ const countries = [ { id: 3, countryCode: "FR", timeZone: "Europe/Paris", city: "paris" }, { id: 4, countryCode: "JP", timeZone: "Asia/Tokyo", city: "tokyo" }, ] as const; -const weekdayFormatter = ({ - timeZone, - locale, -}: { - timeZone: string; - locale: string; -}) => - new Intl.DateTimeFormat([locale], { - timeZone, - weekday: "long", - }); -const clockFormatter = ({ - timeZone, - locale, -}: { - timeZone: string; - locale: string; -}) => - new Intl.DateTimeFormat([locale], { - timeZone, - hour: "numeric", - minute: "numeric", - }); function Clocks() { const isHydrated = useHydrated(); - const { t, i18n } = useTranslation(["q"]); + const { t } = useTranslation(["q"]); + const { formatDate, formatTime } = useTimeFormat(); useAutoRerender(); return (
{countries.map((country) => { + const now = new Date(); return (
@@ -223,19 +204,20 @@ function Clocks() {
{isHydrated - ? weekdayFormatter({ + ? formatDate(now, { timeZone: country.timeZone, - locale: i18n.language, - }).format(new Date()) + weekday: "long", + }) : // take space "Monday"}
{isHydrated - ? clockFormatter({ + ? formatTime(now, { timeZone: country.timeZone, - locale: i18n.language, - }).format(new Date()) + hour: "numeric", + minute: "numeric", + }) : // take space "0:00 PM"}
@@ -293,15 +275,16 @@ function ActiveSeasonInfo({ }: { season: SerializeFrom; }) { - const { t, i18n } = useTranslation(["q"]); + const { t } = useTranslation(["q"]); + const { formatDateTime } = useTimeFormat(); const isHydrated = useHydrated(); const starts = new Date(season.starts); const ends = new Date(season.ends); const dateToString = (date: Date) => - date.toLocaleString(i18n.language, { - month: "short", + formatDateTime(date, { + month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", @@ -401,14 +384,15 @@ function UpcomingSeasonInfo({ season: SerializeFrom; }) { const { t } = useTranslation(["q"]); + const { formatDateTime } = useTimeFormat(); const isHydrated = useHydrated(); if (!isHydrated) return null; const starts = new Date(season.starts); const dateToString = (date: Date) => - date.toLocaleString("en-US", { - month: "long", + formatDateTime(date, { + month: "numeric", day: "numeric", hour: "numeric", }); diff --git a/app/features/team/components/TeamResultsTable.tsx b/app/features/team/components/TeamResultsTable.tsx index 70a506b0b..0d2223b2d 100644 --- a/app/features/team/components/TeamResultsTable.tsx +++ b/app/features/team/components/TeamResultsTable.tsx @@ -47,7 +47,7 @@ export function TeamResultsTable({ results }: TeamResultsTableProps) { {formatDate(databaseTimestampToDate(result.startTime), { day: "numeric", - month: "short", + month: "numeric", year: "numeric", })} diff --git a/app/features/team/routes/t.$customUrl.results.tsx b/app/features/team/routes/t.$customUrl.results.tsx index 3529b36ca..35008b2f1 100644 --- a/app/features/team/routes/t.$customUrl.results.tsx +++ b/app/features/team/routes/t.$customUrl.results.tsx @@ -6,6 +6,10 @@ import { loader } from "../loaders/t.$customUrl.results.server"; export { loader }; +export const handle = { + i18n: ["user"], +}; + export default function TeamResultsPage() { const data = useLoaderData(); diff --git a/app/features/top-search/routes/xsearch.tsx b/app/features/top-search/routes/xsearch.tsx index 1dba5c76f..0b0e97ec7 100644 --- a/app/features/top-search/routes/xsearch.tsx +++ b/app/features/top-search/routes/xsearch.tsx @@ -4,6 +4,7 @@ import type { MetaFunction } from "react-router"; import { useLoaderData, useSearchParams } from "react-router"; import { Main } from "~/components/Main"; import type { Tables } from "~/db/tables"; +import { useTimeFormat } from "~/hooks/useTimeFormat"; import { rankedModesShort } from "~/modules/in-game-lists/modes"; import type { RankedModeShort } from "~/modules/in-game-lists/types"; import invariant from "~/utils/invariant"; @@ -37,6 +38,7 @@ export const meta: MetaFunction = (args) => { export default function XSearchPage() { const [searchParams, setSearchParams] = useSearchParams(); const { t } = useTranslation(["common", "game-misc"]); + const { formatDate } = useTimeFormat(); const data = useLoaderData(); const handleSelectChange = (event: React.ChangeEvent) => { @@ -60,6 +62,12 @@ export default function XSearchPage() { searchParams.get("mode") ?? "SZ" }-${searchParams.get("region") ?? "WEST"}`; + const formatMonthYear = (my: MonthYear) => + formatDate(new Date(my.year, my.month - 1), { + month: "numeric", + year: "numeric", + }); + return (