Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/models/DriverProfile.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import mongoose, { Schema } from 'mongoose';
import { IDriverProfile, ReputationTier } from '../interfaces/IDriverProfile';
import { nowUTC } from '../utils/dateUtils';

const vehicleDetailsSchema = new Schema(
{
Expand All @@ -16,7 +17,7 @@ const vehicleDetailsSchema = new Schema(
year: {
type: Number,
min: [1980, 'Vehicle year must be 1980 or later'],
max: [new Date().getFullYear() + 1, 'Vehicle year cannot be in the future'],
max: [nowUTC().getUTCFullYear() + 1, 'Vehicle year cannot be in the future'],
},
plateNumber: {
type: String,
Expand Down
3 changes: 2 additions & 1 deletion src/services/escrowService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Escrow, { EscrowLockStatus, IEscrow } from '../models/Escrow';
import { sorobanService } from '../blockchain/soroban.service';
import AppError from '../utils/AppError';
import logger from '../config/logger';
import { nowUTC } from '../utils/dateUtils';

// ─── DTOs ──────────────────────────────────────────────────────────────────────

Expand Down Expand Up @@ -49,7 +50,7 @@ export interface ResolveEscrowInput {
* or expiresAt field in the current Escrow model.
*/
export const scanForExpiredEscrows = async (): Promise<ScanExpiredEscrowsResult> => {
const now = new Date();
const now = nowUTC();

// Note: Commented out until EscrowStatus.EXPIRED and expiresAt field are added to model
// const expiredCandidates = await Escrow.find({
Expand Down
3 changes: 2 additions & 1 deletion src/services/idempotency.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import IdempotencyRecord, { IdempotencyStatus } from '../models/IdempotencyRecor
import env from '../config/env';
import logger from '../config/logger';
import { AppError } from '../utils/AppError';
import { toUTC } from '../utils/dateUtils';

/** Payload stored against an idempotency key once a request completes. */
export interface IdempotencyPayload {
Expand Down Expand Up @@ -80,7 +81,7 @@ export class IdempotencyService {
// ─── MongoDB helpers ────────────────────────────────────────────────────────

private expiresAt(): Date {
return new Date(Date.now() + this.ttlSeconds * 1000);
return toUTC(Date.now() + this.ttlSeconds * 1000);
}

private async getFromMongo(key: string, endpoint: string): Promise<IdempotencyPayload | null> {
Expand Down
5 changes: 3 additions & 2 deletions src/sockets/location.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Server as SocketIOServer } from 'socket.io';
import logger from '../config/logger';
import { LocationUpdate } from '../models/LocationUpdate';
import { redisClient } from '../config/redis';
import { toUTC, nowUTC } from '../utils/dateUtils';
import {
DriverLocationUpdatePayload,
LocationBroadcastPayload,
Expand Down Expand Up @@ -225,7 +226,7 @@ export class LocationService {
}

const capturedAt = payload.capturedAt ?? Date.now();
const receivedAt = new Date().toISOString();
const receivedAt = nowUTC().toISOString();

// ── 2. Validate timestamp ────────────────────────────────────────────────
const timestampError = this.validateTimestamp(capturedAt);
Expand Down Expand Up @@ -281,7 +282,7 @@ export class LocationService {
driverId: new Types.ObjectId(driverId),
deliveryId: new Types.ObjectId(payload.deliveryId),
coordinates: { lat: payload.lat, lng: payload.lng },
capturedAt: new Date(capturedAt),
capturedAt: toUTC(capturedAt),
isOfflineSync: false,
status: 'pending',
});
Expand Down
9 changes: 5 additions & 4 deletions src/sockets/sync.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Types } from 'mongoose';
import logger from '../config/logger';
import { LocationUpdate, ILocationUpdate } from '../models/LocationUpdate';
import { toUTC, nowUTC } from '../utils/dateUtils';
import {
LocationSyncPayload,
OfflineLocationPoint,
Expand Down Expand Up @@ -41,7 +42,7 @@ export class SyncService {
driverId: string,
payload: LocationSyncPayload,
): Promise<LocationSyncAck> {
const processedAt = new Date().toISOString();
const processedAt = nowUTC().toISOString();

// ── 1. Validate driverId ─────────────────────────────────────────────────
if (!Types.ObjectId.isValid(driverId)) {
Expand Down Expand Up @@ -86,7 +87,7 @@ export class SyncService {
}

// ── 4. Fetch existing capturedAt values for this driver to detect dupes ──
const capturedAtDates = validPoints.map((p) => new Date(p.capturedAt));
const capturedAtDates = validPoints.map((p) => toUTC(p.capturedAt));

const existingDocs = await LocationUpdate.find(
{
Expand All @@ -96,7 +97,7 @@ export class SyncService {
{ capturedAt: 1 },
).lean<Pick<ILocationUpdate, 'capturedAt'>[]>();

const existingSet = new Set<number>(existingDocs.map((d) => new Date(d.capturedAt).getTime()));
const existingSet = new Set<number>(existingDocs.map((d) => toUTC(d.capturedAt).getTime()));

// ── 5. Build insertable documents, deduplicating within batch ─────────────
const seenInBatch = new Set<number>();
Expand All @@ -116,7 +117,7 @@ export class SyncService {
driverId: driverObjectId,
deliveryId: point.deliveryId ? new Types.ObjectId(point.deliveryId) : undefined,
coordinates: { lat: point.lat, lng: point.lng },
capturedAt: new Date(ts),
capturedAt: toUTC(ts),
isOfflineSync: true,
status: 'pending',
});
Expand Down
20 changes: 20 additions & 0 deletions src/utils/dateUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Returns the current date/time as a UTC Date object.
*/
export const nowUTC = (): Date => {
return new Date();
};

/**
* Parses a date string, number, or Date into a UTC Date object.
*/
export const toUTC = (date: Date | string | number): Date => {
return new Date(date);
};

/**
* Formats a date to an ISO 8601 string in UTC.
*/
export const toISOUTC = (date: Date): string => {
return date.toISOString();
};
Loading