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

@birtalanrobert/clock

v1.1.0

Published

Wall-clock and interval arithmetic across time zones, without a dependency

Readme

@birtalanrobert/clock

Wall-clock and interval arithmetic across time zones. No dependencies, no framework, no database.

pnpm add @birtalanrobert/clock

Why it exists

Two sentences, forgotten, account for most scheduling bugs:

A date is not an instant. "Tuesday the fourteenth" at a salon in Bucharest and one in Budapest are different spans of real time, and anything that conflates them is wrong for one of them. So a LocalDate is a YYYY-MM-DD string in some location's own reckoning, never a Date.

A duration is not a difference of wall clocks. A 22:00–06:00 shift is eight hours on almost every night of the year, seven on the spring-forward night and nine on the autumn one. The elapsed time between two instants is the honest answer; it is the boundaries that need the zone.

import { toInstant, localMinuteOfDay, durationMinutes } from '@birtalanrobert/clock';

// 02:30 does not exist on the spring-forward night. Say so, rather than
// silently landing an hour away.
const { instant, resolution } = toInstant('Europe/Bucharest', '2027-03-28', 150);
resolution; // 'gap'

What it gives you

| | | | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | offsetMinutesAt | The offset a zone is on at an instant, read from Intl rather than a table — so the tz database is the runtime's and updates with it | | toInstant | A local wall clock to an instant, reporting gap and ambiguous rather than hiding them | | localDateOf · localMinuteOfDay | The reverse, in a location's own reckoning | | transitionMinutesOn | How many minutes a local date actually had — 1380 or 1500, twice a year | | overlaps · intersect · subtract · normalise | Interval arithmetic on instants |

MinuteOfDay may exceed 1440 on purpose: a bar open until 02:00 closes at minute 1560 of the day it opened, not at minute 120 of the next one. That is what makes an overnight span one interval rather than two rows something has to remember to stitch together.

Where it came from

Project 02's slot engine, extracted at its second consumer — project 07's rota — under the rule that a capability two or more specifications call for belongs here from the start. Six of the seventeen schedule against somebody's local wall clock.

What stays in each project is the engine built on this: the slot engine, the scheduling rules engine, the costing engine. They share this substrate and no logic.

Calendar months

addDays has siblings: addMonths, daysInMonth, startOfMonth, endOfMonth.

addMonths clamps to the target month's last day rather than rolling over. 31 January plus one month is 28 February — the 29th in a leap year — and not the 3rd of March, which is what Date.prototype.setMonth produces. That is the rule both of this programme's markets expect for a rent payment day, and it is the rule every subscription and tariff cycle needs too; deciding it once is the reason it lives here rather than at each call site.

It is not reversible — 31 January forward a month and back again is 28 January — so anything generating a series must step from a fixed anchor rather than from its own previous result, or every February permanently shortens the payment day.