Skip to content
This repository was archived by the owner on Feb 10, 2025. It is now read-only.

Commit 26633f9

Browse files
committed
fix: consistent date time formats
1 parent e2b5c24 commit 26633f9

12 files changed

Lines changed: 139 additions & 170 deletions

File tree

apps/admin-ui/src/common/util.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { format, parseISO } from "date-fns";
1+
import { format, getDay, isSameDay, parseISO } from "date-fns";
22
import i18next from "i18next";
33
import { trim } from "lodash";
44
import type { LocationFieldsFragment } from "@gql/gql-types";
55
import { NUMBER_OF_DECIMALS } from "./const";
66
import type { TFunction } from "next-i18next";
7+
import { toMondayFirstUnsafe } from "common/src/helpers";
78

89
export { formatDuration } from "common/src/common/util";
910

@@ -12,22 +13,45 @@ export const DATE_FORMAT_SHORT = "d.M.";
1213

1314
/// @deprecated use format directly
1415
/// why convert date -> string -> date?
15-
export const formatDate = (
16+
export function formatDate(
1617
date: string | null,
1718
outputFormat = DATE_FORMAT
18-
): string | null => {
19+
): string | null {
1920
return date ? format(parseISO(date), outputFormat) : null;
20-
};
21+
}
2122

22-
export const formatTime = (
23+
export function formatTime(
2324
date: string | null,
2425
outputFormat = "HH:mm"
25-
): string | null => {
26+
): string | null {
2627
return date ? format(parseISO(date), outputFormat) : null;
27-
};
28+
}
29+
30+
// TODO why is this taking in a string?
31+
export function formatDateTime(date: string): string {
32+
return `${formatDate(date)} ${formatTime(date)}`;
33+
}
2834

29-
export const formatDateTime = (date: string): string =>
30-
`${formatDate(date)} ${formatTime(date)}`;
35+
// TODO move to common and combine with ui (requires i18n changes: replace messages.ts with json)
36+
export function formatDateTimeRange(
37+
t: TFunction,
38+
start: Date,
39+
end: Date
40+
): string {
41+
const startDay = t(`dayShort.${toMondayFirstUnsafe(getDay(start))}`);
42+
43+
if (isSameDay(start, end)) {
44+
return `${startDay} ${format(start, DATE_FORMAT)} ${format(
45+
start,
46+
"HH:mm"
47+
)}${format(end, "HH:mm")}`;
48+
}
49+
50+
return `${format(start, DATE_FORMAT)} ${format(start, "HH:mm")}${format(
51+
end,
52+
"HH:mm"
53+
)} ${format(end, "HH:mm")}`;
54+
}
3155

