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

@brightchain/brightdate

v0.32.6

Published

BrightDate - A scientifically grounded universal decimal time system based on J2000.0 epoch and TAI

Readme

npm Tests

BrightDate

A Universal Decimal Time System for Software Engineers and Scientists

Named in homage to Star Trek's Stardate and reference to BrightChain — a single scalar that resolves the chaos of planetary timezones into one universal quantity. BrightDate does the same for Earth.

BrightDate is a scientifically grounded, timezone-free time representation anchored at J2000.0 — the standard astronomical epoch used by every modern observatory, space agency, and ephemeris. One scalar value. Trivially sortable, diffable, and storable.

Ships with three companion types:

  • BrightDate — Float64 decimal days. Ergonomic, fast, great for math and astronomy. This is what most code should use.
  • BrightInstant — BigInt TAI seconds + integer nanos. 1-nanosecond precision at any magnitude, with no Float64 drift. The rigorous form for distributed systems, GPS engineering, and interplanetary timing.
  • ExactBrightDate — BigInt picoseconds. Bit-exact for every integer-ms input. Use at storage boundaries where lossless round-trips matter.

Why BrightDate?

| Problem | BrightDate Solution | |---------|-------------------| | Timezone confusion | Single universal value, no zones | | Leap second ambiguity | TAI mode for monotonic time (no stutters) | | Complex date arithmetic | Simple subtraction: b - a = elapsed_days | | Sort/compare complexity | Float64 sorts natively; v2 sortable-string encoding handles mixed-sign indexes correctly | | 2038 problem | Float64 covers 287,000+ years with sub-µs resolution | | Blockchain/archival fidelity | ExactBrightDate (BigInt picoseconds) for bit-exact Unix-ms round-trip | | Interplanetary coordination | Naturally works across the solar system |


Core Concept

Format: DDDDD.ddddd
         ↑          ↑
         |          Fractional day (decimal time-of-day)
         Integer days since J2000.0 epoch

BrightDate is a count of SI days elapsed since J2000.0.

The J2000.0 Epoch — Astronomically Correct

J2000.0 is defined in Terrestrial Time (TT): the moment 2000-01-01T12:00:00 TT. Converted to the timescales you actually encounter in software:

| Timescale | Representation | Value | |-----------|---------------|-------| | TT (definition) | 2000-01-01T12:00:00.000 (no zone) | Unix s 946_728_000 | | TAI | 2000-01-01T11:59:27.816 (no zone) | Unix s 946_727_967.816 | | UTC label | 2000-01-01T11:58:55.816Z | Unix ms 946_727_935_816 |

The UTC label (2000-01-01T11:58:55.816Z) is what you see when you call new Date(946_727_935_816) in JavaScript. It is not UTC noon; the 64.184-second gap is caused by the TT–TAI offset (32.184 s) plus the 32-second TAI–UTC offset at J2000.

BrightDate = 0 at 2000-01-01T11:58:55.816Z. This is the only astronomically correct choice.

TAI Substrate

All BrightDate values are computed on a TAI substrate:

bd = (taiUnixSeconds − J2000_TAI_UNIX_S) / 86400

where J2000_TAI_UNIX_S = 946_727_967.816.

This means:

  • BrightDate ticks in exact SI seconds, with no discontinuities.
  • Leap seconds exist only at UTC boundary conversions (toISO, fromISO, toUnixMs, fromDate, etc.). Internally, they are invisible.
  • Two consecutive UTC wall-clock seconds that straddle a leap second boundary correspond to 2 SI seconds in BrightDate, because TAI advances by 2 during that transition. This is the correct physical behavior.

Installation

TypeScript / JavaScript (npm)

npm install @brightchain/brightdate
# or
yarn add @brightchain/brightdate

Rust (crates.io)

A Rust port of BrightDate is published as the brightdate crate, with the same J2000.0 / TAI semantics as this library:

# Cargo.toml
[dependencies]
brightdate = "0.1"
use brightdate::BrightDate;
let now = BrightDate::now();
println!("{:.5}", now);

Source: Digital-Defiance/brightdate-rust.

