import * as pvutils from "pvutils"; import { typeStore } from "./TypeStore"; import { IUTCTime, UTCTimeParams, UTCTimeJson, UTCTime, DateStringEncoding } from "./UTCTime"; export interface IGeneralizedTime extends IUTCTime { millisecond: number; } export type GeneralizedTimeParams = UTCTimeParams; export interface GeneralizedTimeJson extends UTCTimeJson { millisecond: number; } export class GeneralizedTime extends UTCTime { static { typeStore.GeneralizedTime = this; } public static override NAME = "GeneralizedTime"; public millisecond: number; constructor(parameters: GeneralizedTimeParams = {}) { super(parameters); this.millisecond ??= 0; this.idBlock.tagClass = 1; // UNIVERSAL this.idBlock.tagNumber = 24; // GeneralizedTime } public override fromDate(inputDate: Date): void { super.fromDate(inputDate); this.millisecond = inputDate.getUTCMilliseconds(); } public override toDate(): Date { return (new Date(Date.UTC(this.year, this.month - 1, this.day, this.hour, this.minute, this.second, this.millisecond))); } public override fromString(inputString: string): void { //#region Initial variables let isUTC = false; let timeString = ""; let dateTimeString = ""; let fractionPart = 0; let parser; let hourDifference = 0; let minuteDifference = 0; //#endregion //#region Convert as UTC time if (inputString[inputString.length - 1] === "Z") { timeString = inputString.substring(0, inputString.length - 1); isUTC = true; } //#endregion //#region Convert as local time else { const number = new Number(inputString[inputString.length - 1]); if (isNaN(number.valueOf())) throw new Error("Wrong input string for conversion"); timeString = inputString; } //#endregion //#region Check that we do not have a "+" and "-" symbols inside UTC time if (isUTC) { if (timeString.indexOf("+") !== -1) throw new Error("Wrong input string for conversion"); if (timeString.indexOf("-") !== -1) throw new Error("Wrong input string for conversion"); } //#endregion //#region Get "UTC time difference" in case of local time else { let multiplier = 1; let differencePosition = timeString.indexOf("+"); let differenceString = ""; if (differencePosition === -1) { differencePosition = timeString.indexOf("-"); multiplier = -1; } if (differencePosition !== -1) { differenceString = timeString.substring(differencePosition + 1); timeString = timeString.substring(0, differencePosition); if ((differenceString.length !== 2) && (differenceString.length !== 4)) throw new Error("Wrong input string for conversion"); let number = parseInt(differenceString.substring(0, 2), 10); if (isNaN(number.valueOf())) throw new Error("Wrong input string for conversion"); hourDifference = multiplier * number; if (differenceString.length === 4) { number = parseInt(differenceString.substring(2, 4), 10); if (isNaN(number.valueOf())) throw new Error("Wrong input string for conversion"); minuteDifference = multiplier * number; } } } //#endregion //#region Get position of fraction point let fractionPointPosition = timeString.indexOf("."); // Check for "full stop" symbol if (fractionPointPosition === -1) fractionPointPosition = timeString.indexOf(","); // Check for "comma" symbol //#endregion //#region Get fraction part if (fractionPointPosition !== -1) { const fractionPartCheck = new Number(`0${timeString.substring(fractionPointPosition)}`); if (isNaN(fractionPartCheck.valueOf())) throw new Error("Wrong input string for conversion"); fractionPart = fractionPartCheck.valueOf(); dateTimeString = timeString.substring(0, fractionPointPosition); } else dateTimeString = timeString; //#endregion //#region Parse internal date switch (true) { case (dateTimeString.length === 8): // "YYYYMMDD" parser = /(\d{4})(\d{2})(\d{2})/ig; if (fractionPointPosition !== -1) throw new Error("Wrong input string for conversion"); // Here we should not have a "fraction point" break; case (dateTimeString.length === 10): // "YYYYMMDDHH" parser = /(\d{4})(\d{2})(\d{2})(\d{2})/ig; if (fractionPointPosition !== -1) { let fractionResult = 60 * fractionPart; this.minute = Math.floor(fractionResult); fractionResult = 60 * (fractionResult - this.minute); this.second = Math.floor(fractionResult); fractionResult = 1000 * (fractionResult - this.second); this.millisecond = Math.floor(fractionResult); } break; case (dateTimeString.length === 12): // "YYYYMMDDHHMM" parser = /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})/ig; if (fractionPointPosition !== -1) { let fractionResult = 60 * fractionPart; this.second = Math.floor(fractionResult); fractionResult = 1000 * (fractionResult - this.second); this.millisecond = Math.floor(fractionResult); } break; case (dateTimeString.length === 14): // "YYYYMMDDHHMMSS" parser = /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/ig; if (fractionPointPosition !== -1) { const fractionResult = 1000 * fractionPart; this.millisecond = Math.floor(fractionResult); } break; default: throw new Error("Wrong input string for conversion"); } //#endregion //#region Put parsed values at right places const parserArray = parser.exec(dateTimeString); if (parserArray === null) throw new Error("Wrong input string for conversion"); for (let j = 1; j < parserArray.length; j++) { switch (j) { case 1: this.year = parseInt(parserArray[j], 10); break; case 2: this.month = parseInt(parserArray[j], 10); break; case 3: this.day = parseInt(parserArray[j], 10); break; case 4: this.hour = parseInt(parserArray[j], 10) + hourDifference; break; case 5: this.minute = parseInt(parserArray[j], 10) + minuteDifference; break; case 6: this.second = parseInt(parserArray[j], 10); break; default: throw new Error("Wrong input string for conversion"); } } //#endregion //#region Get final date if (isUTC === false) { const tempDate = new Date(this.year, this.month, this.day, this.hour, this.minute, this.second, this.millisecond); this.year = tempDate.getUTCFullYear(); this.month = tempDate.getUTCMonth(); this.day = tempDate.getUTCDay(); this.hour = tempDate.getUTCHours(); this.minute = tempDate.getUTCMinutes(); this.second = tempDate.getUTCSeconds(); this.millisecond = tempDate.getUTCMilliseconds(); } //#endregion } public override toString(encoding: DateStringEncoding = "iso"): string { if (encoding === "iso") { const outputArray = []; outputArray.push(pvutils.padNumber(this.year, 4)); outputArray.push(pvutils.padNumber(this.month, 2)); outputArray.push(pvutils.padNumber(this.day, 2)); outputArray.push(pvutils.padNumber(this.hour, 2)); outputArray.push(pvutils.padNumber(this.minute, 2)); outputArray.push(pvutils.padNumber(this.second, 2)); if (this.millisecond !== 0) { outputArray.push("."); outputArray.push(pvutils.padNumber(this.millisecond, 3)); } outputArray.push("Z"); return outputArray.join(""); } return super.toString(encoding); } public override toJSON(): GeneralizedTimeJson { return { ...super.toJSON(), millisecond: this.millisecond, }; } }