Cleaned up the frame.ts by splitting payload parsing to subpackages
This commit is contained in:
189
src/timestamp.ts
Normal file
189
src/timestamp.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user