mingo
Version:
MongoDB query language for in-memory objects
243 lines (242 loc) • 7.63 kB
JavaScript
import { computeValue } from "../../../core/_internal";
import { assert, isDate, isNil, isNumber } from "../../../util";
const TIME_UNITS = [
"year",
"quarter",
"month",
"week",
"day",
"hour",
"minute",
"second",
"millisecond"
];
const ISO_WEEKDAYS = {
mon: 1,
tue: 2,
wed: 3,
thu: 4,
fri: 5,
sat: 6,
sun: 7
};
const LEAP_YEAR_REF_POINT = -1e9;
const DAYS_PER_WEEK = 7;
const isLeapYear = (y) => (y & 3) == 0 && (y % 100 != 0 || y % 400 == 0);
const DAYS_IN_YEAR = [
365,
366
/*leap*/
];
const YEAR_DAYS_OFFSET = [
[0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334],
[0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
/*leap*/
];
const dayOfYear = (d) => YEAR_DAYS_OFFSET[+isLeapYear(d.getUTCFullYear())][d.getUTCMonth()] + d.getUTCDate();
const isoWeekday = (date, startOfWeek) => {
const dow = date.getUTCDay() || 7;
const name = startOfWeek.toLowerCase().substring(0, 3);
return (dow - ISO_WEEKDAYS[name] + DAYS_PER_WEEK) % DAYS_PER_WEEK;
};
const p = (y) => (y + Math.floor(y / 4) - Math.floor(y / 100) + Math.floor(y / 400)) % 7;
const weeks = (y) => 52 + Number(p(y) == 4 || p(y - 1) == 3);
function isoWeek(d) {
const dow = d.getUTCDay() || 7;
const w = Math.floor((10 + dayOfYear(d) - dow) / 7);
if (w < 1) return weeks(d.getUTCFullYear() - 1);
if (w > weeks(d.getUTCFullYear())) return 1;
return w;
}
function isoWeekYear(d) {
return d.getUTCFullYear() - Number(d.getUTCMonth() === 0 && d.getUTCDate() == 1 && d.getUTCDay() < 1);
}
const MINUTES_PER_HOUR = 60;
const TIMEUNIT_IN_MILLIS = {
week: 6048e5,
day: 864e5,
hour: 36e5,
minute: 6e4,
second: 1e3,
millisecond: 1
};
const DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%LZ";
const DATE_PART_INTERVAL = [
["year", 0, 9999],
["month", 1, 12],
["day", 1, 31],
["hour", 0, 23],
["minute", 0, 59],
["second", 0, 59],
["millisecond", 0, 999]
];
const MONTHS = {
jan: 1,
feb: 2,
mar: 3,
apr: 4,
may: 5,
jun: 6,
jul: 7,
aug: 8,
sep: 9,
oct: 10,
nov: 11,
dec: 12
};
const DATE_SYM_TABLE = {
"%b": {
name: "abbr_month",
padding: 3,
re: /(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/i
},
"%B": {
name: "full_month",
padding: 0,
re: /(January|February|March|April|May|June|July|August|September|October|November|December)/i
},
"%Y": { name: "year", padding: 4, re: /([0-9]{4})/ },
"%G": { name: "year", padding: 4, re: /([0-9]{4})/ },
"%m": { name: "month", padding: 2, re: /(0[1-9]|1[012])/ },
"%d": { name: "day", padding: 2, re: /(0[1-9]|[12][0-9]|3[01])/ },
"%j": {
name: "day_of_year",
padding: 3,
re: /(0[0-9][1-9]|[12][0-9]{2}|3[0-5][0-9]|36[0-6])/
},
"%H": { name: "hour", padding: 2, re: /([01][0-9]|2[0-3])/ },
"%M": { name: "minute", padding: 2, re: /([0-5][0-9])/ },
"%S": { name: "second", padding: 2, re: /([0-5][0-9]|60)/ },
"%L": { name: "millisecond", padding: 3, re: /([0-9]{3})/ },
"%w": { name: "day_of_week", padding: 1, re: /([0-6])/ },
"%u": { name: "day_of_week_iso", padding: 1, re: /([1-7])/ },
"%U": { name: "week_of_year", padding: 2, re: /([1-4][0-9]?|5[0-3]?)/ },
"%V": { name: "week_of_year_iso", padding: 2, re: /([1-4][0-9]?|5[0-3]?)/ },
"%z": {
name: "timezone",
padding: 2,
re: /(([+-][01][0-9]|2[0-3]):?([0-5][0-9])?)/
},
"%Z": { name: "minute_offset", padding: 3, re: /([+-][0-9]{3})/ },
"%%": { name: "percent_literal", padding: 1, re: /%%/ }
};
const DATE_FORMAT_SYM_RE = /(%[bBYGmdjHMSLwuUVzZ%])/g;
const DATE_FORMAT_SEP_RE = /%[bBYGmdjHMSLwuUVzZ%]/;
const TIMEZONE_RE = /^[a-zA-Z_]+\/[a-zA-Z_]+$/;
function parseTimezone(tzstr) {
if (isNil(tzstr)) return 0;
if (TIMEZONE_RE.test(tzstr)) {
const date = /* @__PURE__ */ new Date();
const utcDate = new Date(date.toLocaleString("en-US", { timeZone: "UTC" }));
const tzDate = new Date(date.toLocaleString("en-US", { timeZone: tzstr }));
return (tzDate.getTime() - utcDate.getTime()) / 6e4;
}
const m = DATE_SYM_TABLE["%z"].re.exec(tzstr);
assert(!!m, `timezone '${tzstr}' is invalid or not supported.`);
const hr = parseInt(m[2]) || 0;
const min = parseInt(m[3]) || 0;
return (Math.abs(hr * MINUTES_PER_HOUR) + min) * (hr < 0 ? -1 : 1);
}
function formatTimezone(minuteOffset) {
return (minuteOffset < 0 ? "-" : "+") + padDigits(Math.abs(Math.floor(minuteOffset / MINUTES_PER_HOUR)), 2) + padDigits(Math.abs(minuteOffset) % MINUTES_PER_HOUR, 2);
}
function adjustDate(d, minuteOffset) {
d.setUTCMinutes(d.getUTCMinutes() + minuteOffset);
}
function computeDate(obj, expr, options) {
if (isDate(obj)) return obj;
const d = computeValue(obj, expr, null, options);
if (isDate(d)) return new Date(d);
if (isNumber(d)) return new Date(d * 1e3);
assert(!!d?.date, `cannot convert ${JSON.stringify(expr)} to date`);
const date = isDate(d.date) ? new Date(d.date) : new Date(d.date * 1e3);
if (d.timezone) {
adjustDate(date, parseTimezone(d.timezone));
}
return date;
}
function padDigits(n, digits) {
return new Array(Math.max(digits - String(n).length + 1, 0)).join("0") + n.toString();
}
const leapYearsSinceReferencePoint = (year) => {
const yearsSinceReferencePoint = year - LEAP_YEAR_REF_POINT;
return Math.trunc(yearsSinceReferencePoint / 4) - Math.trunc(yearsSinceReferencePoint / 100) + Math.trunc(yearsSinceReferencePoint / 400);
};
function daysBetweenYears(startYear, endYear) {
return Math.trunc(
leapYearsSinceReferencePoint(endYear - 1) - leapYearsSinceReferencePoint(startYear - 1) + (endYear - startYear) * DAYS_IN_YEAR[0]
);
}
const dateDiffYear = (start, end) => end.getUTCFullYear() - start.getUTCFullYear();
const dateDiffMonth = (start, end) => end.getUTCMonth() - start.getUTCMonth() + dateDiffYear(start, end) * 12;
const dateDiffQuarter = (start, end) => {
const a = Math.trunc(start.getUTCMonth() / 3);
const b = Math.trunc(end.getUTCMonth() / 3);
return b - a + dateDiffYear(start, end) * 4;
};
const dateDiffDay = (start, end) => dayOfYear(end) - dayOfYear(start) + daysBetweenYears(start.getUTCFullYear(), end.getUTCFullYear());
const dateDiffWeek = (start, end, startOfWeek) => {
const wk = (startOfWeek || "sun").substring(0, 3);
return Math.trunc(
(dateDiffDay(start, end) + isoWeekday(start, wk) - isoWeekday(end, wk)) / DAYS_PER_WEEK
);
};
const dateDiffHour = (start, end) => end.getUTCHours() - start.getUTCHours() + dateDiffDay(start, end) * 24;
const addMonth = (d, amount) => {
const m = d.getUTCMonth() + amount;
const yearOffset = Math.floor(m / 12);
if (m < 0) {
const month = m % 12 + 12;
d.setUTCFullYear(d.getUTCFullYear() + yearOffset, month, d.getUTCDate());
} else {
d.setUTCFullYear(d.getUTCFullYear() + yearOffset, m % 12, d.getUTCDate());
}
};
const dateAdd = (date, unit, amount, _timezone) => {
const d = new Date(date);
switch (unit) {
case "year":
d.setUTCFullYear(d.getUTCFullYear() + amount);
break;
case "quarter":
addMonth(d, 3 * amount);
break;
case "month":
addMonth(d, amount);
break;
default:
d.setTime(d.getTime() + TIMEUNIT_IN_MILLIS[unit] * amount);
}
return d;
};
export {
DATE_FORMAT,
DATE_FORMAT_SEP_RE,
DATE_FORMAT_SYM_RE,
DATE_PART_INTERVAL,
DATE_SYM_TABLE,
DAYS_PER_WEEK,
LEAP_YEAR_REF_POINT,
MINUTES_PER_HOUR,
MONTHS,
TIMEUNIT_IN_MILLIS,
TIME_UNITS,
adjustDate,
computeDate,
dateAdd,
dateDiffDay,
dateDiffHour,
dateDiffMonth,
dateDiffQuarter,
dateDiffWeek,
dateDiffYear,
dayOfYear,
daysBetweenYears,
formatTimezone,
isLeapYear,
isoWeek,
isoWeekYear,
isoWeekday,
padDigits,
parseTimezone
};