CLI utilities (Homebrew + cargo)

The Rust workspace also ships five drop-in replacements for the classic Unix date/time utilities — bdate, btime, buptime, bcal, bwatch — that emit BrightDate values.

# Homebrew (macOS / Linux)
brew tap digital-defiance/tap
brew install digital-defiance/tap/bdate
brew install digital-defiance/tap/btime
brew install digital-defiance/tap/buptime
brew install digital-defiance/tap/bcal
brew install digital-defiance/tap/bwatch

# Or via cargo
cargo install bdate btime buptime bcal bwatch

Quick Start

import { BrightDate } from '@brightchain/brightdate';

const now = BrightDate.now();
console.log(now.toString());       // "9622.50417"
console.log(now.toLogString());    // "[9622.50417]"

const bd = BrightDate.fromDate(new Date('2025-06-15T10:30:00Z'));
const bd2 = BrightDate.fromISO('2025-01-01T00:00:00Z');

const tomorrow = now.addDays(1);
const elapsed = tomorrow.difference(now);            // 1.0
console.log(now.formatDurationTo(tomorrow));         // "1.000 days"
console.log(now.isBefore(tomorrow));                 // true

Precision & Trade-offs

BrightDate is honest about what it does and doesn't preserve. This section is the important part — read it before choosing between BrightDate and ExactBrightDate.

Summary matrix

| Operation | BrightDate (Float64) | ExactBrightDate (BigInt ps) | |-----------|------------------------|-------------------------------| | fromDate(d).toDate().getTime()d.getTime() | ✅ Always bit-exact (Date has ms resolution; fromDate is lossless in that direction) | ✅ Always bit-exact | | fromUnixMs(ms).toUnixMs()ms (day-aligned ms) | ✅ Bit-exact | ✅ Bit-exact | | fromUnixMs(ms).toUnixMs()ms (arbitrary ms) | ⚠️ Error ≤ 0.00012 ms (~120 ns) | ✅ Bit-exact | | toBinary / fromBinary round-trip | ✅ Bit-exact Float64 | ✅ Bit-exact 128-bit integer | | encode(v, 'utc', 12) / decode | ✅ Preserves to 12 decimal places (~86 ps) | ✅ Bit-exact (stores full BigInt) | | Arithmetic | Standard IEEE 754 (1-2 ULP max on composite ops) | Bit-exact integer math | | Astronomy (GMST, lunar phase, etc.) | ✅ Native fit | ❌ Not provided (use toBrightDateValue()) | | Cross-node determinism (same V8 version) | ✅ IEEE 754 is deterministic | ✅ BigInt is deterministic | | Speed | Native Float64 ops (~0.5 ns) | BigInt ops (~30-300 ns) |

What Float64 gives you at different magnitudes

A BrightDate value is a Float64, so the distance to the next representable value (one ULP) grows with the magnitude:

| At magnitude | ULP in days | ULP in seconds | What this means | |--------------|-------------|----------------|-----------------| | 0 (J2000.0) | 5e-324 | sub-yoctosecond | Full Float64 precision | | 0.5 (noon boundary) | 1.1e-16 | ~9.6 ps | Picosecond-clean | | 1 (one day) | 2.2e-16 | ~19 ps | Picosecond-clean | | 10,000 (current era, ~2027) | 2.2e-12 | ~190 ns | Sub-microsecond | | 100,000 (year 2273) | 2.2e-11 | ~1.9 µs | Sub-10-microsecond | | 1,000,000 (year 4737) | 2.2e-10 | ~19 µs | Still far better than any NTP jitter |

Takeaway: for the current era (BrightDate values around 9,000-11,000), BrightDate holds about 190-nanosecond resolution. That's above mechanical clock jitter, CPU cache latency, and well above NTP synchronization accuracy.

When the 0.00012 ms round-trip tax appears

// Day-aligned inputs (multiples of 86_400_000 from J2000 anchor) round-trip exactly:
fromUnixMs(0).toUnixMs()                === 0;              // Unix epoch: ✅
fromUnixMs(946_728_000_000).toUnixMs() === 946_728_000_000; // J2000:     ✅

