Skip to content

Commit 0ef413b

Browse files
authored
Merge pull request #174 from starboytiimz/refactor/standardize-utc-timezone
refactor: standardize UTC date handling across backend
2 parents dfdcf16 + 84a5531 commit 0ef413b

6 files changed

Lines changed: 34 additions & 9 deletions

File tree

src/models/DriverProfile.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import mongoose, { Schema } from 'mongoose';
22
import { IDriverProfile, ReputationTier } from '../interfaces/IDriverProfile';
3+
import { nowUTC } from '../utils/dateUtils';
34

45
const vehicleDetailsSchema = new Schema(
56
{
@@ -16,7 +17,7 @@ const vehicleDetailsSchema = new Schema(
1617
year: {
1718
type: Number,
1819
min: [1980, 'Vehicle year must be 1980 or later'],
19-
max: [new Date().getFullYear() + 1, 'Vehicle year cannot be in the future'],
20+
max: [nowUTC().getUTCFullYear() + 1, 'Vehicle year cannot be in the future'],
2021
},
2122
plateNumber: {
2223
type: String,

src/services/escrowService.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Escrow, { EscrowLockStatus, IEscrow } from '../models/Escrow';
44
import { sorobanService } from '../blockchain/soroban.service';
55
import AppError from '../utils/AppError';
66
import logger from '../config/logger';
7+
import { nowUTC } from '../utils/dateUtils';
78

89
// ─── DTOs ──────────────────────────────────────────────────────────────────────
910

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

5455
// Note: Commented out until EscrowStatus.EXPIRED and expiresAt field are added to model
5556
// const expiredCandidates = await Escrow.find({

src/services/idempotency.service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import IdempotencyRecord, { IdempotencyStatus } from '../models/IdempotencyRecor
44
import env from '../config/env';
55
import logger from '../config/logger';
66
import { AppError } from '../utils/AppError';
7+
import { toUTC } from '../utils/dateUtils';
78

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

8283
private expiresAt(): Date {
83-
return new Date(Date.now() + this.ttlSeconds * 1000);
84+
return toUTC(Date.now() + this.ttlSeconds * 1000);
8485
}
8586

8687
private async getFromMongo(key: string, endpoint: string): Promise<IdempotencyPayload | null> {

src/sockets/location.service.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Server as SocketIOServer } from 'socket.io';
33
import logger from '../config/logger';
44
import { LocationUpdate } from '../models/LocationUpdate';
55
import { redisClient } from '../config/redis';
6+
import { toUTC, nowUTC } from '../utils/dateUtils';
67
import {
78
DriverLocationUpdatePayload,
89
LocationBroadcastPayload,
@@ -226,7 +227,7 @@ export class LocationService {
226227
}
227228

228229
const capturedAt = payload.capturedAt ?? Date.now();
229-
const receivedAt = new Date().toISOString();
230+
const receivedAt = nowUTC().toISOString();
230231

231232
// ── 2. Validate timestamp ────────────────────────────────────────────────
232233
const timestampError = this.validateTimestamp(capturedAt);
@@ -282,7 +283,7 @@ export class LocationService {
282283
driverId: new Types.ObjectId(driverId),
283284
deliveryId: new Types.ObjectId(payload.deliveryId),
284285
coordinates: { lat: payload.lat, lng: payload.lng },
285-
capturedAt: new Date(capturedAt),
286+
capturedAt: toUTC(capturedAt),
286287
isOfflineSync: false,
287288
status: 'pending',
288289
});

src/sockets/sync.service.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Types } from 'mongoose';
22
import logger from '../config/logger';
33
import { LocationUpdate, ILocationUpdate } from '../models/LocationUpdate';
4+
import { toUTC, nowUTC } from '../utils/dateUtils';
45
import {
56
LocationSyncPayload,
67
OfflineLocationPoint,
@@ -42,7 +43,7 @@ export class SyncService {
4243
driverId: string,
4344
payload: LocationSyncPayload,
4445
): Promise<LocationSyncAck> {
45-
const processedAt = new Date().toISOString();
46+
const processedAt = nowUTC().toISOString();
4647

4748
// ── 1. Validate driverId ─────────────────────────────────────────────────
4849
if (!Types.ObjectId.isValid(driverId)) {
@@ -87,7 +88,7 @@ export class SyncService {
8788
}
8889

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

9293
const existingDocs = await LocationUpdate.find(
9394
{
@@ -97,7 +98,7 @@ export class SyncService {
9798
{ capturedAt: 1 },
9899
).lean<Pick<ILocationUpdate, 'capturedAt'>[]>();
99100

100-
const existingSet = new Set<number>(existingDocs.map((d) => new Date(d.capturedAt).getTime()));
101+
const existingSet = new Set<number>(existingDocs.map((d) => toUTC(d.capturedAt).getTime()));
101102

102103
// ── 5. Build insertable documents, deduplicating within batch ─────────────
103104
const seenInBatch = new Set<number>();
@@ -117,7 +118,7 @@ export class SyncService {
117118
driverId: driverObjectId,
118119
deliveryId: point.deliveryId ? new Types.ObjectId(point.deliveryId) : undefined,
119120
coordinates: { lat: point.lat, lng: point.lng },
120-
capturedAt: new Date(ts),
121+
capturedAt: toUTC(ts),
121122
isOfflineSync: true,
122123
status: 'pending',
123124
});

src/utils/dateUtils.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Returns the current date/time as a UTC Date object.
3+
*/
4+
export const nowUTC = (): Date => {
5+
return new Date();
6+
};
7+
8+
/**
9+
* Parses a date string, number, or Date into a UTC Date object.
10+
*/
11+
export const toUTC = (date: Date | string | number): Date => {
12+
return new Date(date);
13+
};
14+
15+
/**
16+
* Formats a date to an ISO 8601 string in UTC.
17+
*/
18+
export const toISOUTC = (date: Date): string => {
19+
return date.toISOString();
20+
};

0 commit comments

Comments
 (0)