import type { BitPackerSchema, PackingContext, PackingStrategy, } from "@suaveplan/bitpacker"; import { BITPACKER_CODES, BitpackerError } from "@suaveplan/bitpacker"; const DATETIME_TOKENS: readonly string[] = [ "timestamp", "timestamp-ms", "timestamp-s", "timespan", "timespan-ms", "date-only", ]; export class DateTimePackingStrategy implements PackingStrategy { canHandle(schema: BitPackerSchema): boolean { return typeof schema === "string" && DATETIME_TOKENS.includes(schema); } pack( data: unknown, schema: BitPackerSchema, context: PackingContext ): void { const token = schema as string; let ms: number; if (data instanceof Date) { ms = data.getTime(); } else if (typeof data === "number") { ms = data; } else { throw new BitpackerError( BITPACKER_CODES.INVALID_SCHEMA, `Expected Date or number for datetime schema "${token}", got ${typeof data}`, { context: { token, data } } ); } switch (token) { case "timestamp": case "timestamp-ms": case "date-only": context.writer.writeInt(ms, 53); break; case "timestamp-s": context.writer.writeInt(Math.floor(ms / 1000), 32); break; case "timespan": context.writer.writeInt(ms, 53); break; case "timespan-ms": context.writer.writeInt(ms, 53); break; } } unpack(schema: BitPackerSchema, context: PackingContext): unknown { const token = schema as string; switch (token) { case "timestamp": case "timestamp-ms": case "date-only": return context.reader.readInt(53); case "timestamp-s": return context.reader.readInt(32) * 1000; case "timespan": case "timespan-ms": return context.reader.readInt(53); default: return 0; } } }