// Off-day inputs gain a bounded error (~2^-52 × 946 728 000 000 ≈ 0.00012 ms):
const ms = 1_700_000_000_123;
Math.abs(fromUnixMs(ms).toUnixMs() - ms) < 0.001;           // true (≈1.2e-4)

If your system can tolerate sub-microsecond error at the Unix-ms boundary, use BrightDate. If it cannot — e.g., a blockchain that stores user-supplied Unix ms and must return them byte-identical — use ExactBrightDate.

Float64 algebraic identities — limits

Most identities hold with bit-exactness for realistic inputs. A few don't, because IEEE 754 has well-known limits:

  • lerp(a, b, 0) === a — ✅ always (the term (b-a)*0 is always ±0, addition with a is exact).
  • lerp(a, b, 1) === b — ✅ usually, but can differ by 1-2 ULP when |b-a| is comparable to |a| (catastrophic cancellation).
  • (v + d) - d === v — ✅ usually, but can differ by 1-2 ULP in the same cancellation regime.

These are properties of IEEE 754 arithmetic, not BrightDate bugs. The library's property-based tests assert the honest bounds (≤ 2-4 ULP at realistic magnitudes). For workloads that cannot tolerate any ULP drift, use ExactBrightDate and integer picosecond math.

Choosing between BrightDate and ExactBrightDate

Pick BrightDate (Float64) when:

  • You're doing astronomy, scheduling, analytics, logging, display, or interval math.
  • You need fast operator-friendly arithmetic (a - b, a < b, Math.abs(a - b)).
  • You're storing a timestamp that will be compared and diffed, not exactly reconstructed later.

Pick ExactBrightDate (BigInt picoseconds) when:

  • You must round-trip arbitrary Unix milliseconds bit-for-bit.
  • You need blockchain consensus on exact user-supplied timestamps.
  • You're archiving timestamps for century-scale retention and cannot risk any drift.
  • You need sub-picosecond precision at any magnitude.

You can mix them: store ExactBrightDate at boundaries, convert to BrightDate for computation, convert back if needed.


Metric Sub-Units

| Unit | Value | Real-World Equivalent | |------|-------|----------------------| | 1 day | 1.0 | 24 hours | | 1 milliday (md) | 0.001 | 1 min 26.4 s | | 1 microday (μd) | 0.000001 | 86.4 ms | | 1 nanoday (nd) | 0.000000001 | 86.4 µs |

import { formatDuration } from '@brightchain/brightdate';

formatDuration(0.5);     // "500.000 millidays"
formatDuration(0.00035); // "350.000 microdays"
formatDuration(2.5);     // "2.500 days"

Conversions

const bd = BrightDate.now();

bd.toDate();               // JavaScript Date (ms resolution)
bd.toUnixMs();             // Unix milliseconds (integer; Math.round applied)
bd.toUnixSeconds();        // Unix seconds (Number)
bd.toJulianDate();         // JD = value + 2_451_545.0
bd.toModifiedJulianDate(); // MJD = value + 51_544.5
bd.toISO();                // ISO 8601 string; renders :60 seconds during leap second
bd.toGPSTime();            // { gpsWeek, gpsSeconds }

// TAI — monotonic, no leap-second discontinuities
const tai = bd.toTAI();
const backToUtc = tai.toUTC();

Leap second boundary behavior

Because BrightDate uses a TAI substrate, a UTC timestamp immediately after a leap second is 2 SI seconds later in BrightDate than the timestamp immediately before. The leap second itself is rendered as :60 in toISO(). This is physically correct: during a positive leap second, the TAI clock advances by 2 while the UTC clock repeats :59. Callers who compute BrightDate differences see the correct SI elapsed time.


BrightInstant (TAI seconds + nanos)

BrightInstant is the rigorous, lossless companion to BrightDate. It stores BigInt TAI seconds + integer nanoseconds since J2000.0 — so you get exact 1-nanosecond precision at any magnitude, with no Float64 drift. Use it when nanosecond fidelity matters indefinitely far from the epoch (distributed systems, GPS, interplanetary mission timing).

