If two dates are three days apart, no calendar date is equally distant from both. I'll show how I handle that gap in JavaScript with an explicit earlier-date rule, without confusing calendar-day counts with elapsed timestamps.
I built a small date-only calculator around that question. The useful implementation detail is not averaging two timestamps: it is making the meaning of “middle” explicit, normalizing the inputs, and keeping distance labels attached to the dates the user actually entered.
Normalize to Calendar Days Before Comparing
The inputs come from date pickers rather than timestamp fields. The calculation first checks that both dates exist, then converts them to Day.js values at the start of their respective days:
const d1 = dayjs(date1.value).startOf("day"); const d2 = dayjs(date2.value).startOf("day"); const earlier = d1.isBefore(d2) ? d1 : d2; const later = d1.isBefore(d2) ? d2 : d1; const totalDays = later.diff(earlier, "day"); That normalization prevents a hidden time-of-day value from influencing a task presented as date-only. It is especially relevant to a “Today” button: the current instant has an hour, minute, and second, but those are not part of the question the UI asks.
Sorting the normalized dates gives the main calculation one consistent direction. The user can enter the later date first without creating a negative interval or needing a second midpoint algorithm.
I also want to distinguish calendar operations from dividing elapsed milliseconds by 86,400,000. A local calendar day does not always contain 24 elapsed hours around daylight-saving changes. This implementation uses Day.js day differences and calendar-day addition; it does not claim to find the instant exactly halfway through an arbitrary elapsed-time interval.
For example, March 7 to March 10, 2026 in America/New_York spans three calendar days but only 71 elapsed hours between local midnights. The code selects March 8 by counting whole days, not by halving those 71 hours.
That distinction should be decided before choosing an API. A project planning checkpoint and a midpoint between two precisely timed events may sound similar in conversation, but they need different definitions and potentially different input fields.
Odd Intervals Need a Visible Tie Policy
For an even interval, selecting the middle day is straightforward. January 1 to January 5 spans four days, so adding two days to the earlier date selects January 3.
For an odd interval, the implementation chooses the earlier of the two neighboring calendar dates:
const isOdd = totalDays % 2 !== 0; const halfDays = Math.floor(totalDays / 2); const midpointDate = earlier.add(halfDays, "day"); January 1 to January 4 spans three days. Flooring half the interval gives one day, so the selected result is January 2. It is one day from the earlier endpoint and two days from the later one.
The important thing is not that flooring is the only acceptable policy. Choosing January 3 could also be reasonable for a scheduling product that prefers the later checkpoint. What would be misleading is returning January 2 with no explanation and implying that both distances are equal.
The component stores isOdd in the result and displays a notice when that flag is true. That gives the presentation enough information to explain the asymmetric distances instead of hiding the decision in a formatting function.
There is a wording bug worth separating from the algorithm: the current English warning says the exact midpoint falls at midnight. Between January 1 at 00:00 and January 4 at 00:00 in a fixed-offset zone, the elapsed-time midpoint is January 2 at noon. The code never calculates that instant; it selects January 2 as a calendar date. I would change the warning to describe the earlier-date policy rather than promise a particular clock time.
There is a useful boundary case here: two consecutive dates span one day, so flooring gives zero. The selected midpoint is then the earlier endpoint itself. That can initially look like a calculation failure, but it follows directly from the stated rule. If the product requires a strictly interior date, no valid result exists for that interval.
This is also a calendar-day difference, not an inclusive count of labeled dates. January 1 through January 4 contains four date labels but spans three day boundaries. Mixing those conventions is an easy way to introduce an off-by-one error.
Preserve the Original Input Labels
Sorting dates simplifies the arithmetic, but it should not silently relabel the user's inputs. The result still needs to say how far the selected day is from “Date 1” and “Date 2,” not merely from “earlier” and “later.”
The implementation explicitly maps the distances back:
const daysFromEarlier = midpointDate.diff(earlier, "day"); const daysFromLater = later.diff(midpointDate, "day"); const dFromDate1 = d1.isBefore(d2) ? daysFromEarlier : daysFromLater; const dFromDate2 = d1.isBefore(d2) ? daysFromLater : daysFromEarlier; If January 4 is entered first and January 1 second, the chosen midpoint remains January 2. The first input is now two days away and the second is one day away.
That is a small example of a broader UI pattern: normalize data for computation without losing the association between a value and its original control. The computational order and the presentation order are not always the same thing.
The component also clears the result when an input changes, when dates are swapped, or when “Today” is selected. It requires another calculation rather than leaving an old midpoint visible beside new endpoints. A correct algorithm attached to a stale result is still a confusing experience.
Equal endpoints have a dedicated branch: the selected date is the endpoint, both distances are zero, and an informational message explains the case. No fractional midpoint logic is needed.
Formatting Does Not Add Time-Zone Semantics
Results are formatted with Intl.DateTimeFormat. The application maps its internal language codes to BCP 47 locale identifiers; English uses the browser language when available. That changes how a date is written, not what kind of interval was calculated.
There is no input for two different time zones, no business-day exclusion, and no holiday calendar. A date picker does not make this a meeting-time converter or a working-day scheduler. Those features would require additional rules rather than another localized label.
For testing, I would keep same-day, consecutive-day, even-gap, odd-gap, reversed-input, leap-day, and local daylight-saving cases separate. Each checks a different promise: zero handling, rounding, stable labels, or calendar arithmetic. One ordinary mid-month example cannot stand in for all of them.
I turned these date-only rules into a small free tool: Midpoint Date Calculator.