rrule-rust
Version:
RRule implementation for browsers and Node.js written in Rust
413 lines (412 loc) • 14.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DateTime = void 0;
/**
* Represents a date and time, either local or UTC.
*
* The generic type parameter `T` determines whether this is:
* - `DateTime<Time>` - A date with time information
* - `DateTime<undefined>` - A date without time information
*
* @example
* ```typescript
* // Create a date-only DateTime
* const date = DateTime.date(2024, 1, 15);
* console.log(date.year, date.month, date.day); // 2024, 1, 15
*
* // Create a local DateTime
* const local = DateTime.local(2024, 1, 15, 14, 30, 0);
* console.log(local.time?.utc); // false
*
* // Create a UTC DateTime
* const utc = DateTime.utc(2024, 1, 15, 14, 30, 0);
* console.log(utc.time?.utc); // true
* ```
*/
class DateTime {
constructor(year, month, day, time) {
this.year = year;
this.month = month;
this.day = day;
this.time = time;
}
static create(year, month, day, hour, minute, second, utc) {
if (hour !== undefined &&
minute !== undefined &&
second !== undefined &&
utc !== undefined) {
const dt = new DateTime(year, month, day, {
hour,
minute,
second,
utc,
});
dt.offset = utc ? 0 : undefined;
return dt;
}
else {
return new DateTime(year, month, day, undefined);
}
}
/**
* Creates a new date-only DateTime object (without time information).
*
* @param year - Year component (e.g., 2024)
* @param month - Month component (1-12)
* @param day - Day component (1-31)
* @returns A DateTime instance without time information
*
* @example
* ```typescript
* const date = DateTime.date(2024, 1, 15);
* console.log(date.toString()); // "20240115"
* ```
*/
static date(year, month, day) {
return this.create(year, month, day);
}
/**
* Creates a new local DateTime object (with time in local timezone).
* This method is shorthand for `DateTime.create` with `utc` set to `false`.
*
* @param year - Year component (e.g., 2024)
* @param month - Month component (1-12)
* @param day - Day component (1-31)
* @param hour - Hour component (0-23)
* @param minute - Minute component (0-59)
* @param second - Second component (0-59)
* @returns A DateTime instance with local time
*
* @example
* ```typescript
* const value = DateTime.local(2024, 1, 15, 14, 30, 0);
* console.log(value.time.utc); // false
* console.log(value.toString()); // "20240115T143000"
* ```
*/
static local(year, month, day, hour, minute, second) {
return DateTime.create(year, month, day, hour, minute, second, false);
}
/**
* Creates a new UTC DateTime object (with time in UTC timezone).
* This method is shorthand for `DateTime.create` with `utc` set to `true`.
*
* @param year - Year component (e.g., 2024)
* @param month - Month component (1-12)
* @param day - Day component (1-31)
* @param hour - Hour component (0-23)
* @param minute - Minute component (0-59)
* @param second - Second component (0-59)
* @returns A DateTime instance with UTC time
*
* @example
* ```typescript
* const value = DateTime.utc(2024, 1, 15, 14, 30, 0);
* console.log(value.time.utc); // true
* console.log(value.toString()); // "20240115T143000Z"
* ```
*/
static utc(year, month, day, hour, minute, second) {
return DateTime.create(year, month, day, hour, minute, second, true);
}
/**
* Creates a new UTC DateTime object from a JavaScript Date object.
*
* The resulting DateTime will have `utc` set to `true` and will represent
* the same moment in time as the input Date object.
*
* @param date - A JavaScript Date object
* @returns A DateTime instance with UTC time
*
* @example
* ```typescript
* const jsDate = new Date('2024-01-15T14:30:00.000Z');
* const datetime = DateTime.fromDate(jsDate);
* console.log(datetime.year); // 2024
* console.log(datetime.month); // 1
* console.log(datetime.day); // 15
* console.log(datetime.time.hour); // 14
* console.log(datetime.time.minute); // 30
* console.log(datetime.time.second); // 0
* console.log(datetime.time.utc); // true
* ```
*/
static fromDate(date) {
return DateTime.utc(date.getUTCFullYear(), date.getUTCMonth() + 1, date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds());
}
/**
* Creates a new UTC DateTime object from a Unix timestamp in milliseconds.
*
* The resulting DateTime will have `utc` set to `true` and will represent
* the moment in time corresponding to the given timestamp.
*
* @param timestamp - Unix timestamp in milliseconds since January 1, 1970 00:00:00 UTC
* @returns A DateTime instance with UTC time
*
* @example
* ```typescript
* const timestamp = Date.UTC(2024, 0, 15, 14, 30, 0); // 1705329000000
* const datetime = DateTime.fromTimestamp(timestamp);
* console.log(datetime.year); // 2024
* console.log(datetime.month); // 1
* console.log(datetime.day); // 15
* console.log(datetime.time.hour); // 14
* console.log(datetime.time.minute); // 30
* console.log(datetime.time.second); // 0
* console.log(datetime.time.utc); // true
* ```
*/
static fromTimestamp(timestamp) {
return DateTime.fromDate(new Date(timestamp));
}
static fromPlain(plain) {
if ('hour' in plain) {
return DateTime.create(plain.year, plain.month, plain.day, plain.hour, plain.minute, plain.second, plain.utc ?? false);
}
return DateTime.create(plain.year, plain.month, plain.day);
}
/**
* Creates a new DateTime object from a string representation according to RFC 5545.
*
* Supported formats:
* - `YYYYMMDD` - Date only (e.g., "20240115")
* - `YYYYMMDDTHHMMSS` - Local date-time (e.g., "20240115T143000")
* - `YYYYMMDDTHHMMSSZ` - UTC date-time (e.g., "20240115T143000Z")
*
* @param str - The RFC 5545 formatted string
* @returns A DateTime instance
* @throws {TypeError} If the string format is invalid
*
* @example
* ```typescript
* // Parse date only
* const date = DateTime.fromString("20240115");
*
* // Parse local date-time
* const local = DateTime.fromString("20240115T143000");
*
* // Parse UTC date-time
* const utc = DateTime.fromString("20240115T143000Z");
* ```
*/
static fromString(str) {
if (!(str.length === 8 || (str.length <= 16 && str.length >= 15))) {
throw new TypeError('Invalid date time string');
}
const year = parseInt(str.slice(0, 4));
const month = parseInt(str.slice(4, 6));
const day = parseInt(str.slice(6, 8));
let hour = undefined;
let minute = undefined;
let second = undefined;
let utc = undefined;
if (isNaN(year) || isNaN(month) || isNaN(day)) {
throw new TypeError('Invalid date');
}
if (str.length > 8) {
hour = parseInt(str.slice(9, 11));
minute = parseInt(str.slice(11, 13));
second = parseInt(str.slice(13, 15));
utc = str.endsWith('Z');
if (isNaN(hour) || isNaN(minute) || isNaN(second)) {
throw new TypeError('Invalid time');
}
return DateTime.create(year, month, day, hour, minute, second, utc);
}
else {
return DateTime.create(year, month, day);
}
}
/** @internal */
static fromNumbers(year, month, day, hour, minute, second, offset) {
const dt = new DateTime(year, month, day, hour !== -1
? {
hour,
minute,
second,
utc: offset === 0,
}
: undefined);
dt.offset = offset === -1 ? undefined : offset;
return dt;
}
/** @internal */
static fromInt32Array(arr) {
return this.fromNumbers(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5], arr[6]);
}
/** @internal */
static fromFlatInt32Array(raw) {
const result = [];
for (let i = 0; i < raw.length; i += 7) {
result.push(this.fromNumbers(raw[i], raw[i + 1], raw[i + 2], raw[i + 3], raw[i + 4], raw[i + 5], raw[i + 6]));
}
return result;
}
/** @internal */
static toFlatInt32Array(datetimes) {
const arr = new Int32Array(datetimes.length * 7);
for (let i = 0; i < datetimes.length; i++) {
const dt = datetimes[i];
const offset = i * 7;
arr[offset] = dt.year;
arr[offset + 1] = dt.month;
arr[offset + 2] = dt.day;
if (dt.time) {
arr[offset + 3] = dt.time.hour;
arr[offset + 4] = dt.time.minute;
arr[offset + 5] = dt.time.second;
arr[offset + 6] = dt.offset ?? -1;
}
else {
arr[offset + 3] = -1;
arr[offset + 4] = -1;
arr[offset + 5] = -1;
arr[offset + 6] = -1;
}
}
return arr;
}
toPlain(options) {
let plain;
if (this.time) {
plain = options?.stripUtc
? {
year: this.year,
month: this.month,
day: this.day,
hour: this.time.hour,
minute: this.time.minute,
second: this.time.second,
}
: {
year: this.year,
month: this.month,
day: this.day,
hour: this.time.hour,
minute: this.time.minute,
second: this.time.second,
utc: this.time.utc,
};
}
else {
plain = {
year: this.year,
month: this.month,
day: this.day,
};
}
return plain;
}
/**
* Converts the DateTime into an RFC 5545 formatted string.
*
* Format depends on the DateTime type:
* - `YYYYMMDD` for date only
* - `YYYYMMDDTHHMMSSZ` for UTC date-time
* - `YYYYMMDDTHHMMSS` for local date-time
*
* @returns An RFC 5545 formatted string
*
* @example
* ```typescript
* const date = DateTime.date(2024, 1, 15);
* console.log(date.toString()); // "20240115"
*
* const local = DateTime.local(2024, 1, 15, 14, 30, 0);
* console.log(local.toString()); // "20240115T143000"
*
* const utc = DateTime.utc(2024, 1, 15, 14, 30, 0);
* console.log(utc.toString()); // "20240115T143000Z"
* ```
*/
toString() {
let str = this.year.toString().padStart(4, '0') +
this.month.toString().padStart(2, '0') +
this.day.toString().padStart(2, '0');
if (this.time) {
str +=
'T' +
this.time.hour.toString().padStart(2, '0') +
this.time.minute.toString().padStart(2, '0') +
this.time.second.toString().padStart(2, '0') +
(this.time.utc ? 'Z' : '');
}
return str;
}
/**
* Converts the DateTime to a Unix timestamp in milliseconds.
*
* This method requires timezone offset information to be available. The offset
* is automatically set for:
* - DateTime instances with `utc: true`
* - DateTime instances generated by RRule methods (`all`, `between`, or iteration)
*
* @returns The Unix timestamp in milliseconds since January 1, 1970 00:00:00 UTC
* @throws {Error} If timezone offset information is not available
*
* @example
* ```typescript
* const utc = DateTime.utc(2024, 1, 15, 14, 30, 0);
* const timestamp = utc.toTimestamp();
* console.log(timestamp); // 1705328400000
*
* // DateTime from RRule methods have offset information
* const rrule = new RRule({ freq: Frequency.Daily, dtstart: DateTime.local(2024, 1, 1, 10, 0, 0) });
* const occurrences = rrule.all();
* const timestamp2 = occurrences[0].toTimestamp();
* ```
*/
toTimestamp() {
if (typeof this.offset === 'undefined') {
throw new Error('There is no information about time zone offset');
}
return this.toMilliseconds();
}
/**
* Converts the DateTime to a JavaScript Date object.
*
* This method requires timezone offset information to be available. The offset
* is automatically set for:
* - DateTime instances with `utc: true`
* - DateTime instances generated by RRule methods (`all`, `between`, or iteration)
*
* @returns A JavaScript Date object representing the same moment in time
* @throws {Error} If timezone offset information is not available
*
* @example
* ```typescript
* const utc = DateTime.utc(2024, 1, 15, 14, 30, 0);
* const date = utc.toDate();
* console.log(date.toISOString()); // "2024-01-15T14:30:00.000Z"
*
* // DateTime from RRule methods have offset information
* const rrule = new RRule({ freq: Frequency.Daily, dtstart: DateTime.utc(2024, 1, 1, 10, 0, 0) });
* const occurrences = rrule.all();
* const date2 = occurrences[0].toDate();
* ```
*/
toDate() {
if (typeof this.offset !== 'number') {
throw new Error('There is no information about time zone offset');
}
return new Date(this.toMilliseconds());
}
/** @internal */
toInt32Array() {
return new Int32Array([
this.year,
this.month,
this.day,
this.time ? this.time.hour : -1,
this.time ? this.time.minute : -1,
this.time ? this.time.second : -1,
this.offset ?? -1,
]);
}
toMilliseconds() {
let time = Date.UTC(this.year, this.month - 1, this.day, this.time?.hour ?? 0, this.time?.minute ?? 0, this.time?.second ?? 0);
time -= (this.offset ?? 0) * 1000;
return time;
}
}
exports.DateTime = DateTime;