@datastax/astra-db-ts
Version:
Data API TypeScript client
241 lines (240 loc) • 10.6 kB
JavaScript
;
// 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);
};