UNPKG

@datastax/astra-db-ts

Version:
241 lines (240 loc) 10.6 kB
"use strict"; // Copyright Datastax, Inc // SPDX-License-Identifier: Apache-2.0 Object.defineProperty(exports, "__esModule", { value: true }); exports.date = exports.DataAPIDate = void 0; const constants_js_1 = require("../../lib/constants.js"); const constants_js_2 = require("../../documents/collections/ser-des/constants.js"); const index_js_1 = require("../../documents/index.js"); const constants_js_3 = require("../../documents/tables/ser-des/constants.js"); const utils_js_1 = require("../../documents/utils.js"); const utils_js_2 = require("../../lib/api/ser-des/utils.js"); const MillisecondsPerDay = 1000 * 60 * 60 * 24; class DataAPIDate { [constants_js_2.$SerializeForCollection]() { throw (0, utils_js_2.mkTypeUnsupportedForCollectionsError)('DataAPIDate', '_date', [ 'Use a native Javascript Date object', 'Use another date representation, such as a string, or an object containing the year, month, and date', ]); } ; [constants_js_3.$SerializeForTable](ctx) { return ctx.done(this.toString()); } ; static [constants_js_3.$DeserializeForTable](value, ctx) { return ctx.done(new DataAPIDate(value, false)); } static now() { return new DataAPIDate(new Date()); } static utcnow() { return new DataAPIDate(ofEpochDay(Math.floor(Date.now() / MillisecondsPerDay))); } static ofEpochDay(epochDays) { return new DataAPIDate(ofEpochDay(epochDays)); } static ofYearDay(year, dayOfYear) { return new DataAPIDate(ofYearDay(year, dayOfYear)); } constructor(i1, i2, i3) { Object.defineProperty(this, "year", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "month", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "date", { enumerable: true, configurable: true, writable: true, value: void 0 }); switch (arguments.length) { case 1: case 2: { if (typeof i1 === 'string') { [this.year, this.month, this.date] = parseDateStr(i1, i2 !== false); } else if (i1 instanceof Date) { if (isNaN(i1.getTime())) { throw new Error(`Invalid date '${i1.toString()}'; must be a valid (non-NaN) date`); } this.year = i1.getFullYear(); this.month = i1.getMonth() + 1; this.date = i1.getDate(); } else { throw (0, utils_js_1.mkInvArgsError)('new DataAPIDate', [['date', 'string | Date']], i1); } break; } case 3: { if (typeof i1 !== 'number' || typeof i2 !== 'number' || typeof i3 !== 'number') { throw (0, utils_js_1.mkInvArgsError)('new DataAPIDate', [['year', 'number'], ['month', 'number'], ['date', 'number']], i1, i2, i3); } validateDate(1, i1, i2, i3); this.year = i1; this.month = i2; this.date = i3; break; } default: { throw RangeError(`Invalid number of arguments; expected 1..=3, got ${arguments.length}`); } } Object.defineProperty(this, constants_js_1.$CustomInspect, { value: () => `DataAPIDate("${this.toString()}")`, }); } toDate(base) { if (!base || base instanceof Date) { return new Date(this.year, this.month - 1, this.date, base?.getHours() || 0, base?.getMinutes() || 0, base?.getSeconds() || 0, base?.getMilliseconds() || 0); } return new Date(this.year, this.month - 1, this.date, base.hours, base.minutes, base.seconds, base.nanoseconds / 1000000); } toDateUTC(base) { if (!base || base instanceof Date) { return new Date(Date.UTC(this.year, this.month - 1, this.date, base?.getUTCHours() || 0, base?.getUTCMinutes() || 0, base?.getUTCSeconds() || 0, base?.getUTCMilliseconds() || 0)); } return new Date(Date.UTC(this.year, this.month - 1, this.date, base.hours, base.minutes, base.seconds, base.nanoseconds / 1000000)); } plus(duration) { if (typeof duration === 'string') { duration = new index_js_1.DataAPIDuration(duration); } const date = new Date(this.year, this.month - 1, this.date); date.setDate(date.getDate() + duration.days); date.setMonth(date.getMonth() + duration.months); date.setMilliseconds(date.getMilliseconds() + duration.toMillis()); if (isNaN(date.getTime())) { throw new RangeError(`Invalid date resulting from adding duration '${duration.toString()}' to '${this.toString()}'`); } return new DataAPIDate(date); } toString() { const sign = (this.year < 0) ? '-' : (this.year >= 10000) ? '+' : ''; return `${sign}${Math.abs(this.year).toString().padStart(4, '0')}-${this.month.toString().padStart(2, '0')}-${this.date.toString().padStart(2, '0')}`; } /** * ##### Overview * * Compares this `DataAPIDate` to another `DataAPIDate` * * @example * ```ts * date('2004-09-14').compare(date(2004, 9, 14)) // 0 * * date('2004-09-14').compare(date(2004, 9, 15)) // -1 * * date('2004-09-15').compare(date(2004, 9, 14)) // 1 * ``` * * @param other - The other `DataAPIDate` to compare to * * @returns `0` if the dates are equal, `-1` if this date is before the other, and `1` if this date is after the other */ compare(other) { if (this.year !== other.year) { return this.year < other.year ? -1 : 1; } if (this.month !== other.month) { return this.month < other.month ? -1 : 1; } if (this.date !== other.date) { return this.date < other.date ? -1 : 1; } return 0; } equals(other) { return (other instanceof DataAPIDate) && this.compare(other) === 0; } } exports.DataAPIDate = DataAPIDate; exports.date = Object.assign((...params) => (params[0] instanceof DataAPIDate) ? params[0] : new DataAPIDate(...params), { now: DataAPIDate.now, utcnow: DataAPIDate.utcnow, ofEpochDay: DataAPIDate.ofEpochDay, ofYearDay: DataAPIDate.ofYearDay, }); const parseDateStr = (str, strict) => { return (strict) ? parseDateStrict(str) : parseDateQuick(str); }; const parseDateQuick = (str) => { const sign = (str.startsWith('-')) ? -1 : 1; const startIndex = (str.startsWith('+') || str.startsWith('-')) ? 1 : 0; const yearStr = str.substring(startIndex, str.indexOf('-', startIndex + 1)); const yearStrEnd = startIndex + yearStr.length; const year = parseInt(yearStr, 10); const month = parseInt(str.slice(yearStrEnd + 1, yearStr.length + 4), 10); const date = parseInt(str.slice(yearStrEnd + 4, yearStr.length + 7), 10); return [sign * year, month, date]; }; const DateRegex = /^([-+])?(\d{4,})-(\d{2})-(\d{2})$/; const parseDateStrict = (str) => { const match = DateRegex.exec(str); if (!match) { throw new Error(`Invalid date string '${str}'; must be in the format [+-]?YYY(Y+)-MM-DD, with zero-padded numbers as necessary`); } const sign = (match[1] === '-') ? -1 : 1; const ymd = [sign * parseInt(match[2], 10), parseInt(match[3], 10), parseInt(match[4], 10)]; validateDate(sign, ymd[0], ymd[1], ymd[2]); return ymd; }; const isLeapYear = (year) => { return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0); }; const DaysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; const DaysInMonthLeap = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; const validateDate = (sign, year, month, date) => { if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(date)) { throw new TypeError(`Invalid year: ${year}, month: ${month}, and/or date: ${date}; must be integers`); } if (month < 1 || 12 < month) { throw new RangeError(`Invalid month: ${month}; month must be between 1 and 12 (DataAPIDate's month is NOT zero-indexed)`); } const dim = isLeapYear(year) ? DaysInMonthLeap : DaysInMonth; if (date <= 0 || dim[month - 1] < date) { throw new RangeError(`Invalid date: ${date}; must be between 1 and ${dim[month - 1]} for month ${month} in year ${year}`); } if (sign < 0 && year === 0) { throw new RangeError(`Invalid year: ${year}; year may not be 0 for negative dates`); } }; const ofYearDay = (year, dayOfYear) => { if (typeof year !== 'number' || typeof dayOfYear !== 'number') { throw (0, utils_js_1.mkInvArgsError)('DataAPIDate.ofYearDay', [['year', 'number'], ['dayOfYear', 'number']], year, dayOfYear); } if (!Number.isInteger(year) || !Number.isInteger(dayOfYear)) { throw new TypeError(`Invalid year: ${year} and/or dayOfYear: ${dayOfYear}; must be integers`); } if (dayOfYear < 1 || 365 + (isLeapYear(year) ? 1 : 0) < dayOfYear) { throw new RangeError(`Invalid dayOfYear: ${dayOfYear}; must be between 1 and ${365 + (isLeapYear(year) ? 1 : 0)} for year ${year}`); } const date = new Date(0); date.setUTCFullYear(year, 0, dayOfYear); if (isNaN(date.getTime())) { throw new RangeError(`Invalid year: ${year}; must be within range [-271821, 275760] if using date.ofYearDay; use the DataAPIDate constructor directly for larger values`); } return date; }; const ofEpochDay = (epochDays) => { if (typeof epochDays !== 'number') { throw (0, utils_js_1.mkInvArgsError)('DataAPIDate.ofEpochDay', [['epochDays', 'number']], epochDays); } if (!Number.isInteger(epochDays)) { throw new TypeError(`Invalid epochDays: ${epochDays}; must be an integer`); } if (epochDays < -100000000 || 100000000 < epochDays) { throw new RangeError(`Invalid epochDays: ${epochDays}; must be within range [-100_000_000, 100_000_000] if using date.ofEpochDay; use the DataAPIDate constructor directly for larger values`); } return new Date(epochDays * MillisecondsPerDay); };