/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * Portions Copyright (C) Philipp Kewisch */ import Time from "./time.js"; import Duration from "./duration.js"; // needed for typescript type resolution // eslint-disable-next-line no-unused-vars import Property from "./property.js"; /** * This lets typescript resolve our custom types in the * generated d.ts files (jsdoc typedefs are converted to typescript types). * Ignore prevents the typedefs from being documented more than once. * @ignore * @typedef {import("./types.js").jCalComponent} jCalComponent * Imports the 'occurrenceDetails' type from the "types.js" module */ /** * This class represents the "period" value type, with various calculation and manipulation methods. * * @memberof ICAL */ class Period { /** * Creates a new {@link ICAL.Period} instance from the passed string. * * @param {String} str The string to parse * @param {Property} prop The property this period will be on * @return {Period} The created period instance */ static fromString(str, prop) { let parts = str.split('/'); if (parts.length !== 2) { throw new Error( 'Invalid string value: "' + str + '" must contain a "/" char.' ); } let options = { start: Time.fromDateTimeString(parts[0], prop) }; let end = parts[1]; if (Duration.isValueString(end)) { options.duration = Duration.fromString(end); } else { options.end = Time.fromDateTimeString(end, prop); } return new Period(options); } /** * Creates a new {@link ICAL.Period} instance from the given data object. * The passed data object cannot contain both and end date and a duration. * * @param {Object} aData An object with members of the period * @param {Time=} aData.start The start of the period * @param {Time=} aData.end The end of the period * @param {Duration=} aData.duration The duration of the period * @return {Period} The period instance */ static fromData(aData) { return new Period(aData); } /** * Returns a new period instance from the given jCal data array. The first * member is always the start date string, the second member is either a * duration or end date string. * * @param {jCalComponent} aData The jCal data array * @param {Property} aProp The property this jCal data is on * @param {Boolean} aLenient If true, data value can be both date and date-time * @return {Period} The period instance */ static fromJSON(aData, aProp, aLenient) { function fromDateOrDateTimeString(aValue, dateProp) { if (aLenient) { return Time.fromString(aValue, dateProp); } else { return Time.fromDateTimeString(aValue, dateProp); } } if (Duration.isValueString(aData[1])) { return Period.fromData({ start: fromDateOrDateTimeString(aData[0], aProp), duration: Duration.fromString(aData[1]) }); } else { return Period.fromData({ start: fromDateOrDateTimeString(aData[0], aProp), end: fromDateOrDateTimeString(aData[1], aProp) }); } } /** * Creates a new ICAL.Period instance. The passed data object cannot contain both and end date and * a duration. * * @param {Object} aData An object with members of the period * @param {Time=} aData.start The start of the period * @param {Time=} aData.end The end of the period * @param {Duration=} aData.duration The duration of the period */ constructor(aData) { this.wrappedJSObject = this; if (aData && 'start' in aData) { if (aData.start && !(aData.start instanceof Time)) { throw new TypeError('.start must be an instance of ICAL.Time'); } this.start = aData.start; } if (aData && aData.end && aData.duration) { throw new Error('cannot accept both end and duration'); } if (aData && 'end' in aData) { if (aData.end && !(aData.end instanceof Time)) { throw new TypeError('.end must be an instance of ICAL.Time'); } this.end = aData.end; } if (aData && 'duration' in aData) { if (aData.duration && !(aData.duration instanceof Duration)) { throw new TypeError('.duration must be an instance of ICAL.Duration'); } this.duration = aData.duration; } } /** * The start of the period * @type {Time} */ start = null; /** * The end of the period * @type {Time} */ end = null; /** * The duration of the period * @type {Duration} */ duration = null; /** * The class identifier. * @constant * @type {String} * @default "icalperiod" */ icalclass = "icalperiod"; /** * The type name, to be used in the jCal object. * @constant * @type {String} * @default "period" */ icaltype = "period"; /** * Returns a clone of the duration object. * * @return {Period} The cloned object */ clone() { return Period.fromData({ start: this.start ? this.start.clone() : null, end: this.end ? this.end.clone() : null, duration: this.duration ? this.duration.clone() : null }); } /** * Calculates the duration of the period, either directly or by subtracting * start from end date. * * @return {Duration} The calculated duration */ getDuration() { if (this.duration) { return this.duration; } else { return this.end.subtractDate(this.start); } } /** * Calculates the end date of the period, either directly or by adding * duration to start date. * * @return {Time} The calculated end date */ getEnd() { if (this.end) { return this.end; } else { let end = this.start.clone(); end.addDuration(this.duration); return end; } } /** * Compare this period with a date or other period. To maintain the logic where a.compare(b) * returns 1 when a > b, this function will return 1 when the period is after the date, 0 when the * date is within the period, and -1 when the period is before the date. When comparing two * periods, as soon as they overlap in any way this will return 0. * * @param {Time|Period} dt The date or other period to compare with */ compare(dt) { if (dt.compare(this.start) < 0) { return 1; } else if (dt.compare(this.getEnd()) > 0) { return -1; } else { return 0; } } /** * The string representation of this period. * @return {String} */ toString() { return this.start + "/" + (this.end || this.duration); } /** * The jCal representation of this period type. * @return {Object} */ toJSON() { return [this.start.toString(), (this.end || this.duration).toString()]; } /** * The iCalendar string representation of this period. * @return {String} */ toICALString() { return this.start.toICALString() + "/" + (this.end || this.duration).toICALString(); } } export default Period;