Skip to content

Commit 1294ba4

Browse files
idityaGEarkid15rkasya
authored
issue#1034: Enhance date formatting in Home component to support date ranges (OWASP#1047)
* issue#1034: Enhance date formatting in Home component to support date ranges * Enhance date formatting functions and add unit tests for date utilities * Update Upcoming Events date formatting in tests to reflect range display * Refactor date range formatting for improved readability and maintainability * Refactor formatDateRange function for improved readability * Update code --------- Co-authored-by: Arkadii Yakovets <2201626+arkid15r@users.noreply.github.com> Co-authored-by: Kate Golovanova <kate@kgthreads.com> Co-authored-by: Arkadii Yakovets <arkadii.yakovets@owasp.org>
1 parent 866261b commit 1294ba4

File tree

5 files changed

+129
-12
lines changed

5 files changed

+129
-12
lines changed

frontend/__tests__/e2e/pages/Home.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@ test.describe('Home Page', () => {
7171
test('should have upcoming events', async ({ page }) => {
7272
await expect(page.getByRole('heading', { name: 'Upcoming Events' })).toBeVisible()
7373
await expect(page.getByRole('link', { name: 'Event 1' })).toBeVisible()
74-
await expect(page.getByText('Feb 27,')).toBeVisible()
75-
await expect(page.getByText('Feb 28,')).toBeVisible()
74+
await expect(page.getByText('Feb 27 — 28, 2025')).toBeVisible()
7675
await page.getByRole('link', { name: 'Event 1' }).click()
7776
})
7877
})

frontend/__tests__/unit/pages/Home.test.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { mockAlgoliaData, mockGraphQLData } from '@unit/data/mockHomeData'
44
import { fetchAlgoliaData } from 'api/fetchAlgoliaData'
55
import { toast } from 'hooks/useToast'
66
import { Home } from 'pages'
7-
import { formatDate } from 'utils/dateFormatter'
87
import { render } from 'wrappers/testUtil'
98

