Skip to content

Commit 5b5dce9

Browse files
committed
handle extremely rare js rollover date bugs
1 parent 0b4d526 commit 5b5dce9

File tree

4 files changed

+89
-1
lines changed

4 files changed

+89
-1
lines changed

__tests__/functions/is-timestamp-older.test.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {isTimestampOlder} from '../../src/functions/is-timestamp-older'
44
beforeEach(() => {
55
jest.clearAllMocks()
66
jest.spyOn(core, 'debug').mockImplementation(() => {})
7+
jest.spyOn(core, 'error').mockImplementation(() => {})
78
})
89

910
describe('isTimestampOlder', () => {
@@ -87,4 +88,27 @@ describe('isTimestampOlder', () => {
8788
'2024-10-16T11:00:00Z is not older than 2024-10-16T11:00:00Z'
8889
)
8990
})
91+
92+
test('accepts valid leap year date', () => {
93+
// Feb 29, 2024 is valid (leap year)
94+
expect(() =>
95+
isTimestampOlder('2024-02-29T12:00:00Z', '2024-10-15T11:00:00Z')
96+
).not.toThrow()
97+
expect(
98+
isTimestampOlder('2024-02-29T12:00:00Z', '2024-10-15T11:00:00Z')
99+
).toBe(true)
100+
expect(
101+
isTimestampOlder('2024-10-15T11:00:00Z', '2024-02-29T12:00:00Z')
102+
).toBe(false)
103+
})
104+
105+
test('throws an error on js silent date contructor corrections', () => {
106+
// Invalid date: 2024-02-30T12:00:00Z actually becomes 2024-03-01T12:00:00Z (gross)
107+
expect(() =>
108+
isTimestampOlder('2024-02-30T12:00:00Z', '2024-10-15T11:00:00Z')
109+
).toThrow(/Invalid date format/)
110+
expect(() =>
111+
isTimestampOlder('2024-10-15T11:00:00Z', '2024-02-30T12:00:00Z')
112+
).toThrow(/Invalid date format/)
113+
})
90114
})

dist/index.js

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/functions/is-timestamp-older.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,38 @@ export function isTimestampOlder(timestampA, timestampB) {
2727
const timestampADate = new Date(timestampA)
2828
const timestampBDate = new Date(timestampB)
2929

30+
// Extra strict: ensure the parsed date matches the input string exactly (prevents JS date rollover)
31+
const toStrictISOString = d => {
32+
// Returns YYYY-MM-DDTHH:MM:SSZ
33+
return (
34+
d.getUTCFullYear().toString().padStart(4, '0') +
35+
'-' +
36+
(d.getUTCMonth() + 1).toString().padStart(2, '0') +
37+
'-' +
38+
d.getUTCDate().toString().padStart(2, '0') +
39+
'T' +
40+
d.getUTCHours().toString().padStart(2, '0') +
41+
':' +
42+
d.getUTCMinutes().toString().padStart(2, '0') +
43+
':' +
44+
d.getUTCSeconds().toString().padStart(2, '0') +
45+
'Z'
46+
)
47+
}
48+
if (
49+
isNaN(timestampADate) ||
50+
isNaN(timestampBDate) ||
51+
toStrictISOString(timestampADate) !== timestampA ||
52+
toStrictISOString(timestampBDate) !== timestampB
53+
) {
54+
core.error(
55+
`Invalid date parsing. Received: '${timestampA}' => ${timestampADate}, '${timestampB}' => ${timestampBDate}`
56+
)
57+
throw new Error(
58+
`Invalid date format. Please ensure the dates are valid UTC timestamps. Received: '${timestampA}', '${timestampB}'`
59+
)
60+
}
61+
3062
const result = timestampADate < timestampBDate
3163

3264
if (result) {

0 commit comments

Comments
 (0)