498 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			498 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| "use strict";
 | |
| Object.defineProperty(exports, "__esModule", { value: true });
 | |
| exports.CronDate = exports.DAYS_IN_MONTH = exports.DateMathOp = exports.TimeUnit = void 0;
 | |
| const luxon_1 = require("luxon");
 | |
| var TimeUnit;
 | |
| (function (TimeUnit) {
 | |
|     TimeUnit["Second"] = "Second";
 | |
|     TimeUnit["Minute"] = "Minute";
 | |
|     TimeUnit["Hour"] = "Hour";
 | |
|     TimeUnit["Day"] = "Day";
 | |
|     TimeUnit["Month"] = "Month";
 | |
|     TimeUnit["Year"] = "Year";
 | |
| })(TimeUnit || (exports.TimeUnit = TimeUnit = {}));
 | |
| var DateMathOp;
 | |
| (function (DateMathOp) {
 | |
|     DateMathOp["Add"] = "Add";
 | |
|     DateMathOp["Subtract"] = "Subtract";
 | |
| })(DateMathOp || (exports.DateMathOp = DateMathOp = {}));
 | |
| exports.DAYS_IN_MONTH = Object.freeze([31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]);
 | |
| /**
 | |
|  * CronDate class that wraps the Luxon DateTime object to provide
 | |
|  * a consistent API for working with dates and times in the context of cron.
 | |
|  */
 | |
| class CronDate {
 | |
|     #date;
 | |
|     #dstStart = null;
 | |
|     #dstEnd = null;
 | |
|     /**
 | |
|      * Maps the verb to the appropriate method
 | |
|      */
 | |
|     #verbMap = {
 | |
|         add: {
 | |
|             [TimeUnit.Year]: this.addYear.bind(this),
 | |
|             [TimeUnit.Month]: this.addMonth.bind(this),
 | |
|             [TimeUnit.Day]: this.addDay.bind(this),
 | |
|             [TimeUnit.Hour]: this.addHour.bind(this),
 | |
|             [TimeUnit.Minute]: this.addMinute.bind(this),
 | |
|             [TimeUnit.Second]: this.addSecond.bind(this),
 | |
|         },
 | |
|         subtract: {
 | |
|             [TimeUnit.Year]: this.subtractYear.bind(this),
 | |
|             [TimeUnit.Month]: this.subtractMonth.bind(this),
 | |
|             [TimeUnit.Day]: this.subtractDay.bind(this),
 | |
|             [TimeUnit.Hour]: this.subtractHour.bind(this),
 | |
|             [TimeUnit.Minute]: this.subtractMinute.bind(this),
 | |
|             [TimeUnit.Second]: this.subtractSecond.bind(this),
 | |
|         },
 | |
|     };
 | |
|     /**
 | |
|      * Constructs a new CronDate instance.
 | |
|      * @param {CronDate | Date | number | string} [timestamp] - The timestamp to initialize the CronDate with.
 | |
|      * @param {string} [tz] - The timezone to use for the CronDate.
 | |
|      */
 | |
|     constructor(timestamp, tz) {
 | |
|         const dateOpts = { zone: tz };
 | |
|         // Initialize the internal DateTime object based on the type of timestamp provided.
 | |
|         if (!timestamp) {
 | |
|             this.#date = luxon_1.DateTime.local();
 | |
|         }
 | |
|         else if (timestamp instanceof CronDate) {
 | |
|             this.#date = timestamp.#date;
 | |
|             this.#dstStart = timestamp.#dstStart;
 | |
|             this.#dstEnd = timestamp.#dstEnd;
 | |
|         }
 | |
|         else if (timestamp instanceof Date) {
 | |
|             this.#date = luxon_1.DateTime.fromJSDate(timestamp, dateOpts);
 | |
|         }
 | |
|         else if (typeof timestamp === 'number') {
 | |
|             this.#date = luxon_1.DateTime.fromMillis(timestamp, dateOpts);
 | |
|         }
 | |
|         else {
 | |
|             this.#date = luxon_1.DateTime.fromISO(timestamp, dateOpts);
 | |
|             this.#date.isValid || (this.#date = luxon_1.DateTime.fromRFC2822(timestamp, dateOpts));
 | |
|             this.#date.isValid || (this.#date = luxon_1.DateTime.fromSQL(timestamp, dateOpts));
 | |
|             this.#date.isValid || (this.#date = luxon_1.DateTime.fromFormat(timestamp, 'EEE, d MMM yyyy HH:mm:ss', dateOpts));
 | |
|         }
 | |
|         // Check for valid DateTime and throw an error if not valid.
 | |
|         if (!this.#date.isValid) {
 | |
|             throw new Error(`CronDate: unhandled timestamp: ${timestamp}`);
 | |
|         }
 | |
|         // Set the timezone if it is provided and different from the current zone.
 | |
|         if (tz && tz !== this.#date.zoneName) {
 | |
|             this.#date = this.#date.setZone(tz);
 | |
|         }
 | |
|     }
 | |
