npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ruanitto/react-native-ntp-sync

v1.3.1

Published

Sync time using NTP servers

Readme

@ruanitto/react-native-ntp-sync

Forked from @luneo7/react-native-ntp-sync

React Native NTP client that fetches accurate time from the internet and keeps your app's clock in sync — regardless of the device's local clock drift.

How it works:

  1. Sends an NTP request to a configurable list of servers.
  2. Compensates for network round-trip delay using the standard RFC 5905 offset formula: ((T2−T1) + (T3−T4)) / 2.
  3. Validates the server response (stratum, synchronization status, timestamp sanity).
  4. Stores a rolling history of deltas and uses the median to compute corrected time — outliers from unstable networks are automatically rejected.
  5. Rotates to the next server on failure and retries automatically.

Installation

npm install @ruanitto/react-native-ntp-sync react-native-udp

On iOS, run pod install after installing:

cd ios && pod install

React Native >= 0.60 required. See react-native-udp compatibility for details.


Quick Start

import NTPSync from '@ruanitto/react-native-ntp-sync';

// Create instance with defaults — starts syncing immediately
const clock = new NTPSync();

// Use instead of Date.now() anywhere you need a reliable timestamp
const now = clock.getTime();
console.log(new Date(now).toISOString());

Configuration

All options are optional. Defaults shown below.

import NTPSync from '@ruanitto/react-native-ntp-sync';

const clock = new NTPSync({
  // NTP servers to use, tried in order on failure
  servers: [
    { server: 'time.google.com',     port: 123 },
    { server: 'time.cloudflare.com', port: 123 },
    { server: 'time.windows.com',    port: 123 },
    { server: '0.pool.ntp.org',      port: 123 },
    { server: '1.pool.ntp.org',      port: 123 },
  ],

  // Number of delta samples kept for median calculation
  history: 10,

  // How often to re-sync with NTP (ms). Default: 5 minutes
  syncInterval: 300_000,

  // Per-request timeout (ms). Default: 10 seconds
  syncTimeout: 10_000,

  // Sync immediately on instantiation
  syncOnCreation: true,

  // Start the auto-sync interval on instantiation
  autoSync: true,

  // Set to false to start in offline mode (no network calls)
  startOnline: true,
});

API

getTime(): number

Returns the current corrected Unix timestamp (ms). Use this instead of Date.now().

const timestamp = clock.getTime();
console.log(new Date(timestamp).toISOString()); // e.g. "2026-06-01T12:00:00.000Z"

The value is computed as Date.now() + median(deltas). If no sync has occurred yet, falls back to Date.now().


syncTime(): Promise<boolean>

Forces an immediate NTP sync. Returns true on success, false on failure or when offline.

const synced = await clock.syncTime();
if (!synced) {
  console.warn('Sync failed, using last known delta');
}

You generally don't need to call this manually — autoSync handles it. Useful for forcing a sync after a network reconnect or on app foreground.


getDelta(): Promise<NtpDelta>

Fetches a single delta from the current NTP server without updating history. Returns { delta: 0 } when offline.

const { delta, fetchingServer } = await clock.getDelta();
console.log(`Delta: ${delta}ms from ${fetchingServer?.server}`);

getHistory(): NtpHistory

Returns a snapshot of the current sync state. Safe to mutate — changes do not affect internal state.

const history = clock.getHistory();

console.log(history.currentServer);              // { server: 'time.google.com', port: 123 }
console.log(history.deltas.length);              // number of stored samples
console.log(history.isInErrorState);             // true if last sync failed
console.log(history.lifetimeErrorCount);         // total errors since creation
console.log(history.lastSyncTime);               // local timestamp of last successful sync

NtpHistory fields

| Field | Type | Description | |---|---|---| | currentServer | NtpServer | Server used for the next sync | | deltas | Delta[] | Rolling list of { dt, ntp } samples (max history entries) | | errors | object[] | Rolling list of sync errors (max history entries) | | isInErrorState | boolean | true if last sync failed | | lastSyncTime | number \| null | Local time of last successful sync (ms) | | lastNtpTime | number \| null | NTP time of last successful sync (ms) | | lastError | object \| null | Last error details | | currentConsecutiveErrorCount | number | Errors since last success | | maxConsecutiveErrorCount | number | Peak consecutive error streak | | lifetimeErrorCount | number | Total errors since creation |


