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

orrery.js

v0.1.0

Published

Universal metric time for any celestial body. Earth, Mars, Pluto, Io — same format, different constants.

Readme


Why

Human time is a mess. 60 seconds, 60 minutes, 24 hours, 365.25 days — Babylonian legacy baked into every clock on Earth. It doesn't even work on Earth, let alone Mars.

orrery.js replaces all of it with one universal system:

  • 1000 ticks per day — metric, clean, no base-60
  • Any celestial body — same format, different constants
  • Self-describing timestamps — readable without a manual
  • SI seconds canonical — constants drift, timestamps don't
T56:039:[email protected]        Earth — year 56, day 39, tick 629
T29:556:[email protected]        Mars  — different constants, same format
T4:07649:[email protected]       Jupiter — 5-digit day field, still works
T116:[email protected]        Mercury — day > year, canonical fallback

Install

npm install orrery.js

Quick Start

import orrery from 'orrery.js';

// Earth time — implied, like moment()
const clock = orrery();
clock.format(clock.now());        // T56:039:[email protected]

// Any body
const mars = orrery({
  name: 'Mars',
  daySeconds: 88775.244,          // synodic day in SI seconds
  yearSeconds: 687 * 86400,       // orbital period in SI seconds
});
mars.format(mars.now());          // T29:556:[email protected]

Timestamp Format

T{year}:{day}:{tick}.{subtick}@{tickSeconds}{±longitude}

| Field | Meaning | |-------|---------| | T | Prefix — identifies an orrery timestamp | | year | Years since Unix epoch (1970-01-01) | | day | Day within year (zero-indexed, variable width) | | tick | Tick within day (000–999) | | subtick | Thousandths of a tick (000–999) | | @tickSeconds | SI seconds per tick — makes the timestamp self-describing | | ±longitude | Meridian offset in degrees (-180 to +180) |

clock.format(ts);                                    // T56:039:[email protected]
clock.format(ts, 'display', { longitude: -74 });     // T56:039:[email protected]
clock.format(ts, 'display', { subtick: false });      // T56:039:[email protected]
clock.format(ts, 'canonical');                        // T20493:[email protected]
clock.format(ts, 'full');                             // T56:039:[email protected] (T20493:[email protected])

Why Two Formats?

Display (Y:DDD:tick) uses year + day-of-year. Human-friendly, but only works when a body has ≥2 days per year.

Canonical (D:tick) uses continuous day count from epoch. Universal — works on every body, including Mercury (where one day is two years).

The library auto-selects. If daysPerYear < 2, display falls back to canonical.

Converting

const clock = orrery();

// Orrery → Unix ms / Date
const ts = clock.now();
clock.toMs(ts);                   // 1770651038689
clock.toDate(ts);                 // Date: 2026-02-09T15:30:38.689Z

// Unix ms → Orrery
clock.at(Date.now() / 1000);      // Timestamp

// Date → Orrery
clock.fromDate(new Date());       // Timestamp

// Parse string → Timestamp
clock.parse('T56:039:[email protected]');  // Timestamp
clock.toMs(clock.parse('T56:039:[email protected]'));  // 1770638400000

Body Configuration

A body needs two numbers:

interface Body {
  name: string;
  daySeconds: number;     // synodic day (solar noon → solar noon) in SI seconds
  yearSeconds: number;    // orbital period in SI seconds
}

Synodic day — always. Not sidereal, not orbital period. Solar noon to solar noon, consistently, on every body. This is why Luna's day is 29.53 Earth days (not 27.32), and why tidally locked moons work correctly.

Built-in Bodies

21 bodies ship with the core — 8 planets, 5 dwarf planets, 8 major moons:

import orrery, {
  earth, mars, mercury, venus, jupiter, saturn, uranus, neptune,
  pluto, ceres, eris, haumea, makemake,
  luna, io, europa, ganymede, callisto, titan, enceladus, triton, charon,
} from 'orrery.js';

const m = orrery(mars);
m.format(m.now());  // T29:556:[email protected]

Or define your own:

