UNPKG

mingo

Version:

MongoDB query language for in-memory objects

98 lines (97 loc) 2.96 kB
import { computeValue } from "../../../core/_internal"; import { assert, isDate, isNil } from "../../../util"; import { adjustDate, dateAdd, dateDiffDay, dateDiffMonth, dateDiffQuarter, dateDiffWeek, dateDiffYear, DAYS_PER_WEEK, isoWeekday, parseTimezone, TIME_UNITS, TIMEUNIT_IN_MILLIS } from "./_internal"; const REF_DATE_MILLIS = 9466848e5; const distanceToBinLowerBound = (value, binSize) => { let remainder = value % binSize; if (remainder < 0) { remainder += binSize; } return remainder; }; const DATE_DIFF_FN = { day: dateDiffDay, month: dateDiffMonth, quarter: dateDiffQuarter, year: dateDiffYear }; const DAYS_OF_WEEK_RE = /(mon(day)?|tue(sday)?|wed(nesday)?|thu(rsday)?|fri(day)?|sat(urday)?|sun(day)?)/i; const $dateTrunc = (obj, expr, options) => { const { date, unit, binSize: optBinSize, timezone, startOfWeek: optStartOfWeek } = computeValue(obj, expr, null, options); if (isNil(date) || isNil(unit)) return null; const startOfWeek = (optStartOfWeek ?? "sun").toLowerCase().substring(0, 3); assert( isDate(date), "$dateTrunc: 'date' must resolve to a valid Date object." ); assert(TIME_UNITS.includes(unit), "$dateTrunc: unit is invalid."); assert( unit != "week" || DAYS_OF_WEEK_RE.test(startOfWeek), `$dateTrunc: startOfWeek '${startOfWeek}' is not a valid.` ); assert( isNil(optBinSize) || optBinSize > 0, "$dateTrunc requires 'binSize' to be greater than 0, but got value 0." ); const binSize = optBinSize ?? 1; switch (unit) { case "millisecond": case "second": case "minute": case "hour": { const binSizeMillis = binSize * TIMEUNIT_IN_MILLIS[unit]; const shiftedDate = date.getTime() - REF_DATE_MILLIS; return new Date( date.getTime() - distanceToBinLowerBound(shiftedDate, binSizeMillis) ); } default: { assert(binSize <= 1e11, "dateTrunc unsupported binSize value"); const d = new Date(date); const refPointDate = new Date(REF_DATE_MILLIS); let distanceFromRefPoint = 0; if (unit == "week") { const refPointDayOfWeek = isoWeekday(refPointDate, startOfWeek); const daysToAdjustBy = (DAYS_PER_WEEK - refPointDayOfWeek) % DAYS_PER_WEEK; refPointDate.setTime( refPointDate.getTime() + daysToAdjustBy * TIMEUNIT_IN_MILLIS.day ); distanceFromRefPoint = dateDiffWeek(refPointDate, d, startOfWeek); } else { distanceFromRefPoint = DATE_DIFF_FN[unit](refPointDate, d); } const binLowerBoundFromRefPoint = distanceFromRefPoint - distanceToBinLowerBound(distanceFromRefPoint, binSize); const newDate = dateAdd( refPointDate, unit, binLowerBoundFromRefPoint, timezone ); const minuteOffset = parseTimezone(timezone); adjustDate(newDate, -minuteOffset); return newDate; } } }; export { $dateTrunc };