ifc-expressions
Version:
Parsing and evaluation of IFC expressions
200 lines (199 loc) • 9.04 kB
JavaScript
import { Type } from "../type/Types.js";
import { isNullish } from "../util/IfcExpressionUtils.js";
import { DateFormatException } from "../error/value/DateFormatException.js";
import Decimal from "decimal.js";
import { IfcDateValue } from "./IfcDateValue.js";
import { IfcTimeValue } from "./IfcTimeValue.js";
import { IfcTimeStampValue } from "./IfcTimeStampValue.js";
export class IfcDateTimeValue {
constructor(value) {
if (typeof value === "string") {
var parsed = IfcDateTimeValue.parseIfcDate(value);
this.utcDate = parsed.utcDate;
this.originalTimeZoneHours = parsed.originalTimeZoneHours;
this.originalTimeZoneMinutes = parsed.originalTimeZoneMinutes;
this.secondFraction = parsed.secondFraction;
this.isLocal = parsed.isLocal;
}
else if (typeof value === "number" || value instanceof Decimal) {
let timeStampSeconds = value;
if (value instanceof Decimal) {
timeStampSeconds = value.round().toNumber();
}
this.utcDate = new Date(timeStampSeconds);
this.originalTimeZoneHours = 0;
this.originalTimeZoneMinutes = 0;
this.secondFraction = new Decimal(0);
this.isLocal = false;
}
else if (value instanceof IfcDateTimeValue) {
this.utcDate = value.utcDate;
this.originalTimeZoneMinutes = value.originalTimeZoneMinutes;
this.originalTimeZoneHours = value.originalTimeZoneHours;
this.secondFraction = value.secondFraction;
this.isLocal = value.isLocal;
}
else if (isParsedIfcDateTimeValue(value)) {
this.utcDate = value.utcDate;
this.secondFraction = value.secondFraction;
this.originalTimeZoneHours = value.originalTimeZoneHours;
this.originalTimeZoneMinutes = value.originalTimeZoneMinutes;
this.isLocal = value.isLocal;
}
this.stringRepresentation = this.toCanonicalString();
}
static parseIfcDate(value) {
Decimal.set({ defaults: true });
let match = value.match(this.regex);
if (isNullish(match)) {
throw new DateFormatException("Not an IfcDateTime: " + value);
}
let [wholeString, year, month, day, hours, minutes, seconds, fractionSeconds, timeZoneWhole, timeZoneSign, timeZoneHours, timeZoneMinutes,] = match;
var utcDate = new Date(0);
utcDate.setUTCFullYear(Number.parseInt(year));
utcDate.setUTCMonth(Number.parseInt(month) - 1);
utcDate.setUTCDate(Number.parseInt(day));
utcDate.setUTCHours(Number.parseInt(hours));
utcDate.setUTCMinutes(Number.parseInt(minutes));
utcDate.setUTCSeconds(Number.parseInt(seconds));
const secondFraction = isNullish(fractionSeconds)
? new Decimal("0")
: new Decimal("0" + fractionSeconds); //arbitrary precision fractions (to the precision of Decimal.precision)
const originalZoneSign = isNullish(timeZoneSign)
? 0
: timeZoneSign === "+"
? 1
: -1;
const originalTimeZoneHours = originalZoneSign *
(isNullish(timeZoneHours) ? 0 : Number.parseInt(timeZoneHours));
const originalTimeZoneMinutes = originalZoneSign *
(isNullish(timeZoneMinutes) ? 0 : Number.parseInt(timeZoneMinutes));
const isLocal = isNullish(timeZoneWhole);
utcDate.setUTCHours(utcDate.getUTCHours() - originalTimeZoneHours);
utcDate.setUTCMinutes(utcDate.getUTCMinutes() - originalTimeZoneMinutes);
return {
utcDate,
secondFraction,
originalTimeZoneHours,
originalTimeZoneMinutes,
isLocal,
};
}
static of(value) {
return new IfcDateTimeValue(value);
}
getTimeAsString() {
const fraction = isNullish(this.secondFraction)
? ""
: this.secondFraction.isZero()
? ""
: this.secondFraction.toString().substring(1);
return `${this.pad00(this.utcDate.getUTCHours() + this.originalTimeZoneHours)}:${this.pad00(this.utcDate.getUTCMinutes() + this.originalTimeZoneMinutes)}:${this.pad00(this.utcDate.getUTCSeconds())}${fraction}`;
}
pad00(num) {
return ("" + num).padStart(2, "0");
}
getDateAsString() {
return `${this.utcDate.getUTCFullYear()}-${this.pad00(this.utcDate.getUTCMonth() + 1)}-${this.pad00(this.utcDate.getUTCDate())}`;
}
getTimeZoneAsString() {
if (this.isLocal) {
return "";
}
if (this.originalTimeZoneMinutes === 0 &&
this.originalTimeZoneHours === 0) {
return "Z";
}
const sign = this.originalTimeZoneHours >= 0 && this.originalTimeZoneMinutes >= 0
? "+"
: "-";
return `${sign}${this.pad00(Math.abs(this.originalTimeZoneHours))}:${this.pad00(Math.abs(this.originalTimeZoneMinutes))}`;
}
getTimeStampSeconds() {
const offset = this.isLocal ? new Date().getTimezoneOffset() * 60 : 0;
return new Decimal(this.utcDate.getTime()).divToInt(1000).minus(offset);
}
getTime() {
const offset = this.isLocal ? new Date().getTimezoneOffset() * 60 : 0;
return new Decimal(this.utcDate.getTime())
.minus(offset)
.plus(this.secondFraction);
}
getValue() {
return this;
}
getType() {
return Type.IFC_DATE_TIME;
}
static isIfcDateTimeValueType(arg) {
return (arg instanceof IfcDateTimeValue ||
(!isNullish(arg.stringRepresentation) &&
!isNullish(arg.stringRepresentation.match(IfcDateTimeValue.regex))));
}
static isValidStringRepresentation(str) {
return this.regex.test(str);
}
equals(other) {
return (IfcDateTimeValue.isIfcDateTimeValueType(other) &&
this.getTime().eq(other.getTime()));
}
toCanonicalString() {
return (this.getDateAsString() +
"T" +
this.getTimeAsString() +
this.getTimeZoneAsString());
}
toString() {
return this.stringRepresentation;
}
compareTo(other) {
return Decimal.sign(this.getTime().minus(other.getTime()));
}
static ofTimeStampSeconds(timeStampSeconds) {
var timeStampMillis = timeStampSeconds;
if (timeStampSeconds instanceof Decimal) {
timeStampMillis = timeStampSeconds.round().toNumber();
}
timeStampMillis = timeStampMillis * 1000;
return new IfcDateTimeValue(timeStampMillis);
}
toIfcDateValue() {
return IfcDateValue.of(this.getDateAsString() + this.getTimeZoneAsString());
}
toIfcTimeValue() {
return IfcTimeValue.of(this.getTimeAsString() + this.getTimeZoneAsString());
}
toIfcTimeStampValue() {
return IfcTimeStampValue.of(this.getTimeStampSeconds());
}
addDuration(duration) {
const resultDate = new Date(this.utcDate.getTime());
const accumulatedFractions = this.secondFraction.add(duration.getFractionAsSeconds());
const newSecondFraction = accumulatedFractions.minus(accumulatedFractions.divToInt(1));
resultDate.setUTCSeconds(resultDate.getUTCSeconds() + accumulatedFractions.divToInt(1).toNumber());
resultDate.setUTCSeconds(resultDate.getUTCSeconds() + duration.getSeconds().divToInt(1).toNumber());
resultDate.setUTCMinutes(resultDate.getUTCMinutes() + duration.getMinutes().divToInt(1).toNumber());
resultDate.setUTCHours(resultDate.getUTCHours() + duration.getHours().divToInt(1).toNumber());
resultDate.setUTCDate(resultDate.getUTCDate() + duration.getDays().divToInt(1).toNumber());
resultDate.setUTCDate(resultDate.getUTCDate() +
duration.getWeeks().divToInt(1).times(7).toNumber());
resultDate.setUTCMonth(resultDate.getUTCMonth() + duration.getMonths().divToInt(1).toNumber());
resultDate.setUTCFullYear(resultDate.getUTCFullYear() + duration.getYears().divToInt(1).toNumber());
return new IfcDateTimeValue({
utcDate: resultDate,
secondFraction: newSecondFraction,
originalTimeZoneHours: this.originalTimeZoneHours,
originalTimeZoneMinutes: this.originalTimeZoneMinutes,
isLocal: this.isLocal,
});
}
}
IfcDateTimeValue.regex = /^([+\-]?(?:[1-9]\d*)?\d{4}(?<!0000))-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|(?<=23:59:)60)(\.\d+)?(Z|([+\-])(0[0-9]|1[0-2])(?::?([0-5][0-9]))?)?$/;
export function isParsedIfcDateTimeValue(arg) {
return (!isNullish(arg.utcDate) &&
!isNullish(arg.secondFraction) &&
!isNullish(arg.originalTimeZoneHours) &&
!isNullish(arg.originalTimeZoneMinutes) &&
!isNullish(arg.isLocal));
}
//# sourceMappingURL=IfcDateTimeValue.js.map