Change ParkingStructureCountOptions and HistoricalParkingAverageQueryResult to use Date objects

This matches the behavior of `updatedTime` on shuttle objects. When returning API data, dates are converted into milliseconds since Epoch by the DateTime scalar implementation.
This commit is contained in:
2025-07-19 12:12:08 -04:00
parent ed037cf2d2
commit 8ee1f1522e
6 changed files with 30 additions and 26 deletions

View File

@@ -9,7 +9,7 @@ export interface IParkingStructure extends IEntityWithTimestamp, IEntityWithId {
} }
export interface IParkingStructureTimestampRecord { export interface IParkingStructureTimestampRecord {
timestampMs: number; timestampMs: Date;
id: string; id: string;
spotsAvailable: number; spotsAvailable: number;
} }

View File

@@ -16,7 +16,7 @@ export interface NotificationAlertArguments {
export class AppleNotificationSender { export class AppleNotificationSender {
private apnsToken: string | undefined = undefined; private apnsToken: string | undefined = undefined;
private _lastRefreshedTimeMs: number | undefined = undefined; private _lastRefreshedTimeMs: Date | undefined = undefined;
constructor( constructor(
private shouldActuallySendNotifications = true, private shouldActuallySendNotifications = true,
@@ -34,13 +34,13 @@ export class AppleNotificationSender {
} }
} }
get lastRefreshedTimeMs(): number | undefined { get lastRefreshedTimeMs(): Date | undefined {
return this._lastRefreshedTimeMs; return this._lastRefreshedTimeMs;
} }
private lastReloadedTimeForAPNsIsTooRecent() { private lastReloadedTimeForAPNsIsTooRecent() {
const thirtyMinutesMs = 1800000; const thirtyMinutesMs = 1800000;
return this._lastRefreshedTimeMs && Date.now() - this._lastRefreshedTimeMs < thirtyMinutesMs; return this._lastRefreshedTimeMs && Date.now() - this._lastRefreshedTimeMs.getTime() < thirtyMinutesMs;
} }
public reloadAPNsTokenIfTimePassed() { public reloadAPNsTokenIfTimePassed() {
@@ -70,7 +70,7 @@ export class AppleNotificationSender {
algorithm: "ES256", algorithm: "ES256",
header: tokenHeader header: tokenHeader
}); });
this._lastRefreshedTimeMs = nowMs; this._lastRefreshedTimeMs = new Date(nowMs);
} }
/** /**

View File

@@ -80,7 +80,7 @@ export class InMemoryParkingRepository implements ParkingGetterSetterRepository
private createTimestampRecord = (structure: IParkingStructure, timestampMs: number): IParkingStructureTimestampRecord => ({ private createTimestampRecord = (structure: IParkingStructure, timestampMs: number): IParkingStructureTimestampRecord => ({
id: structure.id, id: structure.id,
spotsAvailable: structure.spotsAvailable, spotsAvailable: structure.spotsAvailable,
timestampMs, timestampMs: new Date(timestampMs),
}); });
private ensureHistoricalDataExists = (structureId: string): void => { private ensureHistoricalDataExists = (structureId: string): void => {
@@ -90,7 +90,7 @@ export class InMemoryParkingRepository implements ParkingGetterSetterRepository
}; };
private addRecordToHistoricalData = (structureId: string, record: IParkingStructureTimestampRecord): void => { private addRecordToHistoricalData = (structureId: string, record: IParkingStructureTimestampRecord): void => {
const sortingCallback = (a: IParkingStructureTimestampRecord, b: IParkingStructureTimestampRecord) => a.timestampMs - b.timestampMs; const sortingCallback = (a: IParkingStructureTimestampRecord, b: IParkingStructureTimestampRecord) => a.timestampMs.getTime() - b.timestampMs.getTime();
this.historicalData.get(structureId)?.appendWithSorting(record, sortingCallback); this.historicalData.get(structureId)?.appendWithSorting(record, sortingCallback);
}; };
@@ -112,10 +112,11 @@ export class InMemoryParkingRepository implements ParkingGetterSetterRepository
const results: HistoricalParkingAverageQueryResult[] = []; const results: HistoricalParkingAverageQueryResult[] = [];
const { startUnixEpochMs, endUnixEpochMs, intervalMs } = options; const { startUnixEpochMs, endUnixEpochMs, intervalMs } = options;
let currentIntervalStart = startUnixEpochMs; let currentIntervalStart = startUnixEpochMs.getTime();
const endTime = endUnixEpochMs.getTime();
while (currentIntervalStart < endUnixEpochMs) { while (currentIntervalStart < endTime) {
const currentIntervalEnd = Math.min(currentIntervalStart + intervalMs, endUnixEpochMs); const currentIntervalEnd = Math.min(currentIntervalStart + intervalMs, endTime);
const recordsInInterval = this.getRecordsInTimeRange(records, currentIntervalStart, currentIntervalEnd); const recordsInInterval = this.getRecordsInTimeRange(records, currentIntervalStart, currentIntervalEnd);
if (recordsInInterval.length > 0) { if (recordsInInterval.length > 0) {
@@ -135,7 +136,7 @@ export class InMemoryParkingRepository implements ParkingGetterSetterRepository
endMs: number endMs: number
): IParkingStructureTimestampRecord[] => { ): IParkingStructureTimestampRecord[] => {
return records.filter(record => return records.filter(record =>
record.timestampMs >= startMs && record.timestampMs < endMs record.timestampMs.getTime() >= startMs && record.timestampMs.getTime() < endMs
); );
}; };
@@ -148,8 +149,8 @@ export class InMemoryParkingRepository implements ParkingGetterSetterRepository
const averageSpotsAvailable = totalSpotsAvailable / records.length; const averageSpotsAvailable = totalSpotsAvailable / records.length;
return { return {
fromUnixEpochMs: fromMs, fromUnixEpochMs: new Date(fromMs),
toUnixEpochMs: toMs, toUnixEpochMs: new Date(toMs),
averageSpotsAvailable averageSpotsAvailable
}; };
}; };

View File

@@ -1,14 +1,14 @@
import { IParkingStructure } from "../../entities/ParkingRepositoryEntities"; import { IParkingStructure } from "../../entities/ParkingRepositoryEntities";
export interface ParkingStructureCountOptions { export interface ParkingStructureCountOptions {
startUnixEpochMs: number; startUnixEpochMs: Date;
endUnixEpochMs: number; endUnixEpochMs: Date;
intervalMs: number; intervalMs: number;
} }
export interface HistoricalParkingAverageQueryResult { export interface HistoricalParkingAverageQueryResult {
fromUnixEpochMs: number; fromUnixEpochMs: Date;
toUnixEpochMs: number; toUnixEpochMs: Date;
averageSpotsAvailable: number; averageSpotsAvailable: number;
} }

View File

@@ -163,10 +163,11 @@ export class RedisParkingRepository extends BaseRedisRepository implements Parki
const { startUnixEpochMs, endUnixEpochMs, intervalMs } = options; const { startUnixEpochMs, endUnixEpochMs, intervalMs } = options;
const results: HistoricalParkingAverageQueryResult[] = []; const results: HistoricalParkingAverageQueryResult[] = [];
let currentIntervalStart = startUnixEpochMs; let currentIntervalStart = startUnixEpochMs.getTime();
const endTime = endUnixEpochMs.getTime();
while (currentIntervalStart < endUnixEpochMs) { while (currentIntervalStart < endTime) {
const currentIntervalEnd = Math.min(currentIntervalStart + intervalMs, endUnixEpochMs); const currentIntervalEnd = Math.min(currentIntervalStart + intervalMs, endTime);
try { try {
const aggregationResult = await this.redisClient.sendCommand([ const aggregationResult = await this.redisClient.sendCommand([
@@ -182,8 +183,8 @@ export class RedisParkingRepository extends BaseRedisRepository implements Parki
if (aggregationResult && aggregationResult.length > 0) { if (aggregationResult && aggregationResult.length > 0) {
const [, averageValue] = aggregationResult[0]; const [, averageValue] = aggregationResult[0];
results.push({ results.push({
fromUnixEpochMs: currentIntervalStart, fromUnixEpochMs: new Date(currentIntervalStart),
toUnixEpochMs: currentIntervalEnd, toUnixEpochMs: new Date(currentIntervalEnd),
averageSpotsAvailable: parseFloat(averageValue) averageSpotsAvailable: parseFloat(averageValue)
}); });
} }

View File

@@ -152,8 +152,8 @@ describe.each(repositoryImplementations)('$name', (holder) => {
describe("getHistoricalAveragesOfParkingStructureCounts", () => { describe("getHistoricalAveragesOfParkingStructureCounts", () => {
it("should return empty array for non-existent structure or no data", async () => { it("should return empty array for non-existent structure or no data", async () => {
const options: ParkingStructureCountOptions = { const options: ParkingStructureCountOptions = {
startUnixEpochMs: 1000, startUnixEpochMs: new Date(1000),
endUnixEpochMs: 2000, endUnixEpochMs: new Date(2000),
intervalMs: 500 intervalMs: 500
}; };
@@ -183,8 +183,8 @@ describe.each(repositoryImplementations)('$name', (holder) => {
const now = Date.now(); const now = Date.now();
const options: ParkingStructureCountOptions = { const options: ParkingStructureCountOptions = {
startUnixEpochMs: now - 10000, // Look back 10 seconds startUnixEpochMs: new Date(now - 10000), // Look back 10 seconds
endUnixEpochMs: now + 10000, // Look forward 10 seconds endUnixEpochMs: new Date(now + 10000), // Look forward 10 seconds
intervalMs: 20000 // Single large interval intervalMs: 20000 // Single large interval
}; };
@@ -195,6 +195,8 @@ describe.each(repositoryImplementations)('$name', (holder) => {
if (result.length > 0) { if (result.length > 0) {
expect(result[0]).toHaveProperty('fromUnixEpochMs'); expect(result[0]).toHaveProperty('fromUnixEpochMs');
expect(result[0]).toHaveProperty('toUnixEpochMs'); expect(result[0]).toHaveProperty('toUnixEpochMs');
expect(result[0].fromUnixEpochMs).toBeInstanceOf(Date);
expect(result[0].toUnixEpochMs).toBeInstanceOf(Date);
expect(result[0]).toHaveProperty('averageSpotsAvailable'); expect(result[0]).toHaveProperty('averageSpotsAvailable');
expect(result[0].averageSpotsAvailable).toBeCloseTo(52.5); expect(result[0].averageSpotsAvailable).toBeCloseTo(52.5);
} }