109
jest.mock('hooks/useToast', () => ({
@@ -161,9 +160,7 @@ describe('Home', () => {
161160
expect(screen.getByText('Upcoming Events')).toBeInTheDocument()
162161
mockGraphQLData.upcomingEvents.forEach((event) => {
163162
expect(screen.getByText(event.name)).toBeInTheDocument()
164-
expect(
165-
screen.getByText(`${formatDate(event.startDate)} - ${formatDate(event.endDate)}`)
166-
).toBeInTheDocument()
163+
expect(screen.getByText('Feb 27 — 28, 2025')).toBeInTheDocument()
167164
})
168165
})
169166
})
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { formatDate, formatDateRange } from 'utils/dateFormatter'
2+
3+
describe('formatDate function', () => {
4+
beforeEach(() => {
5+
jest.clearAllMocks()
6+
})
7+
8+
test('formats ISO date string correctly', () => {
9+
expect(formatDate('2023-09-01')).toBe('Sep 1, 2023')
10+
})
11+
12+
test('formats Unix timestamp correctly', () => {
13+
// 1630454400 is Sep 1, 2021 in Unix timestamp (seconds)
14+
expect(formatDate(1630454400)).toBe('Sep 1, 2021')
15+
})
16+
17+
test('throws error for invalid date', () => {
18+
expect(() => formatDate('invalid-date')).toThrow('Invalid date')
19+
})
20+
21+
test('handles different date formats', () => {
22+
expect(formatDate('2023/09/01')).toBe('Sep 1, 2023')
23+
expect(formatDate('09/01/2023')).toBe('Sep 1, 2023')
24+
})
25+
26+
test('formats dates with leading zeros correctly', () => {
27+
expect(formatDate('2023-01-05')).toBe('Jan 5, 2023')
28+
})
29+
30+
test('handles different months', () => {
31+
expect(formatDate('2023-12-25')).toBe('Dec 25, 2023')
32+
expect(formatDate('2023-07-04')).toBe('Jul 4, 2023')
33+
})
34+
})
35+
36+
describe('formatDateRange function', () => {
37+
beforeEach(() => {
38+
jest.clearAllMocks()
39+
})
40+
41+
test('formats date range in same month correctly', () => {
42+
expect(formatDateRange('2023-09-01', '2023-09-04')).toBe('Sep 1 — 4, 2023')
43+
})
44+
45+
test('formats date range in different months but same year correctly', () => {
46+
expect(formatDateRange('2023-09-29', '2023-10-02')).toBe('Sep 29 — Oct 2, 2023')
47+
})
48+
49+
test('formats date range in different years correctly', () => {
50+
expect(formatDateRange('2023-12-30', '2024-01-03')).toBe('Dec 30, 2023 — Jan 3, 2024')
51+
})
52+
53+
test('formats Unix timestamp date ranges correctly', () => {
54+
// Sept 1-4, 2021
55+
const startTimestamp = 1630454400 // Sep 1, 2021
56+
const endTimestamp = 1630713600 // Sep 4, 2021
57+
expect(formatDateRange(startTimestamp, endTimestamp)).toBe('Sep 1 — 4, 2021')
58+
})
59+
60+
test('throws error when start date is invalid', () => {
61+
expect(() => formatDateRange('invalid-date', '2023-09-04')).toThrow('Invalid date')
62+
})
63+
64+
test('throws error when end date is invalid', () => {
65+
expect(() => formatDateRange('2023-09-01', 'invalid-date')).toThrow('Invalid date')
66+
})
67+
68+
test('handles month boundaries correctly', () => {
69+
expect(formatDateRange('2023-09-30', '2023-10-02')).toBe('Sep 30 — Oct 2, 2023')
70+
})
71+
72+
test('handles year boundaries correctly', () => {
73+
expect(formatDateRange('2023-12-29', '2024-01-02')).toBe('Dec 29, 2023 — Jan 2, 2024')
74+
})
75+
76+
test('handles single-day ranges correctly', () => {
77+
expect(formatDateRange('2023-09-01', '2023-09-01')).toBe('Sep 1, 2023')
78+
})
79+
80+
test('handles mixed input types correctly', () => {
81+
// Sep 1, 2021 as Unix timestamp and ISO string
82+
expect(formatDateRange(1630454400, '2021-09-04')).toBe('Sep 1 — 4, 2021')
83+
})
84+
})

frontend/src/pages/Home.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { ChapterTypeAlgolia } from 'types/chapter'
1818
import { EventType } from 'types/event'
1919
import { MainPageData } from 'types/home'
2020
import { capitalize } from 'utils/capitalize'
21-
import { formatDate } from 'utils/dateFormatter'
21+
import { formatDate, formatDateRange } from 'utils/dateFormatter'
2222
import AnimatedCounter from 'components/AnimatedCounter'
2323
import ChapterMap from 'components/ChapterMap'
2424
import ItemCardList from 'components/ItemCardList'
@@ -146,11 +146,7 @@ export default function Home() {
146146
<div className="flex flex-wrap items-center text-sm text-gray-600 dark:text-gray-300">
147147
<div className="mr-4 flex items-center">
148148
<FontAwesomeIcon icon={faCalendar} className="mr-2 h-4 w-4" />
149-
<span>
150-
{event.endDate && event.startDate != event.endDate
151-
? `${formatDate(event.startDate)} - ${formatDate(event.endDate)}`
152-
: formatDate(event.startDate)}
153-
</span>
149+
<span>{formatDateRange(event.startDate, event.endDate)}</span>
154150
</div>
155151
</div>
156152
</div>

frontend/src/utils/dateFormatter.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,44 @@ export const formatDate = (input: number | string) => {
1414
day: 'numeric',
1515
})
1616
}
17+
18+
export const formatDateRange = (startDate: number | string, endDate: number | string) => {
19+
const start = typeof startDate === 'number' ? new Date(startDate * 1000) : new Date(startDate)
20+
const end = typeof endDate === 'number' ? new Date(endDate * 1000) : new Date(endDate)
21+
22+
if (isNaN(start.getTime()) || isNaN(end.getTime())) {
23+
throw new Error('Invalid date')
24+
}
25+
26+
if (
27+
start.getTime() === end.getTime() ||
28+
(start.getFullYear() === end.getFullYear() &&
29+
start.getMonth() === end.getMonth() &&
30+
start.getDate() === end.getDate())
31+
) {
32+
return formatDate(startDate)
33+
}
34+
35+
const sameMonth = start.getMonth() === end.getMonth() && start.getFullYear() === end.getFullYear()
36+
const sameYear = start.getFullYear() === end.getFullYear()
37+
38+
if (sameMonth) {
39+
// Format as "Month Day - Day, Year" (e.g., "Sep 1 - 4, 2025")
40+
return (
41+
`${start.toLocaleDateString('en-US', { month: 'short' })} ` +
42+
`${start.getDate()}${end.getDate()}, ${start.getFullYear()}`
43+
)
44+
} else if (sameYear) {
45+
// Different months but same year (e.g., "Sep 29 - Oct 2, 2025")
46+
const startMonth = start.toLocaleDateString('en-US', { month: 'short' })
47+
const endMonth = end.toLocaleDateString('en-US', { month: 'short' })
48+
const startDay = start.getDate()
49+
const endDay = end.getDate()
50+
const year = start.getFullYear()
51+
52+
return `${startMonth} ${startDay}${endMonth} ${endDay}, ${year}`
53+
} else {
54+
// Different years (e.g., "Dec 30, 2025 - Jan 3, 2026")
55+
return `${formatDate(startDate)}${formatDate(endDate)}`
56+
}
57+
}

0 commit comments

Comments
 (0)