|     /**
 | |
|      * Determines if the given year is a leap year.
 | |
|      * @param {number} year - The year to check
 | |
|      * @returns {boolean} - True if the year is a leap year, false otherwise
 | |
|      * @private
 | |
|      */
 | |
|     static #isLeapYear(year) {
 | |
|         return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
 | |
|     }
 | |
|     /**
 | |
|      * Returns daylight savings start time.
 | |
|      * @returns {number | null}
 | |
|      */
 | |
|     get dstStart() {
 | |
|         return this.#dstStart;
 | |
|     }
 | |
|     /**
 | |
|      * Sets daylight savings start time.
 | |
|      * @param {number | null} value
 | |
|      */
 | |
|     set dstStart(value) {
 | |
|         this.#dstStart = value;
 | |
|     }
 | |
|     /**
 | |
|      * Returns daylight savings end time.
 | |
|      * @returns {number | null}
 | |
|      */
 | |
|     get dstEnd() {
 | |
|         return this.#dstEnd;
 | |
|     }
 | |
|     /**
 | |
|      * Sets daylight savings end time.
 | |
|      * @param {number | null} value
 | |
|      */
 | |
|     set dstEnd(value) {
 | |
|         this.#dstEnd = value;
 | |
|     }
 | |
|     /**
 | |
|      * Adds one year to the current CronDate.
 | |
|      */
 | |
|     addYear() {
 | |
|         this.#date = this.#date.plus({ years: 1 });
 | |
|     }
 | |
|     /**
 | |
|      * Adds one month to the current CronDate.
 | |
|      */
 | |
|     addMonth() {
 | |
|         this.#date = this.#date.plus({ months: 1 }).startOf('month');
 | |
|     }
 | |
|     /**
 | |
|      * Adds one day to the current CronDate.
 | |
|      */
 | |
|     addDay() {
 | |
|         this.#date = this.#date.plus({ days: 1 }).startOf('day');
 | |
|     }
 | |
|     /**
 | |
|      * Adds one hour to the current CronDate.
 | |
|      */
 | |
|     addHour() {
 | |
|         this.#date = this.#date.plus({ hours: 1 }).startOf('hour');
 | |
|     }
 | |
|     /**
 | |
|      * Adds one minute to the current CronDate.
 | |
|      */
 | |
|     addMinute() {
 | |
|         this.#date = this.#date.plus({ minutes: 1 }).startOf('minute');
 | |
|     }
 | |
|     /**
 | |
|      * Adds one second to the current CronDate.
 | |
|      */
 | |
|     addSecond() {
 | |
|         this.#date = this.#date.plus({ seconds: 1 });
 | |
|     }
 | |
|     /**
 | |
|      * Subtracts one year from the current CronDate.
 | |
|      */
 | |
|     subtractYear() {
 | |
|         this.#date = this.#date.minus({ years: 1 });
 | |
|     }
 | |
|     /**
 | |
|      * Subtracts one month from the current CronDate.
 | |
|      * If the month is 1, it will subtract one year instead.
 | |
|      */
 | |
|     subtractMonth() {
 | |
|         this.#date = this.#date.minus({ months: 1 }).endOf('month').startOf('second');
 | |
|     }
 | |
|     /**
 | |
|      * Subtracts one day from the current CronDate.
 | |
|      * If the day is 1, it will subtract one month instead.
 | |
|      */
 | |
|     subtractDay() {
 | |
|         this.#date = this.#date.minus({ days: 1 }).endOf('day').startOf('second');
 | |
|     }
 | |
|     /**
 | |
|      * Subtracts one hour from the current CronDate.
 | |
|      * If the hour is 0, it will subtract one day instead.
 | |
|      */
 | |
|     subtractHour() {
 | |
|         this.#date = this.#date.minus({ hours: 1 }).endOf('hour').startOf('second');
 | |
|     }
 | |
|     /**
 | |
|      * Subtracts one minute from the current CronDate.
 | |
|      * If the minute is 0, it will subtract one hour instead.
 | |
|      */
 | |
|     subtractMinute() {
 | |
|         this.#date = this.#date.minus({ minutes: 1 }).endOf('minute').startOf('second');
 | |
|     }
 | |
