Sync your Node.js app with NTP servers and get the real time — not whatever your system clock thinks it is.
Your system clock drifts. Servers disagree. Date.now() lies. This library fixes that with a proper 4-timestamp NTP implementation that compensates for network latency, validates server consistency, and corrects drift gradually without breaking your running timers.
npm install precise-time-ntpconst timeSync = require('precise-time-ntp');
await timeSync.sync();
timeSync.now() // Date object — precise NTP time
timeSync.timestamp() // "2026-04-25T15:30:45.123Z"
timeSync.offset() // how far off your system clock is, in msAll options can be set at construction time or overridden per sync() call.
const { TimeSync } = require('precise-time-ntp');
const t = new TimeSync({
// NTP servers to query (up to 3 are used for coherence validation)
servers: ['time.cloudflare.com', 'time.google.com', 'pool.ntp.org'],
timeout: 5000, // ms before a server is considered unreachable
coherenceValidation: true, // compare servers and use median offset
// Auto-sync on startup
autoSync: true,
autoSyncInterval: 300000, // re-sync every 5 minutes
// Smooth correction (see section below)
smoothCorrection: true,
maxCorrectionJump: 1000, // ms — apply instantly if diff is under this
correctionRate: 0.1, // fraction per sync cycle (0.1 = 10%)
maxOffsetThreshold: 5000, // ms — always apply instantly if diff exceeds this
locale: 'fr-FR', // used by format() — defaults to system locale
});Or pass options directly to sync() for a one-off override:
await timeSync.sync({
servers: ['time.cloudflare.com'],
timeout: 3000,
coherenceValidation: false,
});The default global instance uses pool.ntp.org, time.google.com, and time.cloudflare.com.
await timeSync.sync();
timeSync.startAutoSync(300000); // ms — re-sync every 5 minutesOr enable it at construction: new TimeSync({ autoSync: true, autoSyncInterval: 300000 }).
coherenceValidation queries multiple servers and uses the median offset — useful if one server is having a bad day. Emits a coherenceWarning event if servers disagree by more than 100ms.
By default, if your clock is off by 500ms and you re-sync, it jumps 500ms instantly. That can break timers and logs. Smooth correction applies the fix gradually:
timeSync.setSmoothCorrection(true, {
maxCorrectionJump: 1000, // ms — apply instantly if diff is under 1s
correctionRate: 0.1, // fraction per sync cycle (0.1 = 10%)
maxOffsetThreshold: 5000 // ms — always apply instantly if diff > 5s
});
// If you can't wait for the gradual correction to finish:
timeSync.forceCorrection();// Format current NTP time (pass null as first argument to use NTP time)
timeSync.format(null, 'iso') // "2026-04-25T15:30:45.123Z"
timeSync.format(null, 'locale') // system locale, e.g. "25/04/2026, 17:30:45"
timeSync.format(null, 'locale', 'en-US') // "4/25/2026, 5:30:45 PM"
timeSync.format(null, 'utc') // "Sat, 25 Apr 2026 15:30:45 GMT"
timeSync.format(null, 'date') // date only
timeSync.format(null, 'time') // time only
timeSync.format(null, 'timestamp') // Unix timestamp as string
// Format a specific date
timeSync.format('2026-01-01', 'iso') // "2026-01-01T00:00:00.000Z"
timeSync.format(new Date(), 'locale') // any Date object works too
// Difference between two dates — returns ms
timeSync.diff('2026-01-01', timeSync.now()) // e.g. 9849600000 ms
// console.log with NTP timestamp prefix
timeSync.log('order processed')
// → [2026-04-25T15:30:45.123Z] order processed
// Check sync status without throwing
if (timeSync.isSynchronized()) {
console.log(timeSync.now());
}// server.js
await timeSync.sync();
timeSync.startWebSocketServer(8080);
timeSync.startAutoSync(300000);<h1 id="clock"></h1>
<script>
const ws = new WebSocket('ws://localhost:8080');
ws.onmessage = (e) => {
const { data } = JSON.parse(e.data);
document.getElementById('clock').textContent =
new Date(data.timestamp).toLocaleTimeString();
};
</script>timeSync.on('sync', (data) => {
// data.server, data.offset (ms), data.rtt (ms)
console.log(`synced — offset: ${data.offset}ms, rtt: ${data.rtt}ms`);
});
timeSync.on('error', (err) => {
// err.message, err.server
console.error(`sync failed on ${err.server}: ${err.message}`);
});
timeSync.on('coherenceWarning', (data) => {
// servers disagree by more than 100ms
console.warn(`server variance: ${data.variance}ms`, data.servers);
});
timeSync.on('driftWarning', (data) => {
// now() called more than 1 hour after last sync
console.warn(`${(data.elapsed / 3600000).toFixed(1)}h since last sync`);
});
timeSync.on('correctionComplete', (data) => {
// data.finalOffset, data.converged, data.forced, data.timeout
console.log(`correction done — final offset: ${data.finalOffset}ms`);
});const s = timeSync.stats();
s.synchronized // true/false
s.offset // ms — current system clock error
s.rtt // ms — last network round-trip time
s.correctedOffset // ms — offset currently being applied
s.correctionInProgress // true while smooth correction is running
s.lastSync // Date of last successful sync
s.uptime // ms since last sync| Method | Description |
|---|---|
sync(options?) |
Sync with NTP servers |
now() |
Current precise time as a Date |
timestamp() |
Current time as ISO string |
offset() |
System clock error in ms |
isSynchronized() |
Returns true if synced (no throw) |
stats() |
Full sync diagnostics |
startAutoSync(interval) |
Re-sync on an interval (interval in ms) |
stopAutoSync() |
Stop auto-sync |
setSmoothCorrection(bool, options?) |
Configure gradual correction |
forceCorrection() |
Apply pending correction immediately |
startWebSocketServer(port) |
Broadcast time over WebSocket |
stopWebSocketServer() |
Stop WebSocket server |
format(date?, format?, locale?) |
Format a date (iso, locale, utc, date, time, timestamp) |
diff(date1, date2?) |
Difference between two dates — returns ms |
log(message) |
console.log with NTP timestamp prefix |
Full option reference: docs/api-reference.md
npm run basic # simple sync
npm run auto-sync # auto-sync loop
npm run websocket # WebSocket + HTML clockMIT