import type { Clock } from "@suaveplan/types"; import { MS_PER_DAY } from "../constants/constants.js"; import { TimeEncodingType } from "../types/license.js"; const defaultClock: Clock = () => Date.now(); /** * Convert a `Date` (or now) to days since the Unix epoch. The 18-bit * `timeValue` field can hold ~718 years of day numbers. */ export function getDayNumber(date?: Date, clock: Clock = defaultClock): number { const ms = date !== undefined ? date.getTime() : clock(); return Math.floor(ms / MS_PER_DAY); } /** Inverse of {@link getDayNumber}. */ export function dayNumberToDate(dayNumber: number): Date { return new Date(dayNumber * MS_PER_DAY); } /** * Determine whether a license is expired given its time-encoding metadata. * The optional `clock` defaults to `Date.now()` and is read only when * `referenceDate` is omitted. */ export function checkExpiration( timeEncodingType: TimeEncodingType, timeValue: number, referenceDate?: Date, creationDate?: Date, clock: Clock = defaultClock ): { isExpired: boolean; expirationDate?: Date } { const ref = referenceDate ?? new Date(clock()); switch (timeEncodingType) { case TimeEncodingType.NONE: return { isExpired: false }; case TimeEncodingType.ABSOLUTE_DAYS: { const expirationDate = dayNumberToDate(timeValue); return { isExpired: ref > expirationDate, expirationDate }; } case TimeEncodingType.RELATIVE_DAYS: { const creation = creationDate ?? ref; const expirationDate = new Date( creation.getTime() + timeValue * MS_PER_DAY ); return { isExpired: ref > expirationDate, expirationDate }; } case TimeEncodingType.ABSOLUTE_TIMESTAMP: { const expirationDate = new Date(timeValue * 1000); return { isExpired: ref > expirationDate, expirationDate }; } default: return { isExpired: false }; } }