diff --git a/CHANGELOG.md b/CHANGELOG.md index 57df162..bfab7cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ #### Review fixes +- Rejected invalid ISO calendar dates in time estimates instead of silently rolling them into another month. - Ignored empty relative file links wrapped in punctuation or spelled with dot path segments, instead of linking to the Todo document's directory. - Kept tag cleanup and hidden-tag labels working after an unmatched backtick, while preserving tags inside complete inline code spans. - Skipped false embedded-todo scan candidates when custom expressions match without a non-empty marker. diff --git a/src/utils/time.ts b/src/utils/time.ts index dc4d17f..1b8833b 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -272,6 +272,24 @@ const Time = { if (/^\s*\d+\s*$/.test(to)) return 0; + // Sugar normalizes impossible ISO dates (e.g. February 30) into another + // month; reject those explicitly rather than silently changing a deadline. + if ( + /^\d{4}-\d{2}-\d{2}(?:[ T]\d{2}:\d{2}(?::\d{2})?)?$/.test(to) && + !moment( + to, + [ + 'YYYY-MM-DD', + 'YYYY-MM-DD HH:mm', + 'YYYY-MM-DD HH:mm:ss', + 'YYYY-MM-DDTHH:mm', + 'YYYY-MM-DDTHH:mm:ss', + ], + true + ).isValid() + ) + return 0; + // Parse duration strings before calendar expressions so they stay relative to `from`. try { const milliseconds = toTime(to).milliseconds(); diff --git a/test/time.spec.ts b/test/time.spec.ts index 36dda38..251b82d 100644 --- a/test/time.spec.ts +++ b/test/time.spec.ts @@ -74,6 +74,14 @@ describe('Time utilities', () => { expect(Time.durationSeconds('2020-01-02', from)).to.equal(24 * 3600); }); + it('rejects invalid ISO calendar dates rather than rolling them into another month', () => { + const from = new Date(2020, 0, 1); + + expect(Time.durationSeconds('2020-02-30', from)).to.equal(0); + expect(Time.diffSeconds('2021-02-29 09:00', from)).to.equal(0); + expect(Time.durationSeconds('2020-02-29', from)).to.equal(59 * 24 * 3600); + }); + it('supports flat man-hours formatting', () => { const from = new Date('2020-01-01T00:00:00Z'); const to = new Date(from.getTime() + (25 * 3600 + 15 * 60) * 1000);