fix: format date
This commit is contained in:
parent
fb071ff650
commit
0703704307
4 changed files with 104 additions and 6 deletions
|
|
@ -29,6 +29,17 @@ export const EventWithEnd: Story = {
|
|||
},
|
||||
}
|
||||
|
||||
export const MultiDayEvent: Story = {
|
||||
args: {
|
||||
date: '2026-07-24T18:00:00+02:00',
|
||||
end: '2026-07-26T14:00:00+02:00',
|
||||
title: 'Gemeindewochenende',
|
||||
href: 'https://www.link_to_event.com',
|
||||
location: "St. Clara",
|
||||
cancelled: false,
|
||||
},
|
||||
}
|
||||
|
||||
export const EventInMarch: Story = {
|
||||
args: {
|
||||
date: '2024-03-24T15:00:00+01:00',
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useEffect, useMemo, useState } from 'react'
|
|||
import styles from "./styles.module.scss"
|
||||
import classNames from 'classnames'
|
||||
import Link from 'next/link'
|
||||
import { formatEventDate } from '@/utils/formatEventDate'
|
||||
|
||||
export type EventRowProps = {
|
||||
/** datetime 8601 format */
|
||||
|
|
@ -41,8 +42,6 @@ const shortMonth = (date: string) => {
|
|||
return months[month - 1];
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const EventRow = ({date, end, title, location, cancelled, href, color = "base", showDate = true}: EventRowProps) => {
|
||||
const day = useMemo(() => date.substring(8, 10), [date]);
|
||||
const dateObj = useMemo(() => new Date(date), [date]);
|
||||
|
|
@ -80,10 +79,7 @@ export const EventRow = ({date, end, title, location, cancelled, href, color = "
|
|||
<span className={classNames({ [styles.cancelled]: cancelled })}>
|
||||
{ showDate &&
|
||||
<>
|
||||
{dateObj.toLocaleDateString("de-DE", { weekday: "long" })}
|
||||
{dayFormat === "long" && " " + dateObj.toLocaleDateString("de-DE", { dateStyle: "short" })}, {dateObj.toLocaleTimeString("de-DE", { timeStyle: "short", timeZone: "Europe/Berlin" })}
|
||||
{ endObj ? <> - {endObj.toLocaleTimeString("de-DE", { timeStyle: "short", timeZone: "Europe/Berlin" })}</> : "" }
|
||||
Uhr
|
||||
{formatEventDate(dateObj, endObj, dayFormat)}
|
||||
<br />
|
||||
</>
|
||||
}
|
||||
|
|
|
|||
52
src/utils/formatEventDate.test.ts
Normal file
52
src/utils/formatEventDate.test.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { describe, expect, test } from 'vitest'
|
||||
import { formatEventDate } from './formatEventDate'
|
||||
|
||||
// Fri 24.07.2026 18:00 Berlin time
|
||||
const start = new Date('2026-07-24T18:00:00+02:00')
|
||||
const endSameDay = new Date('2026-07-24T20:00:00+02:00')
|
||||
const endNextDay = new Date('2026-07-25T01:00:00+02:00')
|
||||
const endSunday = new Date('2026-07-26T14:00:00+02:00')
|
||||
|
||||
describe('format "long"', () => {
|
||||
test('start only', () => {
|
||||
expect(formatEventDate(start)).toBe(`Freitag 24.07.26, 18:00 Uhr`)
|
||||
})
|
||||
|
||||
test('same-day end renders as time range', () => {
|
||||
expect(formatEventDate(start, endSameDay)).toBe(`Freitag 24.07.26, 18:00 - 20:00 Uhr`)
|
||||
})
|
||||
|
||||
test('multi-day end renders both dates', () => {
|
||||
expect(formatEventDate(start, endSunday)).toBe(
|
||||
`Fr. 24.07. 18:00 Uhr - So. 26.07. 14:00 Uhr`,
|
||||
)
|
||||
})
|
||||
|
||||
test('end after midnight counts as multi-day', () => {
|
||||
expect(formatEventDate(start, endNextDay)).toBe(
|
||||
`Fr. 24.07. 18:00 Uhr - Sa. 25.07. 01:00 Uhr`,
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('format "short"', () => {
|
||||
test('start only', () => {
|
||||
expect(formatEventDate(start, undefined, 'short')).toBe(`Freitag, 18:00 Uhr`)
|
||||
})
|
||||
|
||||
test('same-day end renders as time range without date', () => {
|
||||
expect(formatEventDate(start, endSameDay, 'short')).toBe(`Freitag, 18:00 - 20:00 Uhr`)
|
||||
})
|
||||
|
||||
test('multi-day end renders compact weekday range', () => {
|
||||
expect(formatEventDate(start, endSunday, 'short')).toBe(`Fr 18:00 - So 14:00 Uhr`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('timezone handling', () => {
|
||||
test('calendar day is compared in Europe/Berlin, not UTC', () => {
|
||||
const lateStart = new Date('2026-07-24T22:30:00Z')
|
||||
const earlyEnd = new Date('2026-07-25T01:00:00Z')
|
||||
expect(formatEventDate(lateStart, earlyEnd)).toBe(`Samstag 25.07.26, 00:30 - 03:00 Uhr`)
|
||||
})
|
||||
})
|
||||
39
src/utils/formatEventDate.ts
Normal file
39
src/utils/formatEventDate.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* Format an event's date and time range in a user-friendly way.
|
||||
*
|
||||
* format "long" (default):
|
||||
* - same day: "Freitag 24.07.26, 18:00 - 20:00 Uhr"
|
||||
* - multi-day: "Fr. 24.07. 18:00 Uhr - So. 26.07. 14:00 Uhr"
|
||||
*
|
||||
* format "short" (narrow screens):
|
||||
* - same day: "Freitag, 18:00 - 20:00 Uhr"
|
||||
* - multi-day: "Fr 18:00 - So 14:00 Uhr"
|
||||
*/
|
||||
export const formatEventDate = (start: Date, end?: Date, format: "long" | "short" = "long"): string => {
|
||||
const time = (d: Date) => d.toLocaleTimeString("de-DE", { timeStyle: "short", timeZone: "Europe/Berlin" })
|
||||
const calendarDay = (d: Date) => d.toLocaleDateString("de-DE", { timeZone: "Europe/Berlin" })
|
||||
const weekday = (d: Date, length: "long" | "short") =>
|
||||
d.toLocaleDateString("de-DE", { weekday: length, timeZone: "Europe/Berlin" })
|
||||
const isMultiDay = end && calendarDay(start) !== calendarDay(end)
|
||||
|
||||
switch (format) {
|
||||
case "short": {
|
||||
if (end && isMultiDay) {
|
||||
return `${weekday(start, "short")} ${time(start)} - ${weekday(end, "short")} ${time(end)} Uhr`
|
||||
}
|
||||
const endTime = end ? ` - ${time(end)}` : ""
|
||||
return `${weekday(start, "long")}, ${time(start)}${endTime} Uhr`
|
||||
}
|
||||
|
||||
case "long": {
|
||||
if (end && isMultiDay) {
|
||||
// "Fr. 24.07." — assembled manually because Intl puts a comma between weekday and date
|
||||
const dayDate = (d: Date) =>
|
||||
`${weekday(d, "short")}. ${d.toLocaleDateString("de-DE", { day: "2-digit", month: "2-digit", timeZone: "Europe/Berlin" })}`
|
||||
return `${dayDate(start)} ${time(start)} Uhr - ${dayDate(end)} ${time(end)} Uhr`
|
||||
}
|
||||
const endTime = end ? ` - ${time(end)}` : ""
|
||||
return `${weekday(start, "long")} ${start.toLocaleDateString("de-DE", { dateStyle: "short", timeZone: "Europe/Berlin" })}, ${time(start)}${endTime} Uhr`
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue