Skip to content
Mig ration F low
data dates moment date-fns

Moment.js date-fns

Replace Moment.js with date-fns for tree-shakeable, immutable date utilities.

Copy for

Using an AI agent? See /agents for MCP, JSON, and llms.txt access.

Moment.js → date-fns

Philosophy shift

Moment.js mutates dates and ships as a single large bundle (~67 kB gzipped). date-fns is a collection of pure functions — tree-shakeable, immutable, and composable. You import only what you use.

Rule: Native Date objects stay as-is. date-fns functions accept and return Date — no wrapper type.

Setup

Remove Moment:

npm uninstall moment

Install date-fns:

npm install date-fns

Core transformations

Parsing

// Moment
moment('2024-01-15');
moment('15/01/2024', 'DD/MM/YYYY');

// date-fns
import { parseISO, parse } from 'date-fns';
parseISO('2024-01-15');
parse('15/01/2024', 'dd/MM/yyyy', new Date());

Formatting

// Moment
moment().format('YYYY-MM-DD');
moment().format('DD/MM/YYYY HH:mm');

// date-fns
import { format } from 'date-fns';
format(new Date(), 'yyyy-MM-dd');
format(new Date(), 'dd/MM/yyyy HH:mm');

Relative time

// Moment
moment('2023-01-01').fromNow();

// date-fns
import { formatDistanceToNow } from 'date-fns';
formatDistanceToNow(parseISO('2023-01-01'), { addSuffix: true });

Arithmetic

// Moment (mutates!)
moment().add(7, 'days');
moment().subtract(1, 'month');

// date-fns (returns new Date)
import { addDays, subMonths } from 'date-fns';
addDays(new Date(), 7);
subMonths(new Date(), 1);

Comparison

// Moment
moment('2024-01-15').isBefore(moment('2024-06-01'));
moment('2024-01-15').isAfter(moment('2024-06-01'));
moment('2024-01-15').isSame(moment('2024-01-15'), 'day');

// date-fns
import { isBefore, isAfter, isSameDay } from 'date-fns';
isBefore(parseISO('2024-01-15'), parseISO('2024-06-01'));
isAfter(parseISO('2024-01-15'), parseISO('2024-06-01'));
isSameDay(parseISO('2024-01-15'), parseISO('2024-01-15'));

Difference between dates

// Moment
moment('2024-06-01').diff(moment('2024-01-01'), 'days');

// date-fns
import { differenceInDays } from 'date-fns';
differenceInDays(parseISO('2024-06-01'), parseISO('2024-01-01'));

Start/end of period

// Moment
moment().startOf('month');
moment().endOf('week');

// date-fns
import { startOfMonth, endOfWeek } from 'date-fns';
startOfMonth(new Date());
endOfWeek(new Date());

Locales

// Moment (global side-effect)
import 'moment/locale/pt-br';
moment.locale('pt-BR');

// date-fns (explicit per call, no global state)
import { format } from 'date-fns';
import { ptBR } from 'date-fns/locale';
format(new Date(), "d 'de' MMMM", { locale: ptBR });

Format token differences

Momentdate-fnsMeaning
YYYYyyyy4-digit year
DDdd2-digit day
DodoOrdinal day
HHHH24-hour hour
hhhh12-hour hour
AaAM/PM
diDay of week (ISO)
DDDDDay of year

When NOT to migrate

Pitfalls

Validation checklist

Codemod references

AI Prompt

You are migrating date manipulation code from Moment.js to date-fns.

Rules:
1. Replace all `moment(...)` calls with equivalent date-fns functions.
2. Import only the specific functions used (no wildcard imports).
3. Map format tokens: YYYY→yyyy, DD→dd, D→d (day of week), MM→MM, etc.
4. Operations return new Date instances — never mutate in place.
5. Replace global locale setup with per-call `{ locale }` options.
6. Do not change the surrounding logic — only replace the date manipulation.

Migrate the following file:

References