@digifi/jexl-functions
Version:
Package with available JEXL functions
296 lines (295 loc) • 13.4 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const errors_1 = require("../errors");
const dayjs_1 = __importDefault(require("../dayjs"));
const module_1 = require("../utils/module");
const DEFAULT_WEEKEND_MASK = '0000011';
const WEEKEND_MASK_BY_NUMBER = {
1: DEFAULT_WEEKEND_MASK,
2: '1000001',
3: '1100000',
4: '0110000',
5: '0011000',
6: '0001100',
7: '0000110',
11: '0000001',
12: '1000000',
13: '0100000',
14: '0010000',
15: '0001000',
16: '0000100',
17: '0000010',
};
exports.default = (0, module_1.createModule)(({ validateArrayLikeValueMaxSize, coerceToNumber, coerceToStringWithValidation }, options) => {
const shortUnitTypeToLongUnitType = {
'M': 'months',
'D': 'days',
'Y': 'years',
};
const validateWeekendMask = (weekend) => {
const weekendMaskRegex = new RegExp('^[0|1]{7}$');
if (typeof weekend !== 'string') {
throw new errors_1.JexlFunctionExecutionError('Weekend should be a string');
}
if (!weekendMaskRegex.test(weekend)) {
throw new errors_1.JexlFunctionExecutionError('Weekend string doesn\'t much patter');
}
if (!weekend.includes('0')) {
throw new errors_1.JexlFunctionExecutionError('At least one working day should exists');
}
};
const generateHolidaysSet = (holidays) => {
const holidaysSet = new Set();
for (const holiday of holidays) {
const holidayDate = (0, dayjs_1.default)(holiday);
if (!holidayDate.isValid()) {
throw new errors_1.JexlFunctionExecutionError('Holiday date is not valid.');
}
holidaysSet.add(`${holidayDate.month()}/${holidayDate.date()}/${holidayDate.year()}`);
}
return holidaysSet;
};
const getWeekendMask = (weekend) => {
if (!weekend) {
return DEFAULT_WEEKEND_MASK;
}
if (typeof weekend === 'string') {
return weekend;
}
if (typeof weekend !== 'number') {
return weekend;
}
const mask = WEEKEND_MASK_BY_NUMBER[weekend];
if (!mask) {
throw new errors_1.JexlFunctionExecutionError('Incorrect weekend');
}
return mask;
};
const WEEKNUM = (date, format) => {
return (0, dayjs_1.default)(date, coerceToStringWithValidation(format) || undefined).week();
};
const YEAR = (date, format) => {
return (0, dayjs_1.default)(date, coerceToStringWithValidation(format) || undefined).year();
};
const YEARFRAC = (firstDate, secondDate, basis = 0, format) => {
const coercedFormat = coerceToStringWithValidation(format) || undefined;
const coercedBasis = coerceToNumber(basis);
const firstDateObject = (0, dayjs_1.default)(firstDate, coercedFormat);
const secondDateObject = (0, dayjs_1.default)(secondDate, coercedFormat);
const firstDateDay = firstDateObject.date();
const firstDateYear = firstDateObject.year();
const firstDateMonth = firstDateObject.month();
const secondDateDay = secondDateObject.date();
const secondDateYear = secondDateObject.year();
const secondDateMonth = secondDateObject.month();
switch (coercedBasis) {
case 0: {
const convertedFirstDateDay = firstDateDay === 31 ? 30 : firstDateDay;
const convertedSecondDateDay = secondDateDay === 31 ? 30 : secondDateDay;
return (convertedSecondDateDay + secondDateMonth * 30 + secondDateYear * 360 - (convertedFirstDateDay + firstDateMonth * 30 + firstDateYear * 360)) / 360;
}
case 1: {
return secondDateObject.diff(firstDateObject, 'days') / 360;
}
case 2: {
return secondDateObject.diff(firstDateObject, 'days') / 365;
}
case 3: {
return (secondDateDay + secondDateMonth * 30 + secondDateYear * 360 - (firstDateDay + firstDateMonth * 30 + firstDateYear * 360)) / 360;
}
default: {
throw new errors_1.JexlFunctionExecutionError('Incorrect basis provided.');
}
}
};
const NOW = (format) => {
return (0, dayjs_1.default)().format(coerceToStringWithValidation(format) || undefined);
};
const DATE = (year, month, day, format) => {
return (0, dayjs_1.default)({
year: coerceToNumber(year),
month: coerceToNumber(month) - 1,
day: coerceToNumber(day),
}).format(coerceToStringWithValidation(format) || undefined);
};
const DAY = (date, format) => {
return (0, dayjs_1.default)(date, coerceToStringWithValidation(format) || undefined).date();
};
const DAYS = (firstDate, secondDate, format) => {
const coercedFormat = coerceToStringWithValidation(format) || undefined;
return (0, dayjs_1.default)(firstDate, coercedFormat).diff((0, dayjs_1.default)(secondDate, coercedFormat), 'days');
};
const DATEVALUE = (date, format) => {
return (0, dayjs_1.default)(date).format(coerceToStringWithValidation(format) || undefined);
};
const ISOWEEKNUM = (date, format) => {
const dateObject = (0, dayjs_1.default)(date, coerceToStringWithValidation(format) || undefined).startOf('date');
const yearStateDateObject = (0, dayjs_1.default)({ year: dateObject.year(), month: 0, day: 0 });
const isoDate = dateObject.add(dateObject.date() + 4 - (dateObject.day() || 7));
return Math.ceil(((isoDate.diff(yearStateDateObject, 'milliseconds')) / 86400000 + 1) / 7);
};
const MONTH = (date, format) => {
return (0, dayjs_1.default)(date, coerceToStringWithValidation(format) || undefined).month() + 1;
};
const TODAY = (format) => {
const now = (0, dayjs_1.default)();
return now
.set('hour', 0)
.set('minute', 0)
.set('second', 0)
.format(coerceToStringWithValidation(format) || undefined);
};
const EOMONTH = (startDate, months, format) => {
const coercedMonths = coerceToNumber(months);
const flooredMonths = Math.floor(coercedMonths);
const startDateObject = (0, dayjs_1.default)(startDate);
if (!startDateObject.isValid()) {
throw new errors_1.JexlFunctionExecutionError('Invalid start date.');
}
return (0, dayjs_1.default)({
year: startDateObject.year(),
month: startDateObject.month() + flooredMonths + 1,
day: 0,
})
.add(-1, 'day')
.format(coerceToStringWithValidation(format) || undefined);
};
const NETWORKDAYSINTL = (startDate, endDate, weekend, holidays = []) => {
const startDateObject = (0, dayjs_1.default)(startDate);
const endDateObject = (0, dayjs_1.default)(endDate);
const transformedHolidays = Array.isArray(holidays) ? holidays : [holidays];
const weekendMask = getWeekendMask(weekend);
validateArrayLikeValueMaxSize(holidays);
validateWeekendMask(weekendMask);
if (!startDateObject.isValid()) {
throw new errors_1.JexlFunctionExecutionError('Start date is invalid');
}
if (!endDateObject.isValid()) {
throw new errors_1.JexlFunctionExecutionError('End date is invalid');
}
const holidaysSet = generateHolidaysSet(transformedHolidays);
const typedWeekendMask = weekendMask;
const days = endDateObject.diff(startDateObject, 'days') || 1;
const daysModule = Math.abs(days);
let iterationDate = (0, dayjs_1.default)(startDateObject);
if (days > options.maxDaysForWorkdaysFunctions) {
throw new errors_1.JexlFunctionExecutionError(`Days between dates should be less than ${options.maxDaysForWorkdaysFunctions}`);
}
let total = days < 0 ? days - 1 : days + 1;
for (let i = 0; i < daysModule; i++) {
const iterationDay = iterationDate.date();
const iterationMonth = iterationDate.month();
const iterationYear = iterationDate.year();
const iterationDayOfMonth = iterationDate.day();
const weekendIndex = iterationDayOfMonth ? iterationDayOfMonth - 1 : 6;
const isWorkingDay = !Number(typedWeekendMask[weekendIndex]);
const isHoliday = holidaysSet.has(`${iterationMonth}/${iterationDay}/${iterationYear}`);
if (!isWorkingDay || isHoliday) {
total = days < 0 ? total + 1 : total - 1;
}
iterationDate = iterationDate.add(days < 0 ? -1 : 1, 'day');
}
return total;
};
const WORKDAYINTL = (startDate, days, weekend, holidays = []) => {
const startDateObject = (0, dayjs_1.default)(startDate);
const coercedDays = coerceToNumber(days);
const transformedHolidays = Array.isArray(holidays) ? holidays : [holidays];
const weekendMask = getWeekendMask(weekend);
if (coercedDays < 0) {
throw new errors_1.JexlFunctionExecutionError('Days should be more than 0');
}
if (coercedDays > options.maxDaysForWorkdaysFunctions) {
throw new errors_1.JexlFunctionExecutionError(`Days should be less than ${options.maxDaysForWorkdaysFunctions}`);
}
validateWeekendMask(weekendMask);
validateArrayLikeValueMaxSize(holidays);
const holidaysSet = generateHolidaysSet(transformedHolidays);
const typedWeekendMask = weekendMask;
let workingDays = 0;
let iterationDate = startDateObject;
while (workingDays < coercedDays) {
iterationDate = iterationDate.add(1, 'day');
const iterationDay = iterationDate.date();
const iterationMonth = iterationDate.month();
const iterationYear = iterationDate.year();
const iterationDayOfMonth = iterationDate.day();
const weekendIndex = iterationDayOfMonth ? iterationDayOfMonth - 1 : 6;
const isWorkingDay = !Number(typedWeekendMask[weekendIndex]);
const isHoliday = holidaysSet.has(`${iterationMonth}/${iterationDay}/${iterationYear}`);
if (isWorkingDay && !isHoliday) {
workingDays++;
}
}
return iterationDate.format();
};
const EDATE = (date, months, format) => {
return (0, dayjs_1.default)(date)
.add(coerceToNumber(months), 'months')
.format(coerceToStringWithValidation(format) || undefined);
};
const DAYS360 = (startDate, endDate, method) => {
const startDateObject = (0, dayjs_1.default)(startDate);
const endDateObject = (0, dayjs_1.default)(endDate);
const endMonth = endDateObject.month();
const startMonth = startDateObject.month();
if (method) {
const endMonth = endDateObject.month();
const startMonth = startDateObject.month();
const startDay = startDateObject.date() === 31 ? 30 : startDateObject.date();
const endDay = endDateObject.date() === 31 ? 30 : endDateObject.date();
return 360 * (endDateObject.year() - startDateObject.year()) + 30 * (endMonth - startMonth) + (endDay - startDay);
}
const nextMonthStartDate = (0, dayjs_1.default)({ year: startDateObject.year(), month: startMonth + 1, day: 0 })
.add(-1, 'day');
const nextMonthEndDate = (0, dayjs_1.default)({ year: endDateObject.year(), month: endMonth + 1, day: 0 })
.add(-1, 'day');
const startDay = startDateObject.date() === nextMonthStartDate.date() ? 30 : startDateObject.date();
if (endDateObject.date() === nextMonthEndDate.date()) {
return startDay < 30
? 360 * (endDateObject.year() - startDateObject.year()) + 30 * (endMonth + 1 - startMonth) + (1 - startDay)
: 360 * (endDateObject.year() - startDateObject.year()) + 30 * (endMonth - startMonth) + (30 - startDay);
}
return 360 * (endDateObject.year() - startDateObject.year()) + 30 * (endMonth - startMonth) + (endDateObject.date() - startDay);
};
const DATEDIF = (startDate, endDate, unitType) => {
const startDateObject = (0, dayjs_1.default)(startDate);
const endDateObject = (0, dayjs_1.default)(endDate);
const coercedUnitType = coerceToStringWithValidation(unitType);
const longUnitType = shortUnitTypeToLongUnitType[coercedUnitType];
if (!startDateObject.isValid()) {
return NaN;
}
if (!endDateObject.isValid()) {
return NaN;
}
if (!longUnitType) {
return NaN;
}
return endDateObject.diff(startDateObject, longUnitType);
};
return {
WEEKNUM,
YEAR,
YEARFRAC,
NOW,
DATE,
DAY,
DAYS,
DATEVALUE,
ISOWEEKNUM,
MONTH,
TODAY,
EOMONTH,
NETWORKDAYSINTL,
WORKDAYINTL,
EDATE,
DAYS360,
DATEDIF,
};
}, {
maxDaysForWorkdaysFunctions: 3653,
});