UNPKG

nhb-toolbox

Version:

A versatile collection of smart, efficient, and reusable utility functions, classes and types for everyday development needs.

145 lines (144 loc) 6.8 kB
import { isValidArray } from '../../guards/non-primitives.js'; import { isUndefined } from '../../guards/primitives.js'; import { INTERNALS } from '../constants.js'; export const businessPlugin = ($Chronos) => { const { internalDate: $Date, withOrigin, cast, offset } = $Chronos[INTERNALS]; const _buildWeekendMask = (weekDef, length) => { const weekendDays = isValidArray(weekDef) ? [...weekDef].sort() : Array.from({ length }, (_, i) => (weekDef + 7 - 1 - i) % 7).sort(); const mask = new Array(7).fill(true); for (const d of weekendDays) mask[d] = false; return mask; }; const _countDays = (sWeek, days, mask, step = 1, wd = true) => { let total = Math.floor(days / 7) * mask.filter((m) => (wd ? Boolean(m) : !m)).length; let dayIndex = ((sWeek % 7) + 7) % 7; for (let i = 0; i < days % 7; i++) { const isMatch = wd ? Boolean(mask[dayIndex]) : !mask[dayIndex]; if (isMatch) total++; dayIndex = (dayIndex + step + 7) % 7; } return total; }; $Chronos.prototype.isWeekend = function (wDef = 0, wLen = 2) { const day = $Date(this).getDay(); if (isValidArray(wDef)) { return wDef.includes(day); } const lastDayOfWeek = (wDef + 6) % 7; const weekendDays = Array.from({ length: wLen }, (_, i) => (lastDayOfWeek - i + 7) % 7); return weekendDays.includes(day); }; $Chronos.prototype.isWorkday = function (wDef = 0, wLen = 2) { return !this.isWeekend(wDef, wLen); }; $Chronos.prototype.nextWorkday = function (wDef = 0, wLen = 2) { let nxtWrk = this.addDays(1); while (nxtWrk.isWeekend(wDef, wLen)) { nxtWrk = nxtWrk.addDays(1); } return withOrigin(nxtWrk.startOf('day'), 'nextWorkday', offset(this), this.timeZoneName, this.timeZoneId, this.$tzTracker); }; $Chronos.prototype.previousWorkday = function (wDef = 0, wLen = 2) { let prevWrk = this.addDays(-1); while (prevWrk.isWeekend(wDef, wLen)) { prevWrk = prevWrk.addDays(-1); } return withOrigin(prevWrk.startOf('day'), 'previousWorkday', offset(this), this.timeZoneName, this.timeZoneId, this.$tzTracker); }; $Chronos.prototype.nextWeekend = function (wDef = 0, wLen = 2) { let nxtWknd = this.addDays(1); while (!nxtWknd.isWeekend(wDef, wLen)) { nxtWknd = nxtWknd.addDays(1); } return withOrigin(nxtWknd.startOf('day'), 'nextWeekend', offset(this), this.timeZoneName, this.timeZoneId, this.$tzTracker); }; $Chronos.prototype.previousWeekend = function (wDef = 0, wLen = 2) { let prevWknd = this.addDays(-1); while (!prevWknd.isWeekend(wDef, wLen)) { prevWknd = prevWknd.addDays(-1); } return withOrigin(prevWknd.startOf('day'), 'previousWeekend', offset(this), this.timeZoneName, this.timeZoneId, this.$tzTracker); }; $Chronos.prototype.workdaysBetween = function (to, wDef = 0, wLen = 2) { const end = cast(to).startOf('day'); const start = this.startOf('day'); if (start.isSame(end, 'day')) return 0; const step = start.isBefore(end, 'day') ? 1 : -1; const totalDays = Math.abs(end.diff(start, 'day')); const weekendMask = _buildWeekendMask(wDef, wLen); const startWeekday = (start.isoWeekDay + step) % 7; return _countDays(startWeekday, totalDays, weekendMask, step); }; $Chronos.prototype.weekendsBetween = function (to, wDef = 0, wLen = 2) { const end = cast(to).startOf('day'); const start = this.startOf('day'); if (start.isSame(end, 'day')) return 0; const step = start.isBefore(end, 'day') ? 1 : -1; const totalDays = Math.abs(end.diff(start, 'day')); const weekendMask = _buildWeekendMask(wDef, wLen); const startWeekday = (start.isoWeekDay + step) % 7; return _countDays(startWeekday, totalDays, weekendMask, step, false); }; $Chronos.prototype.workdaysInMonth = function (wDef = 0, wLen = 2) { const daysInMonth = this.daysInMonth(); const weekendMask = _buildWeekendMask(wDef, wLen); const startWeekday = this.startOf('month').isoWeekDay % 7; return _countDays(startWeekday, daysInMonth, weekendMask); }; $Chronos.prototype.weekendsInMonth = function (wDef = 0, wLen = 2) { const daysInMonth = this.daysInMonth(); const weekendMask = _buildWeekendMask(wDef, wLen); const startWeekday = this.startOf('month').isoWeekDay % 7; return _countDays(startWeekday, daysInMonth, weekendMask, 1, false); }; $Chronos.prototype.workdaysInYear = function (wDef = 0, wLen = 2) { const daysInYear = this.isLeapYear() ? 366 : 365; const weekendMask = _buildWeekendMask(wDef, wLen); const startWeekday = this.startOf('year').isoWeekDay % 7; return _countDays(startWeekday, daysInYear, weekendMask); }; $Chronos.prototype.weekendsInYear = function (wDef = 0, wLen = 2) { const daysInYear = this.isLeapYear() ? 366 : 365; const weekendMask = _buildWeekendMask(wDef, wLen); const startWeekday = this.startOf('year').isoWeekDay % 7; return _countDays(startWeekday, daysInYear, weekendMask, 1, false); }; $Chronos.prototype.isBusinessHour = function (options) { const _isBusinessHour = () => { const { businessStartHour = 9, businessEndHour = 17 } = options ?? {}; if (businessStartHour === businessEndHour) return false; const hour = $Date(this).getHours(); if (businessStartHour < businessEndHour) { return hour >= businessStartHour && hour < businessEndHour; } else { return hour >= businessStartHour || hour < businessEndHour; } }; if (options && 'weekendDays' in options && !isUndefined(options?.weekendDays)) { return this.isWorkday(options.weekendDays) && _isBusinessHour(); } const { weekStartsOn = 0, weekendLength = 2 } = (options ?? {}); return this.isWorkday(weekStartsOn, weekendLength) && _isBusinessHour(); }; $Chronos.prototype.toFiscalQuarter = function (startMonth = 7) { const adjusted = (this.isoMonth - startMonth + 12) % 12; return (Math.floor(adjusted / 3) + 1); }; $Chronos.prototype.toAcademicYear = function () { const year = this.year; if (this.month >= 6) { return `${year}-${year + 1}`; } else { return `${year - 1}-${year}`; } }; };