const sedna = orrery({ name: 'Sedna', daySeconds: 36000, yearSeconds: 11400 * 365.25 * 86400 });

Reference Table

| Body | Day (Earth hours) | Days/Year | Tick (s) | Notes | |-----------|-------------------|-----------|----------|-------| | Earth | 24.0 | 365 | 86.4 | | | Mars | 24.7 | 669 | 88.8 | | | Mercury | 4222.6 | 0.5 | 15201.4 | Day > year | | Venus | 2802.0 | 1.9 | 10087.2 | Retrograde | | Jupiter | 9.9 | 10476 | 35.7 | | | Saturn | 10.7 | 24160 | 38.4 | | | Uranus | 17.2 | 42700 | 62.1 | 98° tilt | | Neptune | 16.1 | 89800 | 58.0 | | | Pluto | 153.3 | 3.7 | 551.9 | | | Ceres | 9.1 | 4450 | 32.7 | | | Eris | 25.9 | 6543 | 93.2 | | | Haumea | 3.9 | 17745 | 14.0 | Fastest dwarf planet | | Makemake | 22.8 | 3277 | 82.1 | | | Luna | 708.7 | 12.4 | 2551.4 | Tidally locked | | Io | 42.5 | 2447 | 152.9 | Tidally locked | | Europa | 85.2 | 1220 | 306.7 | Tidally locked | | Ganymede | 171.7 | 605 | 618.2 | Tidally locked | | Callisto | 400.5 | 260 | 1441.9 | Tidally locked | | Titan | 382.7 | 780 | 1377.6 | Tidally locked | | Enceladus | 32.9 | 7850 | 118.4 | Tidally locked | | Triton | 141.0 | 2854 | 507.8 | Retrograde orbit | | Charon | 153.3 | 3.7 | 551.9 | Pluto-locked |

Edge Cases

Mercury & Venus — day longer than year. The year:day:tick format breaks (what's "day of year" when there's half a day per year?). The library falls back to canonical format automatically.

Gas giants — thousands of days per year. The day field widens (Jupiter needs 5 digits). The format handles this — day field width adapts to daysPerYear.

Tidally locked moons — day = orbital period around parent. Synodic day differs slightly from orbital period (because the parent also moves around the Sun). The library uses synodic day, consistently.

Tumbling bodies — asteroids like Toutatis, moons like Hyperion — rotate chaotically with no stable axis. Not supported. Time on a tumbling body is genuinely undefined in the rotational sense. orrery.js requires a stable rotation axis.

Constant drift — Earth's day lengthens ~2.3ms/century. The canonical timestamp is SI seconds from epoch. Body constants are projections. When constants are refined, display changes; the underlying timestamp doesn't.

Design Principles

  1. Day = synodic day. Solar noon to solar noon. No exceptions.
  2. 1000 ticks per day. Metric. The tick duration varies per body — that's the point.
  3. SI seconds canonical. The true timestamp. Everything else is a projection.
  4. Self-describing. The @ suffix tells you the tick duration. No lookup table needed.
  5. Meridian offsets, not time zones. Longitude in degrees, not political boundaries.
  6. Implied Earth. Call orrery() with no arguments. Earth isn't special — it's just the default.

API

orrery(body?: Body): Clock

Create a clock. No arguments = Earth.

Clock

| Method | Returns | Description | |--------|---------|-------------| | .now() | Timestamp | Current time on this body | | .at(epochSeconds) | Timestamp | Time at SI seconds since epoch | | .fromDate(date) | Timestamp | Time from a JS Date | | .format(ts, fmt?, opts?) | string | Format a timestamp | | .parse(str) | Timestamp | Parse an orrery format string | | .toMs(ts) | number | Convert to Unix milliseconds | | .toDate(ts) | Date | Convert to JS Date | | .tickSeconds | number | SI seconds per tick | | .daysPerYear | number | Days per year for this body |

FormatOptions

| Option | Type | Default | Description | |--------|------|---------|-------------| | longitude | number | — | Meridian offset in degrees | | subtick | boolean | true | Include .subtick | | division | boolean | true | Include @tickSeconds |

License

MIT