Live video on the AT Protocol
at next 112 lines 3.0 kB view raw
1import { Platform } from "react-native"; 2 3let timeOffset = 0; 4let hasWarned = false; 5let OriginalDate: DateConstructor = Date; 6 7const CLOCK_DRIFT_THRESHOLD_MS = 5000; // 5 seconds 8 9export function getTimeOffset(): number { 10 return timeOffset; 11} 12 13export function setTimeOffset(offset: number): void { 14 timeOffset = offset; 15} 16 17export function checkClockDrift(serverTime: string): { 18 hasDrift: boolean; 19 driftMs: number; 20 driftSeconds: number; 21} { 22 const serverDate = new Date(serverTime); 23 const clientDate = new Date(); 24 const drift = Math.abs(serverDate.getTime() - clientDate.getTime()); 25 26 if (drift > CLOCK_DRIFT_THRESHOLD_MS) { 27 const driftSeconds = Math.round(drift / 1000); 28 if (!hasWarned) { 29 hasWarned = true; 30 console.warn( 31 `clock drift detected: ${driftSeconds}s difference from server time. ` + 32 `this may cause issues with time-sensitive operations. ` + 33 `please sync your system clock.`, 34 ); 35 } 36 return { hasDrift: true, driftMs: drift, driftSeconds }; 37 } else { 38 return { 39 hasDrift: false, 40 driftMs: drift, 41 driftSeconds: Math.round(drift / 1000), 42 }; 43 } 44} 45 46export function syncTimeWithServer( 47 serverTime: string, 48 networkLatencyMs: number, 49): void { 50 const serverDate = new OriginalDate(serverTime); 51 const clientDate = new OriginalDate(); 52 const offset = serverDate.getTime() - clientDate.getTime() - networkLatencyMs; 53 54 setTimeOffset(offset); 55} 56 57export function getSyncedDate(): Date { 58 const now = new Date(); 59 if (timeOffset !== 0) { 60 return new Date(now.getTime() + timeOffset); 61 } 62 return now; 63} 64 65export function getSystemDate(): Date { 66 return new OriginalDate(); 67} 68 69export function getSystemTime(): number { 70 return OriginalDate.now(); 71} 72 73export function initializeTimeSync(): void { 74 if (Platform.OS !== "web") { 75 return; 76 } 77 78 // store original Date 79 OriginalDate = Date; 80 const OriginalDatePrototype = OriginalDate.prototype; 81 82 // create patched Date constructor 83 function PatchedDate(this: any, ...args: any[]): any { 84 // If called as a function (no `new`), forward to original Date to get the string form 85 if (!(this instanceof PatchedDate)) { 86 return OriginalDate.apply(undefined, args as any); 87 } 88 89 // If called as a constructor, construct a Date with synced time when no args provided 90 if (args.length === 0) { 91 const syncedTime = OriginalDate.now() + timeOffset; 92 return Reflect.construct(OriginalDate, [syncedTime], PatchedDate); 93 } 94 95 // Otherwise construct with the provided arguments 96 return Reflect.construct(OriginalDate, args, PatchedDate); 97 } 98 99 // copy static methods 100 PatchedDate.now = function (): number { 101 return OriginalDate.now() + timeOffset; 102 }; 103 104 PatchedDate.parse = OriginalDate.parse; 105 PatchedDate.UTC = OriginalDate.UTC; 106 107 // copy prototype 108 PatchedDate.prototype = OriginalDatePrototype; 109 110 // replace global Date 111 (globalThis as any).Date = PatchedDate; 112}