Skip to main content

How to Migrate from Moment.js to date-fns

·PkgPulse Team

TL;DR

Migrating from Moment.js to date-fns is mostly a find-and-replace. The main mental model shift: Moment.js is OOP (chained methods on a Moment object), date-fns is functional (pure functions that take a Date). You'll write format(new Date(), 'yyyy-MM-dd') instead of moment().format('YYYY-MM-DD'). One gotcha: date-fns uses different format token casing — yyyy not YYYY, dd not DD.

Key Takeaways

  • Mental model shift: OOP chains → pure functions
  • Format tokens differ: YYYYyyyy, DDdd, HHHH (same), mmmm (same)
  • Moment objects → native Date: date-fns works with JavaScript's built-in Date
  • All functions are pure — no mutation, no hidden timezone state
  • Bundle impact: Moment.js 72KB → date-fns only what you import (~2-5KB typical)

The API Mapping

Basic Formatting

// Before (Moment.js):
moment().format('YYYY-MM-DD');          // '2026-03-08'
moment().format('MMMM Do YYYY');        // 'March 8th 2026'
moment('2026-03-08').format('dddd');    // 'Sunday'

// After (date-fns):
import { format } from 'date-fns';
format(new Date(), 'yyyy-MM-dd');       // '2026-03-08'
format(new Date(), 'MMMM do yyyy');     // 'March 8th 2026'
format(new Date('2026-03-08'), 'EEEE'); // 'Sunday'

Format Token Differences

⚠️  The most common source of bugs during migration:

Moment.js  →  date-fns   Meaning
─────────────────────────────────────────────────
YYYY       →  yyyy       Full year (2026)
YY         →  yy         Short year (26)
DD         →  dd         Day of month (08)
D          →  d          Day of month (8, no pad)
ddd        →  EEE        Short day name (Sun)
dddd       →  EEEE       Full day name (Sunday)
A          →  a          AM/PM (uppercase result: AM)
a          →  aaa        am/pm (lowercase)
Z          →  xxx        Timezone offset (+05:30)
x          →  T          Unix milliseconds (use getTime())

Unchanged:
MM         →  MM         Month number
MMM        →  MMM        Short month (Mar)
MMMM       →  MMMM       Full month (March)
HH         →  HH         24hr hours
hh         →  hh         12hr hours
mm         →  mm         Minutes
ss         →  ss         Seconds

Parsing

// Before (Moment.js):
moment('2026-03-08');                   // Parse ISO string
moment('03/08/2026', 'MM/DD/YYYY');     // Parse with format
moment(1709856000000);                  // Parse Unix milliseconds
moment.unix(1709856);                   // Parse Unix seconds

// After (date-fns):
import { parseISO, parse, fromUnixTime } from 'date-fns';
parseISO('2026-03-08');                 // Parse ISO string
parse('03/08/2026', 'MM/dd/yyyy', new Date()); // Parse with format
new Date(1709856000000);               // Native Date for milliseconds
fromUnixTime(1709856);                  // Parse Unix seconds

Date Arithmetic

// Before (Moment.js — mutable!):
const date = moment();
date.add(7, 'days');                    // Mutates date!
date.subtract(1, 'month');              // Mutates date!
date.startOf('month');                  // Mutates date!
date.endOf('week');                     // Mutates date!

// After (date-fns — immutable, always returns new Date):
import { addDays, subMonths, startOfMonth, endOfWeek } from 'date-fns';
const date = new Date();
const plusWeek = addDays(date, 7);          // New Date, original unchanged
const minusMonth = subMonths(date, 1);      // New Date
const monthStart = startOfMonth(date);      // New Date
const weekEnd = endOfWeek(date);            // New Date

Comparison

// Before (Moment.js):
moment('2026-03-08').isBefore(moment('2026-06-01'));
moment('2026-03-08').isAfter(moment('2026-01-01'));
moment('2026-03-08').isSame(moment('2026-03-08'), 'day');
moment('2026-03-08').isBetween('2026-01-01', '2026-12-31');
moment('2026-03-08').diff(moment('2026-01-01'), 'days');

// After (date-fns):
import { isBefore, isAfter, isSameDay, isWithinInterval, differenceInDays } from 'date-fns';
isBefore(new Date('2026-03-08'), new Date('2026-06-01'));
isAfter(new Date('2026-03-08'), new Date('2026-01-01'));
isSameDay(new Date('2026-03-08'), new Date('2026-03-08'));
isWithinInterval(new Date('2026-03-08'), {
  start: new Date('2026-01-01'),
  end: new Date('2026-12-31'),
});
differenceInDays(new Date('2026-03-08'), new Date('2026-01-01')); // 66

Relative Time

// Before (Moment.js):
moment('2026-01-01').fromNow();         // "2 months ago"
moment('2027-01-01').fromNow();         // "in 10 months"
moment().from(moment('2026-01-01'));    // "in a few seconds"

// After (date-fns):
import { formatDistanceToNow, formatDistance } from 'date-fns';
formatDistanceToNow(new Date('2026-01-01'), { addSuffix: true }); // "2 months ago"
formatDistanceToNow(new Date('2027-01-01'), { addSuffix: true }); // "in 10 months"
formatDistance(new Date(), new Date('2026-01-01'));                 // "about 2 months"

Timezone Migration

// Moment.js: moment-timezone plugin
import moment from 'moment-timezone';
moment.tz('2026-03-08T14:00:00', 'America/New_York').format();

// date-fns: date-fns-tz package
import { formatInTimeZone, toZonedTime, fromZonedTime } from 'date-fns-tz';

// Format in a specific timezone
formatInTimeZone(new Date('2026-03-08T14:00:00Z'), 'America/New_York', 'yyyy-MM-dd HH:mm zzz');
// '2026-03-08 09:00 EST'

// Convert UTC to local timezone object
const nyDate = toZonedTime(new Date('2026-03-08T14:00:00Z'), 'America/New_York');

// Convert local timezone to UTC
const utcDate = fromZonedTime(new Date('2026-03-08T09:00:00'), 'America/New_York');

i18n Migration

// Moment.js: import and set locale globally
import 'moment/locale/fr';
moment.locale('fr');
moment().format('MMMM');  // 'mars'

// date-fns: import locale per function call (tree-shakeable)
import { format } from 'date-fns';
import { fr } from 'date-fns/locale';

format(new Date(), 'MMMM', { locale: fr });  // 'mars'
formatDistanceToNow(new Date(), { locale: fr, addSuffix: true }); // 'il y a quelques secondes'

Codemod: Automate the Migration

# @date-fns/upgrade codemod (community tool)
npx @date-fns/upgrade

# Handles:
# - moment() → new Date()
# - .format() → format() with token conversion
# - .add() / .subtract() → addDays/subDays etc.
# - Import replacements

# Always review the diff manually — format tokens need verification
git diff src/

Compare date library package health on PkgPulse.

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.