mrkeyoor.com_
Sun 20 Sept 04:59 UTC
npmUtilsupdated 20 Sept 2026

moment review

Moment 2.30.1 puts parsing, formatting, calendar arithmetic, durations, relative labels, UTC mode, and locale output on a chainable wrapper around JavaScript `Date`. Those wrappers are mutable: calls such as `add()`, `startOf()`, and `local()` change the same object. Named IANA zones require the separate `moment-timezone` package. The README now labels Moment a legacy project in maintenance mode and says new features are closed. Version 2.30.1 itself is a December 2023 patch that reverted a TypeScript change from 2.30.0 after it broke substantial user code.

25.4Mdownloads / wk
Verdict

Our Moment 2.30.1 browser build weighed 60.4 KB minified and 19.5 KB gzipped, while the maintainers accept maintenance fixes instead of new features. Keep it inside existing systems that depend on its mutable semantics; choose a maintained date model for greenfield code.

We installed it

Lab card: what happened when we installed momentScreenshot of moment documentation
Install✓ · 0.7s1 package on disk · 6 MB
ImportESM import works · require() works · CommonJS package
Browser19.5 KBgzipped (60.4 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does moment install cleanly?

Yes. In a fresh container with an empty cache, npm install moment finished in 0.7s, leaving 1 package and 6 MB on disk. npm audit reported no known vulnerabilities.

How much does moment add to a browser bundle?

19.5 KB gzipped (60.4 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does moment work with both ESM and CommonJS?

Yes. Both import 'moment' and require('moment') worked in Node 22 in our run. The package is published as CommonJS.

Does moment include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

moment or dayjs: which should you use?

dayjs: Use it when a Moment-like chain and immutable instances make an incremental migration practical. Our Moment 2.30.1 browser build weighed 60.4 KB minified and 19.5 KB gzipped, while the maintainers accept maintenance fixes instead of new features.

When should you not use moment?

This is a new application. The project README calls Moment legacy, places it in maintenance mode, and declines new features.

API stability5/5Moment 2.x retains its long-running constructor, chainable mutation, formatting tokens, durations, locales, and UTC methods. The strongest evidence is version 2.30.1: maintainers reverted a 2.30.0 TypeScript change one day later because it broke substantial user code. Maintenance mode makes further feature churn unlikely, although the same frozen design also means mutation and permissive parsing will remain part of the contract.
Docs4/5The official reference explains strict parsing, validity flags, cloning, mutation, UTC and offset modes, duration components, locale loading, and webpack controls with runnable fragments. It also says plainly that the project is legacy and points readers toward alternatives. The material is a very long single page, so related operational facts such as global locale state, dynamic locale bundling, and named-zone separation take deliberate searching.
Maintenance2/5The README limits work to maintenance and rejects new features. Package 2.30.1 dates to 2023 and only backs out a breaking TypeScript change from the prior day's release. GitHub marked the existing tag as its latest release in July 2026, the repository was pushed on 2026-08-23, and 106 issues and pull requests remain open. Fixes still happen, but planned API development is explicitly over.
Ecosystem5/5npm counted 36,798,324 downloads from 2026-08-19 through 2026-08-25, and GitHub shows 47,915 stars. Moment remains embedded in older applications, framework plugins, examples, and `moment-timezone` integrations. That installed base makes maintenance knowledge easy to find. It does not change the project's own advice for new work, and download volume includes legacy and transitive consumers.

Discussed on

  1. hnWe now consider Moment.js to be a legacy project in maintenance mode1,129 points
  2. hnThat awkward moment when Apple mocked good hardware and poor people1,078 points
  3. hnAsk HN: Comment here about whatever you're passionate about at the moment982 points
  4. hnLarge language models are having their Stable Diffusion moment811 points
  5. hnAsk HN: What was your "oh shit" moment with GenAI?739 points

Use it if

  • An established product already passes Moment objects across application or plugin boundaries.
  • A repair must preserve existing parsing, locale, duration, or `moment-timezone` output before a separately tested migration.
  • An older CommonJS or browser stack values long-standing behavior more than tree shaking and immutable values.
  • A required dependency returns Moment instances, making a second date model more confusing than a contained use of the same API.
Skip it if

Setup reality

We installed Moment 2.30.1 in a clean Node 22 Bookworm container. npm finished in 0.7 seconds and left 1 package using 6 MB. The audit returned 0 vulnerabilities at all 4 severities. Moment declares 0 direct dependencies and 0 peer dependencies, reports 5,412 KB unpacked, bundles TypeScript declarations, and accepts every Node version through node: *. Both require() and ESM import worked in our checks.

There is no required config file. Input handling is the first sharp edge: free-form strings may fall through to the host Date parser, and invalid input produces an invalid Moment instead of throwing. For form or file data, supply the expected format, enable strict parsing, and check isValid(). Every Moment is mutable, so clone before add(), subtract(), startOf(), endOf(), utc(), or local() when another caller still needs the original value.

Our esbuild test of a package-wide import produced 60.4 KB minified and 19.5 KB gzipped. The package is CommonJS and has no exports map. Moment also resolves locales dynamically, which can make webpack include far more locale code than the application uses. Restrict or ignore the locale context, import each supported locale explicitly, and test the production artifact. Calling moment.locale() changes the default globally; server code should prefer an instance locale.

Core 2.30.1 does not ship IANA zone rules. Install moment-timezone when named zones are required, then plan for zone-data updates as governments change rules. Calendar boundaries deserve tests around daylight-saving changes. endOf('day') mutates and creates a final millisecond; database filters are easier to reason about with an inclusive start and an exclusive next-day value.

Patterns

Parse an ISO instant and format local time parse-iso

const moment = require('moment');

const value = moment('2026-08-24T09:30:00Z');
console.log(value.format('YYYY-MM-DD HH:mm'));
console.log(value.toISOString());

A string ending in `Z` describes UTC, but a normal Moment displays it in the machine's local zone unless you keep UTC mode.

Reject input outside one date format parse-strict-format

const value = moment('24/08/2026', 'DD/MM/YYYY', true);
if (!value.isValid()) {
  throw new Error('Expected DD/MM/YYYY');
}

The third argument enables strict mode. Without it, separators and partial matches may be accepted.

Read strict-parsing failure details inspect-invalid-input

const value = moment(userInput, 'YYYY-MM-DD', true);
if (!value.isValid()) {
  const flags = value.parsingFlags();
  console.error(flags.overflow, flags.unusedInput);
}

Construction does not throw for an invalid date. Check validity at the input boundary before the value reaches arithmetic or storage.

Keep the source date unchanged clone-before-arithmetic

const openedAt = moment('2026-08-24T09:00:00Z');
const expiresAt = openedAt.clone().add(30, 'minutes');

console.log(openedAt.toISOString());
console.log(expiresAt.toISOString());

`add()` mutates its receiver. `clone()` creates the separate Moment needed when both values remain in use.

Create a half-open UTC day range build-day-range

const start = moment.utc('2026-08-24').startOf('day');
const nextDay = start.clone().add(1, 'day');

const inside = candidate.isSameOrAfter(start) && candidate.isBefore(nextDay);

The exclusive next-day boundary avoids relying on the millisecond made by `endOf('day')`. Both boundary methods mutate.

Request whole or fractional differences measure-difference

const placed = moment('2026-08-01');
const shipped = moment('2026-08-24');

const wholeDays = shipped.diff(placed, 'days');
const exactWeeks = shipped.diff(placed, 'weeks', true);

`diff()` truncates unless its third argument is `true`. Choose the result shape explicitly.

Format a relative deadline render-relative-time

const deadline = moment().add(3, 'hours');
console.log(deadline.fromNow());
console.log(deadline.fromNow(true));

Relative phrases use configurable rounding thresholds. Keep exact scheduling logic on numeric timestamps or durations.

Parse directly into UTC mode stay-in-utc

const recorded = moment.utc('2026-08-24 09:30', 'YYYY-MM-DD HH:mm');
console.log(recorded.format());
console.log(recorded.toISOString());

const localCopy = recorded.clone().local();

`local()` changes display mode on that instance. Clone first when code still needs the UTC-mode object.

Convert between named IANA zones convert-time-zone

const moment = require('moment-timezone');

const meeting = moment.tz('2026-08-24 18:00', 'Asia/Kolkata');
const london = meeting.clone().tz('Europe/London');
console.log(london.format('YYYY-MM-DD HH:mm z'));

This requires the separate `moment-timezone` package and its zone database. Update that data when rule changes matter to the application.

Separate duration components from totals read-duration-total

const elapsed = moment.duration(1500, 'minutes');
console.log(elapsed.days());
console.log(elapsed.hours());
console.log(elapsed.asHours());

`hours()` returns the component left after whole days. `asHours()` converts the complete duration.

Set locale on one Moment localize-one-value

require('moment/locale/fr');

const label = moment('2026-08-24')
  .locale('fr')
  .format('dddd D MMMM');
console.log(label);

`moment.locale('fr')` changes the global default. Instance locale avoids one server request changing another request's language.

Exclude automatic locale loading in webpack trim-webpack-locales

const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.IgnorePlugin({
      resourceRegExp: /^\.\/locale$/,
      contextRegExp: /moment$/
    })
  ]
};

Ignoring the locale context makes unimported locales unavailable. Import each language the application supports and verify the production bundle.

Alternatives

PackageRegistryPick it when
dayjsnpmUse it when a Moment-like chain and immutable instances make an incremental migration practical.
date-fnsnpmUse it when independent functions over native dates suit per-function imports.
luxonnpmUse it when immutable date-time objects and IANA zones should be built around `Intl`.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.