luxon
luxon copied to clipboard
hasSame fastpath for DateTimes in the same zone
Hi,
I ran a quick benchmark (hopefully valid) after I noticed my app spending a lot of time in hasSame
. If I understand it correctly, the only difference between ts_a[unit] === ts_b[unit]
and ts_a.hasSame(ts_b, unit)
is behavior with respect to timezones?
> a = luxon.DateTime.local()
DateTime {ts: 1578612479954, _zone: LocalZone, loc: Locale, invalid: null, weekData: null, …}
> a.toISO()
"2020-01-10T00:27:59.954+01:00"
> start = window.performance.now()
> for (let ix = 0; ix < 100000; ix++) {
> a.day === a.day;
> }
> console.log(window.performance.now() - start)
VM1059:5 2.7500000142026693
undefined
> start = window.performance.now()
> for (let ix = 0; ix < 100000; ix++) {
> a.hasSame(a, 'day');
> }
> console.log(window.performance.now() - start)
VM1099:5 1911.0750000108965
undefined
Is it possible to speed up hasSame
in the case of equal timezones?
yeah, that's probably right
According to the documentation it's not fully correct: "Higher-order units must also be identical for this function to return true. Note that time zones are ignored in this comparison". Most of the work then supposedly lies in checking the higher order units.
@mees- this a bit of a complicated one. For starters, the method's implementation has significantly changed since this was reported.
This is the current implementation of hasSame
:
hasSame(otherDateTime, unit) {
if (!this.isValid) return false;
const inputMs = otherDateTime.valueOf();
const adjustedToZone = this.setZone(otherDateTime.zone, { keepLocalTime: true });
return adjustedToZone.startOf(unit) <= inputMs && inputMs <= adjustedToZone.endOf(unit);
}
I suspect this is now as fast for things in the same time zone as not, since the setZone
will noop for that case.
The question for perf here is really whether we can do better than startOf
and endOf
and just compare the units directly, like,
adjustedToZone.get(unit) == this.get(unit)
I think we can but I don't remember quite why we didn't in the first place...
The difference between a.hasSame(b, unit)
and a.get(unit) == b.get(unit)
is that hasSame
also checks higher-order units implicitly. 2022-04-02 and 2021-04-09 have the same month when you just compare the month index (a.get('month') == b.get('month')
), but they are not in the same calendar month (a.hasSame(b, 'month')
would be false in this case).
One possible optimization might be to reuse startOf
and do something like:
const adjustedToZoneStart = adjustedToZone.startOf(unit);
const adjustedToZoneEnd = adjustedToZoneStart.plus({[unit]: 1});
return adjustedToZoneStart <= inputMs && inputMs < adjustedToZoneEnd
But it would have to be investigated whether that yields any meaningful performance improvement.
@diesieben07 ah, right