import { BrightInstant } from '@brightchain/brightdate';

// J2000.0 itself
const epoch = BrightInstant.J2000;
epoch.taiSecondsSinceJ2000; // 0n
epoch.taiNanos;             // 0

// One SI day later, plus one nanosecond
const later = BrightInstant.fromTaiComponents(86_400n, 1);

// Round-trip the f64 form
const bd = BrightDate.now();
const inst = BrightInstant.fromBrightDate(bd);
const back = inst.toBrightDate(); // lossy to f64 resolution; lossless within range

// UTC / ISO / Unix-ms round-trips honor the leap-second table
BrightInstant.fromUnixMs(Date.now()).toUnixMs();

ExactBrightDate (BigInt picoseconds)

import { ExactBrightDate } from '@brightchain/brightdate';

// Bit-exact Unix ms round-trip (unconditionally, for any integer ms)
const ms = 1_700_000_000_123;
ExactBrightDate.fromUnixMs(ms).toUnixMs() === ms; // true, always

// Integer-unit arithmetic
const e = ExactBrightDate.epoch();
e.addNanoseconds(1n).addMilliseconds(500n).addDays(7n);

// Bit-exact binary / encoded forms
const bin = e.toBinary();           // 16-byte big-endian two's complement
ExactBrightDate.fromBinary(bin);    // bit-exact round-trip

const s = e.encode();               // "EBD1:<picoseconds>"
ExactBrightDate.decode(s);          // bit-exact round-trip

// Differences in any unit
const later = ExactBrightDate.now();
later.differencePicoseconds(e);    // bigint
later.differenceMilliseconds(e);   // bigint

Convert between the two when needed:

const e = ExactBrightDate.fromUnixMs(Date.now());
const bd = BrightDate.fromValue(e.toBrightDateValue()); // lossy to Float64 resolution

Intervals

import { BrightDateInterval, BrightDate } from '@brightchain/brightdate';

const meeting = BrightDateInterval.fromDuration(BrightDate.now(), 0.5 / 24);

meeting.duration;                       // 0.02083...
meeting.formatDuration();               // "20.833 millidays"
meeting.contains(BrightDate.now());     // true

const quarters = meeting.split(4);
const padded = meeting.expand(0.01);

Logging

import {
  BrightDateStopwatch,
  createLogEntry,
  formatLogEntry,
} from '@brightchain/brightdate';

const entry = createLogEntry('info', 'Service started', { port: 3000 });
console.log(formatLogEntry(entry));
// [9622.50417] INFO  Service started {"port":3000}

const sw = new BrightDateStopwatch();
sw.start();
// ... work ...
sw.stop();
console.log(sw.elapsedFormatted); // "2.314 millidays"

Scheduling

import { nextOccurrences, INTERVALS, BrightDate } from '@brightchain/brightdate';

const hourly = nextOccurrences(
  {
    start: BrightDate.now().value,
    intervalDays: INTERVALS.HOUR,
    maxOccurrences: 5,
  },
  5,
);

Astronomy & Interplanetary

import {
  greenwichMeanSiderealTime,
  lunarPhaseName,
  lightDelayTo,
  formatMarsTime,
  BrightDate,
} from '@brightchain/brightdate';

const now = BrightDate.now();

greenwichMeanSiderealTime(now.value); // degrees
lunarPhaseName(now.value);            // "Waxing Gibbous"
lightDelayTo('mars');                 // ~0.00882 days (~12.7 min)
formatMarsTime(now.value);            // "14:23:07 MTC"

These formulae are intentionally lower-precision than the core time math — they use standard approximations (IAU 1982 for GMST, simplified sinusoidal orbits). For high-precision astronomy, use a dedicated ephemeris library and feed it BrightDate values.


Serialization

import {
  encode, decode,
  toSortableString, fromSortableString,
  toBinary, fromBinary,
} from '@brightchain/brightdate';

// Compact string: "BD1:9622.50417000" (or "BD1:9622.50417:tai")
encode(9622.50417);
decode('BD1:9622.50417000'); // { value: 9622.50417, timescale: 'utc' }