3256
export const formatNumber = (
3357
input?: number | null,

apps/admin-ui/src/component/reservations/EditTimeModal.tsx

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ import { useNotification } from "app/context/NotificationContext";
1919
import { useModal } from "app/context/ModalContext";
2020
import { TimeChangeFormSchemaRefined, TimeFormSchema } from "app/schemas";
2121
import ControlledTimeInput from "../my-units/components/ControlledTimeInput";
22-
import { reservationDateTime } from "./requested/util";
2322
import ControlledDateInput from "../my-units/components/ControlledDateInput";
2423
import { BufferToggles } from "../my-units/BufferToggles";
2524
import { useCheckCollisions } from "./requested/hooks";
2625
import { filterNonNullable } from "common/src/helpers";
2726
import { parseDateTimeSafe } from "@/helpers";
27+
import { formatDateTimeRange } from "@/common/util";
2828

2929
const StyledForm = styled.form`
3030
margin-top: var(--spacing-m);
@@ -88,13 +88,12 @@ const recurringReservationInfoText = ({
8888

8989
type FormValueType = z.infer<typeof TimeFormSchema>;
9090

91-
function formatDateInterval(begin: Date, end: Date, t: TFunction) {
92-
return `${reservationDateTime(
93-
begin,
94-
end,
95-
t
96-
)}, ${formatDuration(differenceInMinutes(end, begin), t)}`;
91+
function formatDateInterval(t: TFunction, begin: Date, end: Date) {
92+
const dateString = formatDateTimeRange(t, begin, end);
93+
const durationString = formatDuration(differenceInMinutes(end, begin), t);
94+
return `${dateString} (${durationString})`;
9795
}
96+
9897
const DialogContent = ({ reservation, onAccept, onClose }: Props) => {
9998
const { t, i18n } = useTranslation();
10099
const { notifyError, notifySuccess } = useNotification();
@@ -216,8 +215,8 @@ const DialogContent = ({ reservation, onAccept, onClose }: Props) => {
216215
const translateError = (errorMsg?: string) =>
217216
errorMsg ? t(`reservationForm:errors.${errorMsg}`) : "";
218217

219-
const newTimeString = start && end ? formatDateInterval(start, end, t) : "";
220-
const originalTime = formatDateInterval(startDateTime, endDateTime, t);
218+
const newTimeString = start && end ? formatDateInterval(t, start, end) : "";
219+
const originalTime = formatDateInterval(t, startDateTime, endDateTime);
221220

222221
return (
223222
<Dialog.Content>

apps/admin-ui/src/component/reservations/ReservationsTable.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { memoize } from "lodash";
55
import type { ReservationsQuery } from "@gql/gql-types";
66
import { truncate } from "@/helpers";
77
import { reservationUrl } from "@/common/urls";
8-
import { formatDateTime } from "@/common/util";
8+
import { formatDateTime, formatDateTimeRange } from "@/common/util";
99
import { CustomTable, TableLink } from "@/component/Table";
10-
import { getReserveeName, reservationDateTimeString } from "./requested/util";
10+
import { getReserveeName } from "./requested/util";
1111
import { MAX_NAME_LENGTH } from "@/common/const";
1212

1313
type ReservationTableColumn = {
@@ -70,7 +70,7 @@ const getColConfig = (t: TFunction): ReservationTableColumn[] => [
7070
key: "begin",
7171
isSortable: true,
7272
transform: ({ begin, end }: ReservationType) =>
73-
reservationDateTimeString(begin, end, t),
73+
formatDateTimeRange(t, new Date(begin), new Date(end)),
7474
},
7575
{
7676
headerName: t("Reservations.headings.createdAt"),

apps/admin-ui/src/component/reservations/requested/util.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ function constructReservation(
5757
pricings,
5858
} as ReservationUnitNode,
5959
],
60-
};
60+
} as ReservationNode;
6161
}
6262

6363
describe("pricingDetails", () => {
@@ -134,9 +134,9 @@ describe("createTag", () => {
134134
const mockT = ((x: string) => x) as TFunction;
135135
const tag = createTagString(res, mockT);
136136
expect(tag).toContain(
137-
"dayShort.0, dayShort.1, dayShort.3 12:00-14:00, common:abbreviations.hour"
137+
"dayShort.0, dayShort.1, dayShort.3 12:0014:00, common:abbreviations.hour"
138138
);
139-
expect(tag).toContain("1.4.2023-1.7.2023");
139+
expect(tag).toContain("1.4.20231.7.2023");
140140
expect(tag).toContain("Foobar");
141141
});
142142

@@ -151,7 +151,7 @@ describe("createTag", () => {
151151
const tag = createTagString(res, mockT);
152152
expect(tag).not.toContain("dayShort.0, dayShort.1, dayShort.3");
153153
expect(tag).toContain("1.4.2023");
154-
expect(tag).toContain("12:00-14:00, common:abbreviations.hour");
154+
expect(tag).toContain("12:0014:00, common:abbreviations.hour");
155155
expect(tag).toContain("dayShort.5");
156156
expect(tag).toContain("Foobar");
157157
});

apps/admin-ui/src/component/reservations/requested/util.ts

Lines changed: 14 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { differenceInMinutes, format, getDay, isSameDay } from "date-fns";
1+
import { differenceInMinutes, format } from "date-fns";
22
import type { TFunction } from "i18next";
33
import { trim } from "lodash";
44
import {
@@ -17,63 +17,19 @@ import {
1717
type ReservationQuery,
1818
} from "@gql/gql-types";
1919
import { formatDuration, fromApiDate } from "common/src/common/util";
20-
import { toMondayFirstUnsafe } from "common/src/helpers";
2120
import { truncate } from "@/helpers";
22-
import { DATE_FORMAT, formatDate, formatTime } from "@/common/util";
21+
import { formatDateTimeRange, formatDate } from "@/common/util";
2322

2423
type ReservationType = NonNullable<ReservationQuery["reservation"]>;
2524
type ReservationUnitType = NonNullable<ReservationType["reservationUnit"]>[0];
2625

27-
export function reservationDateTime(
28-
start: Date,
29-
end: Date,
30-
t: TFunction
31-
): string {
32-
const startDay = t(`dayShort.${toMondayFirstUnsafe(getDay(start))}`);
33-
34-
if (isSameDay(start, end)) {
35-
return `${startDay} ${format(start, DATE_FORMAT)} ${format(
36-
start,
37-
"HH:mm"
38-
)}-${format(end, "HH:mm")}`;
39-
}
40-
41-
return `${format(start, DATE_FORMAT)} ${format(start, "HH:mm")}-${format(
42-
end,
43-
"HH:mm"
44-
)} ${format(end, "HH:mm")}`;
45-
}
46-
47-
export function reservationDateTimeString(
48-
start: string,
49-
end: string,
50-
t: TFunction
51-
): string {
52-
const startDate = new Date(start);
53-
const endDate = new Date(end);
54-
55-
return reservationDateTime(startDate, endDate, t);
56-
}
57-
58-
function reservationDurationString(
59-
start: string,
60-
end: string,
61-
t: TFunction
62-
): string {
63-
const startDate = new Date(start);
64-
const endDate = new Date(end);
65-
66-
const durMinutes = differenceInMinutes(endDate, startDate);
67-
const abbreviated = true;
68-
return formatDuration(durMinutes, t, abbreviated);
69-
}
70-
71-
const reservationUnitName = (
26+
function reservationUnitName(
7227
reservationUnit: Maybe<ReservationUnitType>
73-
): string =>
74-
reservationUnit
28+
): string {
29+
return reservationUnit
7530
? `${reservationUnit.nameFi}, ${reservationUnit.unit?.nameFi || ""}`
7631
: "-";
32+
}
7733

7834
export function reservationPrice(
7935
reservation: ReservationType,
@@ -245,7 +201,7 @@ export function createTagString(
245201
t: TFunction
246202
): string {
247203
const createRecurringTag = (begin?: string, end?: string) =>
248-
begin && end ? `${formatDate(begin)}-${formatDate(end)}` : "";
204+
begin && end ? `${formatDate(begin)}${formatDate(end)}` : "";
249205

250206
const recurringTag = createRecurringTag(
251207
reservation.recurringReservation?.beginDate ?? undefined,
@@ -256,30 +212,20 @@ export function createTagString(
256212
?.map(reservationUnitName)
257213
.join(", ");
258214

259-
const singleDateTimeTag = reservationDateTimeString(
260-
reservation.begin,
261-
reservation.end,
262-
t
263-
);
215+
const begin = new Date(reservation.begin);
216+
const end = new Date(reservation.end);
217+
const singleDateTimeTag = formatDateTimeRange(t, begin, end);
264218

265219
const weekDayTag = reservation.recurringReservation?.weekdays
266220
?.sort()
267221
?.map((x) => t(`dayShort.${x}`))
268222
?.reduce((agv, x) => `${agv}${agv.length > 0 ? "," : ""} ${x}`, "");
269223

270-
const recurringDateTag =
271-
reservation.begin && reservation.end
272-
? `${weekDayTag} ${formatTime(reservation.begin, "HH:mm")}-${formatTime(
273-
reservation.end,
274-
"HH:mm"
275-
)}`
276-
: "";
224+
const recurringDateTag = `${weekDayTag} ${format(begin, "HH:mm")}${format(end, "HH:mm")}`;
277225

278-
const durationTag = reservationDurationString(
279-
reservation.begin,
280-
reservation.end,
281-
t
282-
);
226+
const durMinutes = differenceInMinutes(end, begin);
227+
const abbreviated = true;
228+
const durationTag = formatDuration(durMinutes, t, abbreviated);
283229

284230
const reservationTagline = `${
285231
reservation.recurringReservation ? recurringDateTag : singleDateTimeTag

apps/ui/components/reservation/ReservationCard.tsx

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import React, { useMemo } from "react";
1+
import React from "react";
22
import { IconGlyphEuro, IconCross, IconArrowRight } from "hds-react";
33
import { useTranslation } from "next-i18next";
4-
import { differenceInMinutes, parseISO } from "date-fns";
4+
import { differenceInMinutes } from "date-fns";
55
import styled from "styled-components";
66
import { getReservationPrice } from "common";
77
import { trim } from "lodash";
@@ -12,6 +12,7 @@ import {
1212
getMainImage,
1313
getTranslation,
1414
reservationsUrl,
15+
formatDateTimeRange,
1516
} from "@/modules/util";
1617
import {
1718
canUserCancelReservation,
@@ -159,29 +160,10 @@ function ReservationCard({ reservation, type }: PropsT): JSX.Element {
159160
const reservationUnit = reservation.reservationUnit?.[0] ?? undefined;
160161
const link = reservation.pk ? `/reservations/${reservation.pk}` : "";
161162

162-
const timeStripContent = useMemo(() => {
163-
const beginDate = t("common:dateWithWeekday", {
164-
date: reservation.begin && parseISO(reservation.begin),
165-
});
166-
167-
const beginTime = t("common:timeWithPrefix", {
168-
date: reservation.begin && parseISO(reservation.begin),
169-
});
170-
171-
const endDate = t("common:dateWithWeekday", {
172-
date: reservation.end && parseISO(reservation.end),
173-
});
174-
175-
const endTime = t("common:time", {
176-
date: reservation.end && parseISO(reservation.end),
177-
});
178-
179-
return capitalize(
180-
`${beginDate} ${beginTime} -${
181-
endDate !== beginDate ? endDate : ""
182-
} ${endTime}`
183-
);
184-
}, [reservation, t]);
163+
const { begin, end } = reservation;
164+
const timeString = capitalize(
165+
formatDateTimeRange(t, new Date(begin), new Date(end))
166+
);
185167

186168
const title = trim(
187169
`${getReservationUnitName(reservationUnit)}, ${getUnitName(
@@ -254,7 +236,7 @@ function ReservationCard({ reservation, type }: PropsT): JSX.Element {
254236
<Bottom>
255237
<Props>
256238
<TimeStrip data-testid="reservation-card__time">
257-
{timeStripContent}
239+
{timeString}
258240
</TimeStrip>
259241
<JustForMobile
260242
customBreakpoint={breakpoints.l}

0 commit comments

Comments
 (0)