Source: trimDate.js

import { DateTime } from "luxon";

/**
 * Trim a time series to full local calendar days.
 *
 * Discards any partial days at the beginning or end of the array. Uses the local
 * time zone to determine day boundaries, but always returns timestamps in UTC.
 *
 * @param {Array.<DateTime>} datetime - Array of Luxon DateTime objects (in UTC).
 * @param {Array.<number>} x - Array of measurements (same length as datetime).
 * @param {string} timezone - IANA/Olson timezone (e.g. "America/Los_Angeles").
 * @returns {object} - Object with `datetime` and `x` arrays trimmed to full days.
 */
export function trimDate(datetime, x, timezone) {
  if (!Array.isArray(datetime) || !Array.isArray(x) || datetime.length !== x.length) {
    throw new Error("datetime and x must be arrays of equal length.");
  }
  if (typeof timezone !== "string") {
    throw new Error("timezone must be a string.");
  }

  for (const dt of datetime) {
    if (!DateTime.isDateTime(dt)) {
      throw new Error("All entries in datetime must be Luxon DateTime objects.");
    }
    if (dt.zoneName !== "UTC") {
      throw new Error("All datetime values must be in the UTC time zone.");
    }
  }

  // Convert to local hours
  const localHours = datetime.map((dt) => dt.setZone(timezone).hour);

  // Determine how many entries to trim from start
  const start = localHours[0] === 0 ? 0 : 24 - localHours[0];

  // Determine how many entries to keep up to the last full hour of a full day
  const lastHour = localHours[localHours.length - 1];
  const end = lastHour === 23 ? localHours.length : localHours.length - (lastHour + 1);

  if (end <= start) {
    return { datetime: [], x: [] };
  }

  return {
    datetime: datetime.slice(start, end),
    x: x.slice(start, end),
  };
}