@burglekitt/gmt
Version:
Temporal-based date and time utilities with timezone support and polyfill integration
44 lines (43 loc) • 1.76 kB
JavaScript
import { Temporal } from "@js-temporal/polyfill";
import { isValidDate } from "../validate/index.js";
/**
* Return the candidate date nearest to `target` by calendar distance.
*
* - Distance is measured in whole days using `Temporal.PlainDate.compare`.
* - Returns `null` if the candidates array is empty or contains no valid dates.
* - Returns `null` if `target` is invalid.
* - On a tie (two equidistant candidates), returns the first one in array order.
*
* @param target ISO PlainDate string to measure distance from
* @param candidates Array of ISO PlainDate strings to choose from
* @returns The nearest candidate date string, or null on invalid input
*
* @example closestDateTo("2024-03-15", ["2024-03-01", "2024-03-20", "2024-03-18"]) // "2024-03-18"
* @example closestDateTo("2024-03-15", ["2024-03-01", "2024-03-29"]) // "2024-03-01"
* @example closestDateTo("2024-03-15", []) // null
* @example closestDateTo("invalid", ["2024-03-01"]) // null
*/
export function closestDateTo(target, candidates) {
if (!isValidDate(target) ||
!candidates.length ||
!candidates.some(isValidDate)) {
return null;
}
try {
const t = Temporal.PlainDate.from(target);
const validCandidates = candidates.filter(isValidDate);
const parsed = validCandidates.map((c) => ({
str: c,
date: Temporal.PlainDate.from(c),
}));
const closest = parsed.reduce((best, candidate) => {
const bestDist = Math.abs(t.until(best.date).days);
const candDist = Math.abs(t.until(candidate.date).days);
return candDist < bestDist ? candidate : best;
}, parsed[0]);
return closest.str;
}
catch {
return null;
}
}