setIsOnline(isOnline: boolean): void

Controls network activity. Useful when responding to connectivity changes.

  • true → immediately syncs and resumes auto-sync interval.
  • false → stops all network activity. getTime() continues to work using the last known deltas.
import NTPSync from '@ruanitto/react-native-ntp-sync';
import NetInfo from '@react-native-community/netinfo';

const clock = new NTPSync({ startOnline: false });

// Set initial state
NetInfo.fetch().then(state => {
  clock.setIsOnline(state.isConnected ?? false);
});

// React to connectivity changes
const unsubscribe = NetInfo.addEventListener(state => {
  clock.setIsOnline(state.isConnected ?? false);
});

// Clean up when done (e.g. component unmount)
unsubscribe();
clock.setIsOnline(false);

getIsOnline(): boolean

Returns the current online state of the instance.


startAutoSync(): void

Starts the periodic sync interval. No-op if already running.

stopAutoSync(): void

Stops the periodic sync interval.


addListener(handler: (history: NtpHistory) => void): void

Registers a callback invoked after every successful sync. Receives a NtpHistory snapshot.

clock.addListener(history => {
  console.log('Synced. Delta samples:', history.deltas.length);
  console.log('Last NTP time:', new Date(history.lastNtpTime!).toISOString());
});

removeListener(handler: (history: NtpHistory) => void): void

Removes a previously registered listener. Always call this when tearing down to avoid memory leaks.

const handler = (history: NtpHistory) => { /* ... */ };

clock.addListener(handler);

// Later, on cleanup:
clock.removeListener(handler);

Examples

Basic usage in a React Native component

import React, { useEffect, useState } from 'react';
import { Text, View } from 'react-native';
import NTPSync from '@ruanitto/react-native-ntp-sync';

const clock = new NTPSync();

export default function ClockDisplay() {
  const [time, setTime] = useState(clock.getTime());

  useEffect(() => {
    const interval = setInterval(() => {
      setTime(clock.getTime());
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return (
    <View>
      <Text>{new Date(time).toLocaleTimeString()}</Text>
    </View>
  );
}

Responding to network changes

import NTPSync from '@ruanitto/react-native-ntp-sync';
import NetInfo from '@react-native-community/netinfo';

// Start offline — no network calls until connectivity is confirmed
const clock = new NTPSync({ startOnline: false, autoSync: false });

const unsubscribe = NetInfo.addEventListener(state => {
  clock.setIsOnline(state.isConnected ?? false);
});

// On app shutdown / component unmount
function cleanup() {
  unsubscribe();
  clock.stopAutoSync();
}

Monitoring sync health

import NTPSync from '@ruanitto/react-native-ntp-sync';

const clock = new NTPSync();

clock.addListener(history => {
  if (history.currentConsecutiveErrorCount > 3) {
    console.warn('NTP sync struggling — check connectivity');
  }

  const drift = history.deltas.at(-1)?.dt ?? 0;
  console.log(`Clock drift: ${drift}ms`);
});

Manual sync on app foreground (React Native AppState)

import { AppState } from 'react-native';
import NTPSync from '@ruanitto/react-native-ntp-sync';

const clock = new NTPSync();

AppState.addEventListener('change', nextState => {
  if (nextState === 'active') {
    clock.syncTime(); // re-sync when app comes back to foreground
  }
});

Testing

npm test
npm run test:coverage

Tests use Jest with a full mock of react-native-udp, covering NTP packet parsing, round-trip compensation, server validation, server rotation, and all public API methods.


Types

type NtpServer = {
  server: string;
  port: number;
};

type Delta = {
  dt: number;   // delta between NTP and local time (ms)
  ntp: number;  // NTP server time (Unix ms)
};

type NtpDelta = {
  delta: number;
  fetchingServer?: NtpServer;
};

type Config = {
  autoSync: boolean;
  startOnline: boolean;
  history: number;
  servers: NtpServer[];
  syncInterval: number;
  syncOnCreation: boolean;
  syncTimeout: number;
};

Dependencies

License

MIT