// Sortable string for databases (v2 nine's-complement format):
// lexicographic order MATCHES numeric order across positive and negative values
toSortableString(9622.50417);  // "+0009622.50417000"
toSortableString(-10957.5);    // "!9989042.49999999" (nine's complement)

// Binary — bit-exact Float64
toBinary(9622.50417);          // 8-byte ArrayBuffer (big-endian)
fromBinary(buf);               // 9622.50417 (Object.is bit-exact)

Database index note

The sortable string format uses nine's complement for negative values and a ! prefix (instead of -). This is necessary because the ASCII character '+' (0x2B) sorts before '-' (0x2D), which would invert the expected ordering of mixed-sign timestamps. Both ! (0x21) and the nine's-complement scheme ensure that lexicographic ordering of the strings exactly matches numeric ordering of the values — including the negative-to-positive transition. The older (v1) -N format is still readable by fromSortableString for backward compatibility, but new writes use v2.


Reference Dates

| Event | ISO 8601 (UTC) | BrightDate | |-------|---------------|-----------| | J2000.0 (TAI substrate anchor) | 2000-01-01T11:58:55.816Z | 0.000000000 | | TT noon (definition moment) | 2000-01-01T12:00:00.000Z | ≈ 0.000742870 | | Y2K midnight | 2000-01-01T00:00:00Z | ≈ −0.499257130 | | Unix epoch | 1970-01-01T00:00:00Z | ≈ −10957.499512 | | GPS epoch | 1980-01-06T00:00:00Z | ≈ −7300.499408 | | Apollo 11 landing | 1969-07-20T20:17:40Z | ≈ −11125.154 | | Current era (~2027) | — | ≈ 10,000 |


Key Constants

import {
  J2000_UTC_UNIX_MS,       // 946_727_935_816  — UTC label of J2000.0 in Unix ms
  J2000_TAI_UNIX_S,        // 946_727_967.816  — TAI Unix seconds at J2000.0
  J2000_TT_UNIX_S,         // 946_728_000      — TT (definition) Unix seconds
  J2000_JD,                // 2_451_545.0      — Julian Date at J2000.0
  J2000_MJD,               // 51_544.5         — Modified Julian Date at J2000.0
  TAI_UTC_OFFSET_AT_J2000, // 32               — TAI − UTC at J2000.0 (seconds)
  TT_TAI_OFFSET_SECONDS,   // 32.184           — TT − TAI (seconds, fixed by definition)
  CURRENT_TAI_UTC_OFFSET,  // 37               — current TAI − UTC (seconds, as of 2017)
  GPS_EPOCH_UNIX_TAI,      // 315_964_819      — GPS epoch as TAI Unix seconds
} from '@brightchain/brightdate';

Leap Seconds

Leap seconds are an afterthought in UTC, not a physical phenomenon. BrightDate embraces this:

  • Internally: leap seconds don't exist. BrightDate ticks in strict SI days on a TAI substrate.
  • At UTC boundaries: the leap second table (LEAP_SECOND_TABLE) maps TAI seconds to UTC seconds. The table is valid through LEAP_SECOND_TABLE_VALID_UNTIL_UNIX_S and was last reviewed on LEAP_SECOND_TABLE_REVIEWED_AT.
  • toISO() during a leap second: renders the leap-second moment as :60, e.g. 1998-12-31T23:59:60.000Z.
  • If the table expires: getTaiUtcOffset() throws a LeapSecondTableExpiredError. Update the library to continue.

Design Philosophy

  1. One number, one timeline — no zones, no formats, no ambiguity.
  2. Astronomically correct — TAI substrate, J2000.0 anchor, SI days throughout.
  3. Engineer-friendly — arithmetic is just addition and subtraction.
  4. Honest about precision — documented Float64 bounds; exact BigInt companion for when you need it.
  5. Future-proof — works through at least year 287,000 without losing sub-microsecond precision.
  6. In homage to Stardate — one universal scalar to rule them all.

License

MIT © Digital Defiance