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

@mdaemon/moment-temporal

v1.0.1

Published

Drop-in moment replacement backed by the Temporal API, with graceful fallback to moment when Temporal is unavailable.

Readme

Dynamic JSON Badge Static Badge TypeScript install size Dynamic JSON Badge Node.js CI

@mdaemon/moment-temporal

A drop-in replacement for moment that is backed by the TC39 Temporal API when one is available on the host. If Temporal is not available, the package returns the installed moment module unchanged so your code keeps working without any runtime changes.

[ @mdaemon/moment-temporal on npm ]

How it works

At load time the library probes globalThis.Temporal:

  • Temporal present → the default export is a moment-shaped callable backed by Temporal.ZonedDateTime. Calling .add(), .format(), .tz(), .duration(), etc. routes through Temporal internally. The surface is deliberately moment's, so existing code doesn't change.
  • Temporal absent → the default export is the installed moment module (identity-equal; momentTemporal === moment holds). Every call resolves against raw moment / moment-timezone with no wrapping overhead.

Detection happens exactly once. No per-call branching. No feature flags.

Install

$ npm install @mdaemon/moment-temporal --save

Node CommonJS

const moment = require("@mdaemon/moment-temporal/dist/moment-temporal.cjs");

moment().add(1, "day").format("YYYY-MM-DD");
moment.tz("2023-06-15T12:00:00Z", "America/New_York").format("LLLL");

Node / ES Modules / TypeScript

import moment from "@mdaemon/moment-temporal/dist/moment-temporal.mjs";
// or the unscoped import — bundlers pick the right output via `module`/`main`:
import moment from "@mdaemon/moment-temporal";

const m = moment("2023-06-15T12:00:00Z");
m.add(1, "month").subtract(3, "days");
console.log(m.format("dddd, MMMM Do YYYY"));

Web (UMD)

<script src="/path_to_modules/dist/moment-temporal.umd.js"></script>
<script>
  // Exposed as window.MomentTemporal in UMD mode
  MomentTemporal().format("YYYY-MM-DD");
</script>

Temporal polyfill

Native Temporal is not yet shipped in every runtime. If you want the Temporal-backed path everywhere, install a polyfill before importing this package:

import { Temporal } from "@js-temporal/polyfill";
globalThis.Temporal = Temporal;

import moment from "@mdaemon/moment-temporal";

Either @js-temporal/polyfill or temporal-polyfill works. The package itself does not bundle a polyfill — consumers choose.

Bundle size: lazy moment loading under ESM bundlers

The ESM build (dist/moment-temporal.mjs) loads moment and moment-timezone via dynamic import() behind top-level await. That means bundlers which honor the module field and support code-splitting (Vite, modern Rollup, Webpack 5+) treat moment as a separate chunk that is only fetched when the fallback path actually runs.

What this means for your bundle:

  • Temporal is available (polyfill installed or native support) → moment is never requested. The moment chunk sits in dist/assets/ but no <script> tag ever fetches it. Your initial JS payload does not include moment.
  • Temporal is not available → one extra async fetch for the moment chunk, then the API is identity-equal to raw moment.

To get the small-bundle path, install a Temporal polyfill before importing the package:

// app entry (e.g. src/main.ts in a Vite app)
import { Temporal } from "@js-temporal/polyfill";
globalThis.Temporal = Temporal;

// Now import moment-temporal. The dynamic branch for moment is
// dead code from the bundler's point of view — it will still be
// emitted as a chunk, but never fetched at runtime.
import moment from "@mdaemon/moment-temporal";

Polyfill sizes for reference: temporal-polyfill is ~20KB gzipped, versus moment + moment-timezone at ~70KB+ gzipped. Dropping moment entirely is usually a net win.

CJS and UMD consumers get the eager load behavior — CJS has no top-level await, and UMD is designed for script tags that assume synchronous availability. dist/moment-temporal.cjs and dist/moment-temporal.umd.js static-require moment at module load time, same as the fallback contract always has.

API

Public surface mirrors moment and moment-timezone one-for-one. Commonly used entry points:

| Category | Methods | |---|---| | Parse | moment(), moment(input), moment(input, format), moment.utc(), moment.unix(), moment.tz() | | Get / Set | year, month, date, day, hour, minute, second, millisecond, get, set | | Manipulate | add, subtract, startOf, endOf, local, utc, tz | | Display | format, fromNow, from, toNow, to, calendar, diff, valueOf, unix, toDate, toArray, toJSON, toISOString, toObject, toString | | Query | isBefore, isSame, isAfter, isSameOrBefore, isSameOrAfter, isBetween, isDST, isLeapYear, isValid, isMoment, isDate | | Duration | moment.duration, .asYears/Months/Days/Hours/Minutes/Seconds/Milliseconds, .humanize, .toISOString | | Timezone | moment.tz, moment.tz.guess, moment.tz.names, moment.tz.zone, moment.tz.setDefault, .tz(zone), .zoneName() | | Locale | moment.locale(), .locale() |

Temporal-first semantics

This library presents moment's API surface, but its behavior is governed by the Temporal specification. Where moment and Temporal disagree, Temporal wins. A small set of outputs will not byte-match moment. These differences are intentional and locked in by the moment-divergence test suite.

  1. Localized format output comes from Intl.DateTimeFormat with the stored locale, not moment's bundled locale files. Most strings match in common locales; edge cases (LLLL weekday spellings in some languages, ordinals in non-English locales) may differ.
  2. Week numbering is ISO 8601: weeks start on Monday and week 1 is the week containing the first Thursday. Moment defaults to Sunday-start in English locales — that is not replicated.
  3. DST arithmetic follows Temporal's rules. Ambiguous local times at DST boundaries resolve via Temporal's disambiguation: 'compatible' default.
  4. Strict parsing — input strings that Temporal refuses report isValid() === false. Moment's forgiving parser (e.g. "June 15 2023", "2023-13-45") is not replicated.
  5. diff on variable-length units (month, year) uses Temporal.Duration.total with a relativeTo, which is more accurate than moment's heuristic.
  6. Custom timezones (moment.tz.add) are a no-op with a one-time deprecation warning. Temporal reads IANA zones from the host's CLDR database and cannot be extended at runtime.
  7. Deprecated moment internals (moment.fn, internal parser hooks) are not implemented. Only the documented public surface.

Fallback behavior caveat

When Temporal is not present on the host, the default export is raw moment — which means the fallback path produces moment's output, including the quirks listed above. A consumer running on two different hosts (one with Temporal, one without) can see different output for the same input in those edge cases.

For byte-identical behavior across environments, install a Temporal polyfill as shown above. The wrapper path is then always used and all hosts agree.

Versioning

This package is pre-1.0 (0.x.y) while the Temporal-backed surface stabilizes. Non-breaking additions land in minor versions; divergence-test-guarded behavior changes are breaking and bump the minor until 1.0.

License

Published under the LGPL-2.1 license.

Published by MDaemon Technologies, Ltd. Simple Secure Email https://www.mdaemon.com