mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: implement customised formatDate() function (#21423)
Closes #21301 This pull-request adds a very simple `function formatDate(date: Date, options: Intl.DateTimeFormatOptions)` function which will allow us to standardise the rendering of dates across the application. This is inline with the content of #21252.
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import type { FC } from "react";
|
||||
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts";
|
||||
import { formatDate } from "utils/time";
|
||||
|
||||
const chartConfig = {
|
||||
amount: {
|
||||
@@ -43,9 +44,13 @@ export const ActiveUserChart: FC<ActiveUserChartProps> = ({ data }) => {
|
||||
tickMargin={12}
|
||||
minTickGap={24}
|
||||
tickFormatter={(value: string) =>
|
||||
new Date(value).toLocaleDateString(undefined, {
|
||||
formatDate(new Date(value), {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
year: undefined,
|
||||
hour: undefined,
|
||||
minute: undefined,
|
||||
second: undefined,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { css, useTheme } from "@emotion/react";
|
||||
import { TableCell, TableRow } from "components/Table/Table";
|
||||
import type { FC } from "react";
|
||||
import { formatDate } from "utils/time";
|
||||
import { createDisplayDate } from "./utils";
|
||||
|
||||
export interface TimelineDateRow {
|
||||
@@ -27,7 +28,7 @@ export const TimelineDateRow: FC<TimelineDateRow> = ({ date }) => {
|
||||
color: theme.palette.text.secondary,
|
||||
textTransform: "capitalize",
|
||||
}}
|
||||
title={date.toLocaleDateString()}
|
||||
title={formatDate(date)}
|
||||
>
|
||||
{createDisplayDate(date)}
|
||||
</TableCell>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import dayjs from "dayjs";
|
||||
import calendar from "dayjs/plugin/calendar";
|
||||
import { formatDate } from "utils/time";
|
||||
|
||||
dayjs.extend(calendar);
|
||||
|
||||
@@ -16,5 +17,9 @@ export const createDisplayDate = (
|
||||
sameElse: "MM/DD/YYYY",
|
||||
});
|
||||
}
|
||||
return date.toLocaleDateString();
|
||||
return formatDate(date, {
|
||||
hour: undefined,
|
||||
minute: undefined,
|
||||
second: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -16,23 +16,13 @@ import {
|
||||
} from "lucide-react";
|
||||
import { type FC, Fragment, useState } from "react";
|
||||
import { cn } from "utils/cn";
|
||||
import { humanDuration } from "utils/time";
|
||||
import { formatDate, humanDuration } from "utils/time";
|
||||
import { AIBridgeProviderIcon } from "../AIBridgeProviderIcon";
|
||||
|
||||
type RequestLogsRowProps = {
|
||||
interception: AIBridgeInterception;
|
||||
};
|
||||
|
||||
const customisedDateLocale: Intl.DateTimeFormatOptions = {
|
||||
second: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour: "2-digit",
|
||||
day: "numeric",
|
||||
// Show the month as a short name
|
||||
month: "short",
|
||||
year: "numeric",
|
||||
};
|
||||
|
||||
type TokenUsageMetadataMerged =
|
||||
| null
|
||||
| Record<string, unknown>
|
||||
@@ -161,10 +151,7 @@ export const RequestLogsRow: FC<RequestLogsRowProps> = ({ interception }) => {
|
||||
<ChevronRightIcon className="size-icon-xs" />
|
||||
)}
|
||||
<span className="sr-only">({isOpen ? "Hide" : "Show more"})</span>
|
||||
{new Date(interception.started_at).toLocaleString(
|
||||
undefined,
|
||||
customisedDateLocale,
|
||||
)}
|
||||
{formatDate(new Date(interception.started_at))}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell className="w-48 max-w-48">
|
||||
@@ -268,20 +255,14 @@ export const RequestLogsRow: FC<RequestLogsRowProps> = ({ interception }) => {
|
||||
|
||||
<dt>Start Time:</dt>
|
||||
<dd data-chromatic="ignore">
|
||||
{new Date(interception.started_at).toLocaleString(
|
||||
undefined,
|
||||
customisedDateLocale,
|
||||
)}
|
||||
{formatDate(new Date(interception.started_at))}
|
||||
</dd>
|
||||
|
||||
{interception.ended_at && (
|
||||
<>
|
||||
<dt>End Time:</dt>
|
||||
<dd data-chromatic="ignore">
|
||||
{new Date(interception.ended_at).toLocaleString(
|
||||
undefined,
|
||||
customisedDateLocale,
|
||||
)}
|
||||
{formatDate(new Date(interception.ended_at))}
|
||||
</dd>
|
||||
</>
|
||||
)}
|
||||
|
||||
+6
-1
@@ -24,6 +24,7 @@ import {
|
||||
YAxis,
|
||||
} from "recharts";
|
||||
import { docs } from "utils/docs";
|
||||
import { formatDate } from "utils/time";
|
||||
|
||||
const chartConfig = {
|
||||
users: {
|
||||
@@ -153,9 +154,13 @@ export const LicenseSeatConsumptionChart: FC<
|
||||
tickMargin={12}
|
||||
minTickGap={24}
|
||||
tickFormatter={(value: string) =>
|
||||
new Date(value).toLocaleDateString(undefined, {
|
||||
formatDate(new Date(value), {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
year: undefined,
|
||||
hour: undefined,
|
||||
minute: undefined,
|
||||
second: undefined,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -16,6 +16,7 @@ import { ChevronRightIcon } from "lucide-react";
|
||||
import type { FC } from "react";
|
||||
import { Link as RouterLink } from "react-router";
|
||||
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts";
|
||||
import { formatDate } from "utils/time";
|
||||
|
||||
const chartConfig = {
|
||||
users: {
|
||||
@@ -105,9 +106,13 @@ export const UserEngagementChart: FC<UserEngagementChartProps> = ({ data }) => {
|
||||
tickMargin={12}
|
||||
minTickGap={24}
|
||||
tickFormatter={(value: string) =>
|
||||
new Date(value).toLocaleDateString(undefined, {
|
||||
formatDate(new Date(value), {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
year: undefined,
|
||||
hour: undefined,
|
||||
minute: undefined,
|
||||
second: undefined,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
} from "react";
|
||||
import { Link } from "react-router";
|
||||
import { cn } from "utils/cn";
|
||||
import { formatDate } from "utils/time";
|
||||
import { displayWorkspaceBuildDuration } from "utils/workspace";
|
||||
import { Sidebar, SidebarCaption, SidebarItem } from "./Sidebar";
|
||||
|
||||
@@ -118,7 +119,7 @@ export const WorkspaceBuildPageView: FC<WorkspaceBuildPageViewProps> = ({
|
||||
{displayWorkspaceBuildDuration(build)}
|
||||
</BuildStatsItem>
|
||||
<BuildStatsItem label="Started at">
|
||||
{new Date(build.created_at).toLocaleString()}
|
||||
{formatDate(new Date(build.created_at))}
|
||||
</BuildStatsItem>
|
||||
<BuildStatsItem label="Action">
|
||||
<span className="capitalize">{build.transition}</span>
|
||||
|
||||
@@ -17,6 +17,7 @@ import { type FC, useEffect, useState } from "react";
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
import { useQuery } from "react-query";
|
||||
import { formatDate } from "utils/time";
|
||||
import type { WorkspacePermissions } from "../../../modules/workspaces/permissions";
|
||||
import {
|
||||
NotificationActionButton,
|
||||
@@ -128,9 +129,9 @@ export const WorkspaceNotifications: FC<WorkspaceNotificationsProps> = ({
|
||||
const advancedSchedulingEnabled =
|
||||
entitlements.features.advanced_template_scheduling.enabled;
|
||||
if (advancedSchedulingEnabled && workspace.dormant_at) {
|
||||
const formatDate = (dateStr: string, timestamp: boolean): string => {
|
||||
const formatDateTime = (dateStr: string, timestamp: boolean): string => {
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleDateString(undefined, {
|
||||
return formatDate(date, {
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
@@ -150,17 +151,17 @@ export const WorkspaceNotifications: FC<WorkspaceNotificationsProps> = ({
|
||||
<>
|
||||
This workspace has not been used for{" "}
|
||||
{dayjs(workspace.last_used_at).fromNow(true)} and was marked dormant
|
||||
on {formatDate(workspace.dormant_at, false)}. It is scheduled to be
|
||||
deleted on {formatDate(workspace.deleting_at, true)}. To keep it you
|
||||
must activate the workspace.
|
||||
on {formatDateTime(workspace.dormant_at, false)}. It is scheduled to
|
||||
be deleted on {formatDateTime(workspace.deleting_at, true)}. To keep
|
||||
it you must activate the workspace.
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
This workspace has not been used for{" "}
|
||||
{dayjs(workspace.last_used_at).fromNow(true)} and was marked dormant
|
||||
on {formatDate(workspace.dormant_at, false)}. It is not scheduled for
|
||||
auto-deletion but will become a candidate if auto-deletion is enabled
|
||||
on this template. To keep it you must activate the workspace.
|
||||
on {formatDateTime(workspace.dormant_at, false)}. It is not scheduled
|
||||
for auto-deletion but will become a candidate if auto-deletion is
|
||||
enabled on this template. To keep it you must activate the workspace.
|
||||
</>
|
||||
),
|
||||
});
|
||||
|
||||
@@ -31,6 +31,7 @@ import type { FC } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
import { Link as RouterLink } from "react-router";
|
||||
import { displayDormantDeletion } from "utils/dormant";
|
||||
import { formatDate } from "utils/time";
|
||||
import type { WorkspacePermissions } from "../../modules/workspaces/permissions";
|
||||
import { WorkspaceActions } from "./WorkspaceActions/WorkspaceActions";
|
||||
import { WorkspaceNotifications } from "./WorkspaceNotifications/WorkspaceNotifications";
|
||||
@@ -201,9 +202,7 @@ export const WorkspaceTopbar: FC<WorkspaceProps> = ({
|
||||
css={{ color: "inherit" }}
|
||||
>
|
||||
{workspace.deleting_at ? (
|
||||
<>
|
||||
Deletion on {new Date(workspace.deleting_at).toLocaleString()}
|
||||
</>
|
||||
<>Deletion on {formatDate(new Date(workspace.deleting_at))}</>
|
||||
) : (
|
||||
"Deletion soon"
|
||||
)}
|
||||
|
||||
@@ -48,6 +48,25 @@ export function formatDateTime(
|
||||
return dayjs(date).format(format);
|
||||
}
|
||||
|
||||
const defaultDateLocaleOptions: Intl.DateTimeFormatOptions = {
|
||||
second: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour: "2-digit",
|
||||
day: "numeric",
|
||||
month: "short",
|
||||
year: "numeric",
|
||||
};
|
||||
|
||||
export function formatDate(
|
||||
date: Date,
|
||||
options?: { locale?: Intl.LocalesArgument } & Intl.DateTimeFormatOptions,
|
||||
) {
|
||||
return date.toLocaleDateString(options?.locale, {
|
||||
...defaultDateLocaleOptions,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
// Duration functions
|
||||
export function humanDuration(durationInMs: number) {
|
||||
return humanizeDuration(durationInMs, {
|
||||
|
||||
Reference in New Issue
Block a user