Registry
AAugustinMauroy avatar

moment-to-temporal

Replaces deprecated moment.js usages with Temporal API equivalents.

AugustinMauroy5 executions

  • transformation
  • migration
  • moment.js
  • temporal

moment to Temporal

This codemod migrates common moment and moment-timezone usage to Temporal equivalents. It rewrites frequently used patterns such as “now”, add/subtract chains, simple formatting, parsing, durations, timezone conversion, and validation checks.

It also supports migrating a limited Luxon entrypoint (DateTime.local()) to Temporal in mixed codebases.

Goal

  • Replace common moment APIs with Temporal equivalents.
  • Remove moment / moment-timezone / luxon imports when transformed.
  • Keep transformed code explicit and readable.
  • Cover common migration paths while surfacing edge cases for review.

Example

Replace moment() + .format()

diff
- import moment from 'moment';
- const now = moment();
- console.log(now.format());
+ const now = Temporal.Now.zonedDateTimeISO();
+ console.log(now.toString());

Replace add/subtract chaining

diff
- import moment from 'moment';
- const m = moment().add(3, 'days').subtract(4, 'hours');
- console.log(m.format());
+ let z = Temporal.Now.zonedDateTimeISO();
+ z = z.plus({ days: 3 });
+ z = z.minus({ hours: 4 });
+ console.log(z.toString());

Replace simple date formatting (YYYY-MM-DD)

diff
- import moment from 'moment';
- const s = moment().format('YYYY-MM-DD');
+ const s = Temporal.Now.plainDateISO().toString();

Replace moment.tz(...)

diff
- import moment from 'moment-timezone';
- const m = moment.tz('2020-03-08 02:30', 'America/Los_Angeles');
- console.log(m.format());
+ const dt = Temporal.PlainDateTime.from('2020-03-08T02:30:00');
+ const tz = Temporal.TimeZone.from('America/Los_Angeles');
+ const instants = tz.getPossibleInstantsFor(dt);
+ const zdt = instants[0].toZonedDateTimeISO(tz.id);
+ console.log(zdt.toString());

Replace locale token formatting (LL)

diff
- import moment from 'moment';
- moment.locale('fr');
- const s = moment().format('LL');
+ const dt = Temporal.Now.plainDateTimeISO();
+ const s = new Intl.DateTimeFormat('fr', { dateStyle: 'long' }).format(dt);

Replace moment.duration(...)

diff
- import moment from 'moment';
- const d = moment.duration({ days: 1, hours: 2 });
- const later = moment().add(d);
- console.log(later.format());
+ const d = Temporal.Duration.from({ days: 1, hours: 2 });
+ const later = Temporal.Now.zonedDateTimeISO().plus(d);
+ console.log(later.toString());

What this codemod currently covers

  • moment() creation and .format() default usage.
  • .add(...) / .subtract(...) chains with common units.
  • format('YYYY-MM-DD') to Temporal.Now.plainDateISO().toString().
  • Parsing patterns:
    • moment('12/31/2020', 'MM/DD/YYYY')
    • moment('2020-01-02', 'YYYY-MM-DD')
  • Timezone pattern: moment.tz(datetime, timezone).
  • Unix epoch pattern: moment.unix(seconds).
  • Relative time pattern: .fromNow() (rewritten with Intl.RelativeTimeFormat).
  • Differences pattern: .diff(..., unit) for covered units.
  • Validation patterns:
    • moment('bad').isValid()
    • strict parse shape moment(value, 'YYYY-MM-DD', true).isValid()
  • Edge helpers for ordinals, quarters, weeks, start/end of day, UTC/local combinations.
  • moment.duration({...}) and add-duration flows.
  • Limited Luxon migration: DateTime.local() and .toISO().

Usage

Enable optional Temporal polyfill import insertion via workflow param:

  • Param: importPolyfill=true
  • Env (supported aliases): IMPORT_POLYFILL=true, CODEMOD_IMPORT_POLYFILL=true, or POLYFILL=true

When enabled, the transform attempts to inject:

  • ESM: import { Temporal } from '@js-temporal/polyfill';
  • CJS: const { Temporal } = require('@js-temporal/polyfill');

Caveats

  • This codemod focuses on known patterns from fixtures and may skip unsupported call shapes.
  • Token-rich moment.format(...) cases beyond covered patterns need manual migration.
  • Temporal timezone ambiguity resolution (e.g. DST folds/skips) may require project-specific policy.
  • Relative time wording can differ from Moment output because it uses Intl.RelativeTimeFormat.
  • Always review transformed code and run your app/tests after migration.
  • This codemod does not attempt to remove dep of your package.json or handle runtime polyfill loading. You must manage those separately after migration.

Ready to evolve your codebase?

Build tailored, compiler-aware skills to assess, plan, and automate complex codebase changes.