Auto backup before evolver run - 2026年 03月 06日 星期五 19:00:05 CST
This commit is contained in:
+374
@@ -0,0 +1,374 @@
|
||||
/* 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 { isStrictlyNaN, trunc } from "./helpers.js";
|
||||
|
||||
const DURATION_LETTERS = /([PDWHMTS]{1,1})/;
|
||||
const DATA_PROPS_TO_COPY = ["weeks", "days", "hours", "minutes", "seconds", "isNegative"];
|
||||
|
||||
/**
|
||||
* This class represents the "duration" value type, with various calculation
|
||||
* and manipulation methods.
|
||||
*
|
||||
* @memberof ICAL
|
||||
*/
|
||||
class Duration {
|
||||
/**
|
||||
* Returns a new ICAL.Duration instance from the passed seconds value.
|
||||
*
|
||||
* @param {Number} aSeconds The seconds to create the instance from
|
||||
* @return {Duration} The newly created duration instance
|
||||
*/
|
||||
static fromSeconds(aSeconds) {
|
||||
return (new Duration()).fromSeconds(aSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given string is an iCalendar duration value.
|
||||
*
|
||||
* @param {String} value The raw ical value
|
||||
* @return {Boolean} True, if the given value is of the
|
||||
* duration ical type
|
||||
*/
|
||||
static isValueString(string) {
|
||||
return (string[0] === 'P' || string[1] === 'P');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ICAL.Duration} instance from the passed string.
|
||||
*
|
||||
* @param {String} aStr The string to parse
|
||||
* @return {Duration} The created duration instance
|
||||
*/
|
||||
static fromString(aStr) {
|
||||
let pos = 0;
|
||||
let dict = Object.create(null);
|
||||
let chunks = 0;
|
||||
|
||||
while ((pos = aStr.search(DURATION_LETTERS)) !== -1) {
|
||||
let type = aStr[pos];
|
||||
let numeric = aStr.slice(0, Math.max(0, pos));
|
||||
aStr = aStr.slice(pos + 1);
|
||||
|
||||
chunks += parseDurationChunk(type, numeric, dict);
|
||||
}
|
||||
|
||||
if (chunks < 2) {
|
||||
// There must be at least a chunk with "P" and some unit chunk
|
||||
throw new Error(
|
||||
'invalid duration value: Not enough duration components in "' + aStr + '"'
|
||||
);
|
||||
}
|
||||
|
||||
return new Duration(dict);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ICAL.Duration instance from the given data object.
|
||||
*
|
||||
* @param {Object} aData An object with members of the duration
|
||||
* @param {Number=} aData.weeks Duration in weeks
|
||||
* @param {Number=} aData.days Duration in days
|
||||
* @param {Number=} aData.hours Duration in hours
|
||||
* @param {Number=} aData.minutes Duration in minutes
|
||||
* @param {Number=} aData.seconds Duration in seconds
|
||||
* @param {Boolean=} aData.isNegative If true, the duration is negative
|
||||
* @return {Duration} The createad duration instance
|
||||
*/
|
||||
static fromData(aData) {
|
||||
return new Duration(aData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ICAL.Duration instance.
|
||||
*
|
||||
* @param {Object} data An object with members of the duration
|
||||
* @param {Number=} data.weeks Duration in weeks
|
||||
* @param {Number=} data.days Duration in days
|
||||
* @param {Number=} data.hours Duration in hours
|
||||
* @param {Number=} data.minutes Duration in minutes
|
||||
* @param {Number=} data.seconds Duration in seconds
|
||||
* @param {Boolean=} data.isNegative If true, the duration is negative
|
||||
*/
|
||||
constructor(data) {
|
||||
this.wrappedJSObject = this;
|
||||
this.fromData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The weeks in this duration
|
||||
* @type {Number}
|
||||
* @default 0
|
||||
*/
|
||||
weeks = 0;
|
||||
|
||||
/**
|
||||
* The days in this duration
|
||||
* @type {Number}
|
||||
* @default 0
|
||||
*/
|
||||
days = 0;
|
||||
|
||||
/**
|
||||
* The days in this duration
|
||||
* @type {Number}
|
||||
* @default 0
|
||||
*/
|
||||
hours = 0;
|
||||
|
||||
/**
|
||||
* The minutes in this duration
|
||||
* @type {Number}
|
||||
* @default 0
|
||||
*/
|
||||
minutes = 0;
|
||||
|
||||
/**
|
||||
* The seconds in this duration
|
||||
* @type {Number}
|
||||
* @default 0
|
||||
*/
|
||||
seconds = 0;
|
||||
|
||||
/**
|
||||
* The seconds in this duration
|
||||
* @type {Boolean}
|
||||
* @default false
|
||||
*/
|
||||
isNegative = false;
|
||||
|
||||
/**
|
||||
* The class identifier.
|
||||
* @constant
|
||||
* @type {String}
|
||||
* @default "icalduration"
|
||||
*/
|
||||
icalclass = "icalduration";
|
||||
|
||||
/**
|
||||
* The type name, to be used in the jCal object.
|
||||
* @constant
|
||||
* @type {String}
|
||||
* @default "duration"
|
||||
*/
|
||||
icaltype = "duration";
|
||||
|
||||
/**
|
||||
* Returns a clone of the duration object.
|
||||
*
|
||||
* @return {Duration} The cloned object
|
||||
*/
|
||||
clone() {
|
||||
return Duration.fromData(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* The duration value expressed as a number of seconds.
|
||||
*
|
||||
* @return {Number} The duration value in seconds
|
||||
*/
|
||||
toSeconds() {
|
||||
let seconds = this.seconds + 60 * this.minutes + 3600 * this.hours +
|
||||
86400 * this.days + 7 * 86400 * this.weeks;
|
||||
return (this.isNegative ? -seconds : seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the passed seconds value into this duration object. Afterwards,
|
||||
* members like {@link ICAL.Duration#days days} and {@link ICAL.Duration#weeks weeks} will be set up
|
||||
* accordingly.
|
||||
*
|
||||
* @param {Number} aSeconds The duration value in seconds
|
||||
* @return {Duration} Returns this instance
|
||||
*/
|
||||
fromSeconds(aSeconds) {
|
||||
let secs = Math.abs(aSeconds);
|
||||
|
||||
this.isNegative = (aSeconds < 0);
|
||||
this.days = trunc(secs / 86400);
|
||||
|
||||
// If we have a flat number of weeks, use them.
|
||||
if (this.days % 7 == 0) {
|
||||
this.weeks = this.days / 7;
|
||||
this.days = 0;
|
||||
} else {
|
||||
this.weeks = 0;
|
||||
}
|
||||
|
||||
secs -= (this.days + 7 * this.weeks) * 86400;
|
||||
|
||||
this.hours = trunc(secs / 3600);
|
||||
secs -= this.hours * 3600;
|
||||
|
||||
this.minutes = trunc(secs / 60);
|
||||
secs -= this.minutes * 60;
|
||||
|
||||
this.seconds = secs;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the current instance using members from the passed data object.
|
||||
*
|
||||
* @param {Object} aData An object with members of the duration
|
||||
* @param {Number=} aData.weeks Duration in weeks
|
||||
* @param {Number=} aData.days Duration in days
|
||||
* @param {Number=} aData.hours Duration in hours
|
||||
* @param {Number=} aData.minutes Duration in minutes
|
||||
* @param {Number=} aData.seconds Duration in seconds
|
||||
* @param {Boolean=} aData.isNegative If true, the duration is negative
|
||||
*/
|
||||
fromData(aData) {
|
||||
for (let prop of DATA_PROPS_TO_COPY) {
|
||||
if (aData && prop in aData) {
|
||||
this[prop] = aData[prop];
|
||||
} else {
|
||||
this[prop] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the duration instance to the default values, i.e. PT0S
|
||||
*/
|
||||
reset() {
|
||||
this.isNegative = false;
|
||||
this.weeks = 0;
|
||||
this.days = 0;
|
||||
this.hours = 0;
|
||||
this.minutes = 0;
|
||||
this.seconds = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the duration instance with another one.
|
||||
*
|
||||
* @param {Duration} aOther The instance to compare with
|
||||
* @return {Number} -1, 0 or 1 for less/equal/greater
|
||||
*/
|
||||
compare(aOther) {
|
||||
let thisSeconds = this.toSeconds();
|
||||
let otherSeconds = aOther.toSeconds();
|
||||
return (thisSeconds > otherSeconds) - (thisSeconds < otherSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the duration instance. For example, a duration with a value
|
||||
* of 61 seconds will be normalized to 1 minute and 1 second.
|
||||
*/
|
||||
normalize() {
|
||||
this.fromSeconds(this.toSeconds());
|
||||
}
|
||||
|
||||
/**
|
||||
* The string representation of this duration.
|
||||
* @return {String}
|
||||
*/
|
||||
toString() {
|
||||
if (this.toSeconds() == 0) {
|
||||
return "PT0S";
|
||||
} else {
|
||||
let str = "";
|
||||
if (this.isNegative) str += "-";
|
||||
str += "P";
|
||||
let hasWeeks = false;
|
||||
if (this.weeks) {
|
||||
if (this.days || this.hours || this.minutes || this.seconds) {
|
||||
str += (this.weeks * 7 + this.days) + "D";
|
||||
} else {
|
||||
str += (this.weeks + "W");
|
||||
hasWeeks = true;
|
||||
}
|
||||
} else if (this.days) {
|
||||
str += (this.days + "D");
|
||||
}
|
||||
|
||||
if (!hasWeeks) {
|
||||
if (this.hours || this.minutes || this.seconds) {
|
||||
str += "T";
|
||||
if (this.hours) {
|
||||
str += this.hours + "H";
|
||||
}
|
||||
|
||||
if (this.minutes) {
|
||||
str += this.minutes + "M";
|
||||
}
|
||||
|
||||
if (this.seconds) {
|
||||
str += this.seconds + "S";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The iCalendar string representation of this duration.
|
||||
* @return {String}
|
||||
*/
|
||||
toICALString() {
|
||||
return this.toString();
|
||||
}
|
||||
}
|
||||
export default Duration;
|
||||
|
||||
/**
|
||||
* Internal helper function to handle a chunk of a duration.
|
||||
*
|
||||
* @private
|
||||
* @param {String} letter type of duration chunk
|
||||
* @param {String} number numeric value or -/+
|
||||
* @param {Object} dict target to assign values to
|
||||
*/
|
||||
function parseDurationChunk(letter, number, object) {
|
||||
let type;
|
||||
switch (letter) {
|
||||
case 'P':
|
||||
if (number && number === '-') {
|
||||
object.isNegative = true;
|
||||
} else {
|
||||
object.isNegative = false;
|
||||
}
|
||||
// period
|
||||
break;
|
||||
case 'D':
|
||||
type = 'days';
|
||||
break;
|
||||
case 'W':
|
||||
type = 'weeks';
|
||||
break;
|
||||
case 'H':
|
||||
type = 'hours';
|
||||
break;
|
||||
case 'M':
|
||||
type = 'minutes';
|
||||
break;
|
||||
case 'S':
|
||||
type = 'seconds';
|
||||
break;
|
||||
default:
|
||||
// Not a valid chunk
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (type) {
|
||||
if (!number && number !== 0) {
|
||||
throw new Error(
|
||||
'invalid duration value: Missing number before "' + letter + '"'
|
||||
);
|
||||
}
|
||||
let num = parseInt(number, 10);
|
||||
if (isStrictlyNaN(num)) {
|
||||
throw new Error(
|
||||
'invalid duration value: Invalid number "' + number + '" before "' + letter + '"'
|
||||
);
|
||||
}
|
||||
object[type] = num;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user