UNPKG

@barchart/common-js

Version:
547 lines (541 loc) 16.2 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var Day_exports = {}; __export(Day_exports, { default: () => Day }); module.exports = __toCommonJS(Day_exports); var assert = __toESM(require("./assert.js")); var comparators = __toESM(require("./../collections/sorting/comparators.js")); var is = __toESM(require("./is.js")); var import_ComparatorBuilder = __toESM(require("./../collections/sorting/ComparatorBuilder.js")); var import_DayFormatType = __toESM(require("./DayFormatType.js")); class Day { #year; #month; #day; /** * @param {number} year * @param {number} month * @param {number} day */ constructor(year, month, day) { if (!Day.validate(year, month, day)) { throw new Error(`Unable to instantiate [ Day ], input is invalid [ ${year} ], [ ${month} ], [ ${day} ]`); } this.#year = year; this.#month = month; this.#day = day; } /** * Calculates a new {@link Day} in the future (or past). * * @public * @param {number} days - The number of days to add (negative numbers can be used for subtraction). * @param {boolean=} inverse - If true, the sign of the "days" value will be flipped. * @returns {Day} */ addDays(days, inverse) { assert.argumentIsRequired(days, "days", Number); assert.argumentIsOptional(inverse, "inverse", Boolean); assert.argumentIsValid(days, "days", is.large, "is an integer"); let totalDaysToShift; if (is.boolean(inverse) && inverse) { totalDaysToShift = days * -1; } else { totalDaysToShift = days; } const positive = is.positive(totalDaysToShift); let shiftedDay = this.#day; let shiftedMonth = this.#month; let shiftedYear = this.#year; while (totalDaysToShift !== 0) { let monthDaysAvailable; let monthDaysToShift; if (positive) { monthDaysAvailable = Day.getDaysInMonth(shiftedYear, shiftedMonth) - shiftedDay; monthDaysToShift = Math.min(totalDaysToShift, monthDaysAvailable); } else { monthDaysAvailable = 1 - shiftedDay; monthDaysToShift = Math.max(totalDaysToShift, monthDaysAvailable); } totalDaysToShift = totalDaysToShift - monthDaysToShift; if (totalDaysToShift === 0) { shiftedDay = shiftedDay + monthDaysToShift; } else if (positive) { shiftedMonth++; if (shiftedMonth > 12) { shiftedYear++; shiftedMonth = 1; } shiftedDay = 0; } else { shiftedMonth--; if (shiftedMonth < 1) { shiftedYear--; shiftedMonth = 12; } shiftedDay = Day.getDaysInMonth(shiftedYear, shiftedMonth) + 1; } } return new Day(shiftedYear, shiftedMonth, shiftedDay); } /** * Calculates a new {@link Day} in the past (or future). * * @public * @param {number} days - The number of days to subtract (negative numbers can be used for addition). * @returns {Day} */ subtractDays(days) { return this.addDays(days, true); } /** * Calculates a new {@link Day} in the future (or past). If the new date is at the end of * the month and the new month has fewer days than the current month, days will be subtracted * as necessary (e.g. adding one month to March 31 will return April 30). * * @public * @param {number} months - The number of months to add (negative numbers can be used for subtraction). * @param {boolean=} inverse - If true, the sign of the "days" value will be flipped. * @returns {Day} */ addMonths(months, inverse) { assert.argumentIsRequired(months, "months", Number); assert.argumentIsOptional(inverse, "inverse", Boolean); assert.argumentIsValid(months, "months", is.large, "is an integer"); let totalMonthsToShift; if (is.boolean(inverse) && inverse) { totalMonthsToShift = months * -1; } else { totalMonthsToShift = months; } const monthsToShift = totalMonthsToShift % 12; const yearsToShift = (totalMonthsToShift - monthsToShift) / 12; let shiftedYear = this.year + yearsToShift; let shiftedMonth = this.month + monthsToShift; let shiftedDay = this.day; if (shiftedMonth > 12) { shiftedYear = shiftedYear + 1; shiftedMonth = shiftedMonth - 12; } if (shiftedMonth < 1) { shiftedYear = shiftedYear - 1; shiftedMonth = shiftedMonth + 12; } while (!Day.validate(shiftedYear, shiftedMonth, shiftedDay)) { shiftedDay = shiftedDay - 1; } return new Day(shiftedYear, shiftedMonth, shiftedDay); } /** * Calculates a new {@link Day} in the past (or future). * * @public * @param {number} months - The number of months to subtract (negative numbers can be used for addition). * @returns {Day} */ subtractMonths(months) { return this.addMonths(months, true); } /** * Calculates a new {@link Day} in the future (or past). If the new date is at the end of * the month and the new month has fewer days than the current month, days will be subtracted * as necessary (e.g. adding one year to February 29 will return February 28). * * @public * @param {number} years - The number of years to add (negative numbers can be used for subtraction). * @param {boolean=} inverse - If true, the sign of the "days" value will be flipped. * @returns {Day} */ addYears(years, inverse) { assert.argumentIsRequired(years, "years", Number); assert.argumentIsOptional(inverse, "inverse", Boolean); assert.argumentIsValid(years, "years", is.large, "is an integer"); let yearsToShift; if (is.boolean(inverse) && inverse) { yearsToShift = years * -1; } else { yearsToShift = years; } let shiftedYear = this.year + yearsToShift; let shiftedMonth = this.month; let shiftedDay = this.day; while (!Day.validate(shiftedYear, shiftedMonth, shiftedDay)) { shiftedDay = shiftedDay - 1; } return new Day(shiftedYear, shiftedMonth, shiftedDay); } /** * Calculates a new {@link Day} in the past (or future). * * @public * @param {number} years - The number of years to subtract (negative numbers can be used for addition). * @returns {Day} */ subtractYears(years) { return this.addYears(years, true); } /** * Returns a new {@link Day} instance for the start of the month referenced by the current instance. * * @public * @returns {Day} */ getStartOfMonth() { return new Day(this.year, this.month, 1); } /** * Returns a new instance for the {@link Day} end of the month referenced by the current instance. * * @public * @returns {Day} */ getEndOfMonth() { return new Day(this.year, this.month, Day.getDaysInMonth(this.year, this.month)); } /** * Indicates if the current {@link Day} instance occurs before another day. * * @public * @param {Day} other * @returns {boolean} */ getIsBefore(other) { return Day.compareDays(this, other) < 0; } /** * Indicates if the current {@link Day} instance occurs after another day. * * @public * @param {Day} other * @returns {boolean} */ getIsAfter(other) { return Day.compareDays(this, other) > 0; } /** * Indicates the current day falls between two other days, inclusive * of the range boundaries. * * @public * @param {Day=} first * @param {Day=} last * @returns {boolean} */ getIsContained(first, last) { assert.argumentIsOptional(first, "first", Day, "Day"); assert.argumentIsOptional(last, "last", Day, "Day"); let notAfter; let notBefore; if (first && last && first.getIsAfter(last)) { notBefore = false; notAfter = false; } else { notAfter = !(last instanceof Day) || !this.getIsAfter(last); notBefore = !(first instanceof Day) || !this.getIsBefore(first); } return notAfter && notBefore; } /** * Indicates if another {@link Day} refers to the same moment. * * @public * @param {Day} other * @returns {boolean} */ getIsEqual(other) { return Day.compareDays(this, other) === 0; } /** * Calculates and returns name of the day of the week (e.g. Monday, Tuesday, Wednesday, etc.). * * @public * @returns {string} */ getName() { const count = Day.countDaysBetween(REFERENCE_MONDAY, this); let index = count % NAMES_OF_DAYS.length; if (index < 0) { index = index + NAMES_OF_DAYS.length; } return NAMES_OF_DAYS[index]; } /** * The year. * * @public * @returns {number} */ get year() { return this.#year; } /** * The month of the year (January is one, December is twelve). * * @public * @returns {number} */ get month() { return this.#month; } /** * The day of the month. * * @public * @returns {number} */ get day() { return this.#day; } /** * Outputs the date as the formatted string: {year}-{month}-{day}. * * @public * @returns {string} */ format() { return `${leftPad(this.#year, 4, "0")}-${leftPad(this.#month, 2, "0")}-${leftPad(this.#day, 2, "0")}`; } /** * Returns the JSON representation. * * @public * @returns {string} */ toJSON() { return this.format(); } /** * Clones a {@link Day} instance. * * @public * @static * @param {Day} value * @returns {Day} */ static clone(value) { assert.argumentIsRequired(value, "value", Day, "Day"); return new Day(value.year, value.month, value.day); } /** * Converts a string (which matches the output of {@link Day#format}) into * a {@link Day} instance. * * @public * @static * @param {string} value * @param {DayFormatType=} type * @returns {Day} */ static parse(value, type) { assert.argumentIsRequired(value, "value", String); let t; if (type instanceof import_DayFormatType.default) { t = type; } else { t = import_DayFormatType.default.YYYY_MM_DD; } const match = value.match(t.regex); if (match === null) { throw new Error(`Unable to parse value as Day [ ${value} ]`); } return new Day(parseInt(match[t.yearIndex]) + t.yearShift, parseInt(match[t.monthIndex]), parseInt(match[t.dayIndex])); } /** * Creates a {@link Day} from the year, month, and day properties (in local time) * of the {@link Date} argument. * * @public * @static * @param {Date} date * @returns {Day} */ static fromDate(date) { assert.argumentIsRequired(date, "date", Date); return new Day(date.getFullYear(), date.getMonth() + 1, date.getDate()); } /** * Creates a {@link Day} from the year, month, and day properties (in UTC) * of the {@link Date} argument. * * @public * @static * @param {Date} date * @returns {Day} */ static fromDateUtc(date) { assert.argumentIsRequired(date, "date", Date); return new Day(date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate()); } /** * Returns a {@link Day} instance using today's local date. * * @public * @static * @returns {Day} */ static getToday() { return Day.fromDate(/* @__PURE__ */ new Date()); } /** * Returns true if the year, month, and day combination is valid; otherwise false. * * @public * @static * @param {number} year * @param {number} month * @param {number} day * @returns {boolean} */ static validate(year, month, day) { return is.integer(year) && is.integer(month) && is.integer(day) && !(month < 1) && !(month > 12) && !(day < 1) && !(day > Day.getDaysInMonth(year, month)); } /** * Returns the number of days in a given month. * * @public * @static * @param {number} year - The year number (e.g. 2017) * @param {number} month - The month number (e.g. 2 is February) * @returns {number} */ static getDaysInMonth(year, month) { switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: { return 31; } case 4: case 6: case 9: case 11: { return 30; } case 2: { if (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) { return 29; } else { return 28; } } } } /** * A comparator function for {@link Day} instances. * * @public * @static * @param {Day} a * @param {Day} b * @returns {number} */ static compareDays(a, b) { assert.argumentIsRequired(a, "a", Day, "Day"); assert.argumentIsRequired(b, "b", Day, "Day"); return comparator(a, b); } /** * Calculates the number of days between two {@link Day} instances (may return * a negative value). * * @public * @static * @param {Day} a * @param {Day} b * @returns {number} */ static countDaysBetween(a, b) { assert.argumentIsRequired(a, "a", Day, "Day"); assert.argumentIsRequired(b, "b", Day, "Day"); if (a.getIsEqual(b)) { return 0; } let start; let end; let reversed = b.getIsBefore(a); if (reversed) { start = b; end = a; } else { start = a; end = b; } let currentMonth = start.month; let currentYear = start.year; let counter = 0 - start.day; while (!(currentMonth === end.month && currentYear === end.year)) { counter = counter + Day.getDaysInMonth(currentYear, currentMonth); if (currentMonth === 12) { currentMonth = 1; currentYear = currentYear + 1; } else { currentMonth = currentMonth + 1; } } counter = counter + end.day; if (reversed) { counter = counter * -1; } return counter; } /** * Returns a string representation. * * @public * @returns {string} */ toString() { return "[Day]"; } } function leftPad(value, digits, character) { let string = value.toString(); let padding = digits - string.length; return `${character.repeat(padding)}${string}`; } const comparator = import_ComparatorBuilder.default.startWith((a, b) => comparators.compareNumbers(a.year, b.year)).thenBy((a, b) => comparators.compareNumbers(a.month, b.month)).thenBy((a, b) => comparators.compareNumbers(a.day, b.day)).toComparator(); const NAMES_OF_DAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]; const REFERENCE_MONDAY = new Day(2024, 1, 1); { const cjsExports = module.exports; const cjsDefaultExport = cjsExports && cjsExports.__esModule ? cjsExports.default : cjsExports; if (cjsDefaultExport && (typeof cjsDefaultExport === 'function' || typeof cjsDefaultExport === 'object')) { Object.keys(cjsExports).forEach((key) => { if (key !== 'default' && key !== '__esModule') { cjsDefaultExport[key] = cjsExports[key]; } }); } module.exports = cjsDefaultExport; }