date-fns: Incorrect result across DST in differenceInDays
Calculating differenceInDays fails across DST
Example: For London 2017-03-26, 01:00 clocks were turned forward 1 hour to 02:00 https://www.timeanddate.com/time/change/uk/london?year=2017
const d1 = new Date(2017, 2, 27, 1);
const d2 = new Date(2017, 2, 26, 2);
differenceInDays(d1, d2) === 1 // should be '0'
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 3
- Comments: 20 (12 by maintainers)
Commits related to this issue
- Fix differenceInDays across DST (closes #533) (#1630) — committed to date-fns/date-fns by dcousens 4 years ago
- Fix differenceInDays across DST (closes #533) (#1630) — committed to date-fns/date-fns by dcousens 4 years ago
- Fix differenceInDays across DST (closes #533) (#1630) — committed to Pyppe/date-fns by dcousens 4 years ago
Hi @dcousens - Yep, your investigation is correct. Math with days (and larger) units use local time, while math with hours (and smaller) units uses UTC. AFAIK, this is consistent with behavior of
add*/sub*functions, and also matches how moment.js has behaved since 2011. It’s also consistent with JS’s ownDate.setDateAPI which tries to keep local time constant when adding or subtracting days.It’s not surprising that this behavior was chosen, given that hours are always the same length so UTC math is always safe. Also, UTC math for hours (and smaller) units aligns with colloquial user expectations. If I say “I’ll meet you at the bar in 2 hours” then I don’t mean “I’ll probably meet you in 120 minutes but maybe in 180 minutes and maybe in 60 minutes, depending on what Saturday night this is.”
The reverse is true for days (and larger) units which have different lengths depending on time zone and time of year, so plain UTC math can be problematic for many use-cases. User expectations are also different. If my friend asks me to reschedule a 6:00PM diner reservation for the same time a week later, he won’t expect to sometimes eat at 5:00PM or 7:00PM depending on the time of year.
There are also valid use-cases where ignoring DST is OK, like displaying relative time lengths (e.g. “2 days ago” above GitHub comments) or any case where there’s no user expectation that local times stay the same across dates. But the JS ecosystem seems to have decided long ago that local day math is the default behavior and UTC day math should be opt-in.
Yep, agreed. Changing expected API behavior should only happen in a new major version. That said, given that local day math seems to be the default across the JS ecosystem, I’d recommend that local remain the default and UTC should be opt-in, with a nice side effect of not needing a new major version.
One choice would be to point users who want UTC behavior to the UTC submodule. I don’t know how mature that submodule is today… and if not then I’m sure PRs would be welcome! 😉
Agreed. #1754 updated API docs and examples to clarify behavior. There’s also explanation and a code sample for avoiding DST behavior if that’s what’s desired. Here’s the newly-published docs for
differenceInDays:And for
differenceInWeeks:In a DST change-over, a user typically expects to see the “number of days” between 8am today and 8am tomorrow, to be 1 day. That is why a
differenceInLocalTimecould be useful.@dcousens This seems like a fundamental breach in expectations. The difference between two points in time is fixed and does not change. The
difference*functions should convert from and to times to UTC before calculating the difference.@canvaspixels how would you expect the difference in days to be different based on time zone? two timestamps should always have the same difference regardless of timezone.