|     /**
 | |
|      * Subtracts one second from the current CronDate.
 | |
|      * If the second is 0, it will subtract one minute instead.
 | |
|      */
 | |
|     subtractSecond() {
 | |
|         this.#date = this.#date.minus({ seconds: 1 });
 | |
|     }
 | |
|     /**
 | |
|      * Adds a unit of time to the current CronDate.
 | |
|      * @param {TimeUnit} unit
 | |
|      */
 | |
|     addUnit(unit) {
 | |
|         this.#verbMap.add[unit]();
 | |
|     }
 | |
|     /**
 | |
|      * Subtracts a unit of time from the current CronDate.
 | |
|      * @param {TimeUnit} unit
 | |
|      */
 | |
|     subtractUnit(unit) {
 | |
|         this.#verbMap.subtract[unit]();
 | |
|     }
 | |
|     /**
 | |
|      * Handles a math operation.
 | |
|      * @param {DateMathOp} verb - {'add' | 'subtract'}
 | |
|      * @param {TimeUnit} unit - {'year' | 'month' | 'day' | 'hour' | 'minute' | 'second'}
 | |
|      */
 | |
|     invokeDateOperation(verb, unit) {
 | |
|         if (verb === DateMathOp.Add) {
 | |
|             this.addUnit(unit);
 | |
|             return;
 | |
|         }
 | |
|         if (verb === DateMathOp.Subtract) {
 | |
|             this.subtractUnit(unit);
 | |
|             return;
 | |
|         }
 | |
|         /* istanbul ignore next - this would only happen if an end user call the handleMathOp with an invalid verb */
 | |
|         throw new Error(`Invalid verb: ${verb}`);
 | |
|     }
 | |
|     /**
 | |
|      * Returns the day.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getDate() {
 | |
|         return this.#date.day;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the year.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getFullYear() {
 | |
|         return this.#date.year;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the day of the week.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getDay() {
 | |
|         const weekday = this.#date.weekday;
 | |
|         return weekday === 7 ? 0 : weekday;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the month.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getMonth() {
 | |
|         return this.#date.month - 1;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the hour.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getHours() {
 | |
|         return this.#date.hour;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the minutes.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getMinutes() {
 | |
|         return this.#date.minute;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the seconds.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getSeconds() {
 | |
|         return this.#date.second;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the milliseconds.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getMilliseconds() {
 | |
|         return this.#date.millisecond;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the time.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getTime() {
 | |
|         return this.#date.valueOf();
 | |
|     }
 | |
|     /**
 | |
|      * Returns the UTC day.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getUTCDate() {
 | |
|         return this.#getUTC().day;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the UTC year.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getUTCFullYear() {
 | |
|         return this.#getUTC().year;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the UTC day of the week.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getUTCDay() {
 | |
|         const weekday = this.#getUTC().weekday;
 | |
|         return weekday === 7 ? 0 : weekday;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the UTC month.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getUTCMonth() {
 | |
|         return this.#getUTC().month - 1;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the UTC hour.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getUTCHours() {
 | |
|         return this.#getUTC().hour;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the UTC minutes.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getUTCMinutes() {
 | |
|         return this.#getUTC().minute;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the UTC seconds.
 | |
|      * @returns {number}
 | |
|      */
 | |
|     getUTCSeconds() {
 | |
|         return this.#getUTC().second;
 | |
|     }
 | |
|     /**
 | |
|      * Returns the UTC milliseconds.
 | |
|      * @returns {string | null}
 | |
|      */
 | |
|     toISOString() {
 | |
|         return this.#date.toUTC().toISO();
 | |
|     }
 | |
|     /**
 | |
|      * Returns the date as a JSON string.
 | |
|      * @returns {string | null}
 | |
|      */
 | |
|     toJSON() {
 | |
|         return this.#date.toJSON();
 | |
|     }
 | |
|     /**
 | |
|      * Sets the day.
 | |
|      * @param d
 | |
|      */
 | |
|     setDate(d) {
 | |
|         this.#date = this.#date.set({ day: d });
 | |
|     }
 | |
|     /**
 | |
|      * Sets the year.
 | |
|      * @param y
 | |
|      */
 | |
|     setFullYear(y) {
 | |
|         this.#date = this.#date.set({ year: y });
 | |
|     }
 | |
|     /**
 | |
|      * Sets the day of the week.
 | |
|      * @param d
 | |
|      */
 | |
|     setDay(d) {
 | |
|         this.#date = this.#date.set({ weekday: d });
 | |
|     }
 | |
|     /**
 | |
|      * Sets the month.
 | |
|      * @param m
 | |
|      */
 | |
