190 lines
5.9 KiB
TypeScript
190 lines
5.9 KiB
TypeScript
import { FieldType, Segment } from "@hamradio/packet";
|
|
|
|
import { ITimestamp } from "./frame.types";
|
|
|
|
export class Timestamp implements ITimestamp {
|
|
day?: number;
|
|
month?: number;
|
|
hours: number;
|
|
minutes: number;
|
|
seconds?: number;
|
|
format: "DHM" | "HMS" | "MDHM";
|
|
zulu?: boolean;
|
|
|
|
constructor(
|
|
hours: number,
|
|
minutes: number,
|
|
format: "DHM" | "HMS" | "MDHM",
|
|
options: {
|
|
day?: number;
|
|
month?: number;
|
|
seconds?: number;
|
|
zulu?: boolean;
|
|
} = {}
|
|
) {
|
|
this.hours = hours;
|
|
this.minutes = minutes;
|
|
this.format = format;
|
|
this.day = options.day;
|
|
this.month = options.month;
|
|
this.seconds = options.seconds;
|
|
this.zulu = options.zulu;
|
|
}
|
|
|
|
/**
|
|
* Convert APRS timestamp to JavaScript Date object
|
|
* Note: APRS timestamps don't include year, so we use current year
|
|
* For DHM format, we find the most recent occurrence of that day
|
|
* For HMS format, we use current date
|
|
* For MDHM format, we use the specified month/day in current year
|
|
*/
|
|
toDate(): Date {
|
|
const now = new Date();
|
|
|
|
if (this.format === "DHM") {
|
|
// Day-Hour-Minute format (UTC)
|
|
// Find the most recent occurrence of this day
|
|
const currentYear = this.zulu ? now.getUTCFullYear() : now.getFullYear();
|
|
const currentMonth = this.zulu ? now.getUTCMonth() : now.getMonth();
|
|
|
|
let date: Date;
|
|
if (this.zulu) {
|
|
date = new Date(Date.UTC(currentYear, currentMonth, this.day!, this.hours, this.minutes, 0, 0));
|
|
} else {
|
|
date = new Date(currentYear, currentMonth, this.day!, this.hours, this.minutes, 0, 0);
|
|
}
|
|
|
|
// If the date is in the future, it's from last month
|
|
if (date > now) {
|
|
if (this.zulu) {
|
|
date = new Date(Date.UTC(currentYear, currentMonth - 1, this.day!, this.hours, this.minutes, 0, 0));
|
|
} else {
|
|
date = new Date(currentYear, currentMonth - 1, this.day!, this.hours, this.minutes, 0, 0);
|
|
}
|
|
}
|
|
|
|
return date;
|
|
} else if (this.format === "HMS") {
|
|
// Hour-Minute-Second format (UTC)
|
|
// Use current date
|
|
if (this.zulu) {
|
|
const date = new Date();
|
|
date.setUTCHours(this.hours, this.minutes, this.seconds || 0, 0);
|
|
|
|
// If time is in the future, it's from yesterday
|
|
if (date > now) {
|
|
date.setUTCDate(date.getUTCDate() - 1);
|
|
}
|
|
|
|
return date;
|
|
} else {
|
|
const date = new Date();
|
|
date.setHours(this.hours, this.minutes, this.seconds || 0, 0);
|
|
|
|
if (date > now) {
|
|
date.setDate(date.getDate() - 1);
|
|
}
|
|
|
|
return date;
|
|
}
|
|
} else {
|
|
// MDHM format: Month-Day-Hour-Minute (local time)
|
|
const currentYear = now.getFullYear();
|
|
let date = new Date(currentYear, (this.month || 1) - 1, this.day!, this.hours, this.minutes, 0, 0);
|
|
|
|
// If date is in the future, it's from last year
|
|
if (date > now) {
|
|
date = new Date(currentYear - 1, (this.month || 1) - 1, this.day!, this.hours, this.minutes, 0, 0);
|
|
}
|
|
|
|
return date;
|
|
}
|
|
}
|
|
|
|
static fromString(
|
|
str: string,
|
|
withStructure: boolean = false
|
|
): {
|
|
timestamp: Timestamp | undefined;
|
|
segment?: Segment;
|
|
} {
|
|
if (str.length !== 7) return { timestamp: undefined };
|
|
|
|
const timeType = str.charAt(6);
|
|
|
|
if (timeType === "z") {
|
|
// DHM format: Day-Hour-Minute (UTC)
|
|
const timestamp = new Timestamp(parseInt(str.substring(2, 4), 10), parseInt(str.substring(4, 6), 10), "DHM", {
|
|
day: parseInt(str.substring(0, 2), 10),
|
|
zulu: true
|
|
});
|
|
|
|
const segment = withStructure
|
|
? {
|
|
name: "timestamp",
|
|
data: new TextEncoder().encode(str).buffer,
|
|
isString: true,
|
|
fields: [
|
|
{ type: FieldType.STRING, name: "day (DD)", length: 2 },
|
|
{ type: FieldType.STRING, name: "hour (HH)", length: 2 },
|
|
{ type: FieldType.STRING, name: "minute (MM)", length: 2 },
|
|
{ type: FieldType.CHAR, name: "timezone indicator", length: 1 }
|
|
]
|
|
}
|
|
: undefined;
|
|
|
|
return { timestamp, segment };
|
|
} else if (timeType === "h") {
|
|
// HMS format: Hour-Minute-Second (UTC)
|
|
const timestamp = new Timestamp(parseInt(str.substring(0, 2), 10), parseInt(str.substring(2, 4), 10), "HMS", {
|
|
seconds: parseInt(str.substring(4, 6), 10),
|
|
zulu: true
|
|
});
|
|
|
|
const segment = withStructure
|
|
? {
|
|
name: "timestamp",
|
|
data: new TextEncoder().encode(str).buffer,
|
|
isString: true,
|
|
fields: [
|
|
{ type: FieldType.STRING, name: "hour (HH)", length: 2 },
|
|
{ type: FieldType.STRING, name: "minute (MM)", length: 2 },
|
|
{ type: FieldType.STRING, name: "second (SS)", length: 2 },
|
|
{ type: FieldType.CHAR, name: "timezone indicator", length: 1 }
|
|
]
|
|
}
|
|
: undefined;
|
|
|
|
return { timestamp, segment };
|
|
} else if (timeType === "/") {
|
|
// MDHM format: Month-Day-Hour-Minute (local)
|
|
const timestamp = new Timestamp(parseInt(str.substring(4, 6), 10), parseInt(str.substring(6, 8), 10), "MDHM", {
|
|
month: parseInt(str.substring(0, 2), 10),
|
|
day: parseInt(str.substring(2, 4), 10),
|
|
zulu: false
|
|
});
|
|
|
|
const segment = withStructure
|
|
? {
|
|
name: "timestamp",
|
|
data: new TextEncoder().encode(str).buffer,
|
|
isString: true,
|
|
fields: [
|
|
{ type: FieldType.STRING, name: "month (MM)", length: 2 },
|
|
{ type: FieldType.STRING, name: "day (DD)", length: 2 },
|
|
{ type: FieldType.STRING, name: "hour (HH)", length: 2 },
|
|
{ type: FieldType.STRING, name: "minute (MM)", length: 2 },
|
|
{ type: FieldType.CHAR, name: "timezone indicator", length: 1 }
|
|
]
|
|
}
|
|
: undefined;
|
|
|
|
return { timestamp, segment };
|
|
}
|
|
|
|
return { timestamp: undefined };
|
|
}
|
|
}
|
|
|
|
export default Timestamp;
|