|     setMonth(m) {
 | |
|         this.#date = this.#date.set({ month: m + 1 });
 | |
|     }
 | |
|     /**
 | |
|      * Sets the hour.
 | |
|      * @param h
 | |
|      */
 | |
|     setHours(h) {
 | |
|         this.#date = this.#date.set({ hour: h });
 | |
|     }
 | |
|     /**
 | |
|      * Sets the minutes.
 | |
|      * @param m
 | |
|      */
 | |
|     setMinutes(m) {
 | |
|         this.#date = this.#date.set({ minute: m });
 | |
|     }
 | |
|     /**
 | |
|      * Sets the seconds.
 | |
|      * @param s
 | |
|      */
 | |
|     setSeconds(s) {
 | |
|         this.#date = this.#date.set({ second: s });
 | |
|     }
 | |
|     /**
 | |
|      * Sets the milliseconds.
 | |
|      * @param s
 | |
|      */
 | |
|     setMilliseconds(s) {
 | |
|         this.#date = this.#date.set({ millisecond: s });
 | |
|     }
 | |
|     /**
 | |
|      * Returns the date as a string.
 | |
|      * @returns {string}
 | |
|      */
 | |
|     toString() {
 | |
|         return this.toDate().toString();
 | |
|     }
 | |
|     /**
 | |
|      * Returns the date as a Date object.
 | |
|      * @returns {Date}
 | |
|      */
 | |
|     toDate() {
 | |
|         return this.#date.toJSDate();
 | |
|     }
 | |
|     /**
 | |
|      * Returns true if the day is the last day of the month.
 | |
|      * @returns {boolean}
 | |
|      */
 | |
|     isLastDayOfMonth() {
 | |
|         const { day, month } = this.#date;
 | |
|         // Special handling for February in leap years
 | |
|         if (month === 2) {
 | |
|             const isLeap = CronDate.#isLeapYear(this.#date.year);
 | |
|             return day === exports.DAYS_IN_MONTH[month - 1] - (isLeap ? 0 : 1);
 | |
|         }
 | |
|         // For other months, check against the static map
 | |
|         return day === exports.DAYS_IN_MONTH[month - 1];
 | |
|     }
 | |
|     /**
 | |
|      * Returns true if the day is the last weekday of the month.
 | |
|      * @returns {boolean}
 | |
|      */
 | |
|     isLastWeekdayOfMonth() {
 | |
|         const { day, month } = this.#date;
 | |
|         // Get the last day of the current month
 | |
|         let lastDay;
 | |
|         if (month === 2) {
 | |
|             // Special handling for February
 | |
|             lastDay = exports.DAYS_IN_MONTH[month - 1] - (CronDate.#isLeapYear(this.#date.year) ? 0 : 1);
 | |
|         }
 | |
|         else {
 | |
|             lastDay = exports.DAYS_IN_MONTH[month - 1];
 | |
|         }
 | |
|         // Check if the current day is within 7 days of the end of the month
 | |
|         return day > lastDay - 7;
 | |
|     }
 | |
|     /**
 | |
|      * Primarily for internal use.
 | |
|      * @param {DateMathOp} op - The operation to perform.
 | |
|      * @param {TimeUnit} unit - The unit of time to use.
 | |
|      * @param {number} [hoursLength] - The length of the hours. Required when unit is not month or day.
 | |
|      */
 | |
|     applyDateOperation(op, unit, hoursLength) {
 | |
|         if (unit === TimeUnit.Month || unit === TimeUnit.Day) {
 | |
|             this.invokeDateOperation(op, unit);
 | |
|             return;
 | |
|         }
 | |
|         const previousHour = this.getHours();
 | |
|         this.invokeDateOperation(op, unit);
 | |
|         const currentHour = this.getHours();
 | |
|         const diff = currentHour - previousHour;
 | |
|         if (diff === 2) {
 | |
|             if (hoursLength !== 24) {
 | |
|                 this.dstStart = currentHour;
 | |
|             }
 | |
|         }
 | |
|         else if (diff === 0 && this.getMinutes() === 0 && this.getSeconds() === 0) {
 | |
|             if (hoursLength !== 24) {
 | |
|                 this.dstEnd = currentHour;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     /**
 | |
|      * Returns the UTC date.
 | |
|      * @private
 | |
|      * @returns {DateTime}
 | |
|      */
 | |
|     #getUTC() {
 | |
|         return this.#date.toUTC();
 | |
|     }
 | |
| }
 | |
| exports.CronDate = CronDate;
 | |
| exports.default = CronDate;
 |