Moment.js Cheatsheet - Format, Parse & Date Calculations

This reference is for JavaScript developers doing everyday date work — formatting a timestamp for display, adding days to an expiry, comparing two dates, or showing "3 days ago". It walks creation from strings, timestamps, and object literals (mind the zero-based month), formatting tokens, add/subtract and startOf/endOf snapping, comparison helpers, relative time, and timezone handling with moment-timezone. Note that moment is in maintenance mode; prefer native Date or Day.js for new code. After reading you should be able to parse, format, shift, and compare dates, and render friendly relative strings.

Languages·39 commands·Last updated 2026-07-21
momentDatesTimeFormatting

Typical Use Case

For front-end developers maintaining an existing codebase that already uses moment heavily: order timestamps, relative time ("3 days ago"), duration maths, cross-timezone scheduling. One fact has to come first — moment is officially in maintenance mode and receives no new features, so new projects should prefer dayjs, date-fns or the native Temporal API. This sheet keeps the APIs you will actually look up while reading legacy code: parsing, formatting, add/subtract, comparison, range checks, diff, relative time, timezones and durations. Use it to read old code and to compare semantics during a migration, not to justify new adoption.

Creating Dates 5

moment()
Current time
moment("2024-01-15")
Parse from a string
moment("2024-01-15", "YYYY-MM-DD")
Parse with an explicit format
moment(Date.now())
Create from a timestamp
moment({ year: 2024, month: 0, day: 15 })
Create from an object

Formatting 5

moment().format("YYYY-MM-DD")
2024-01-15
moment().format("HH:mm:ss")
14:30:00
moment().format("dddd, MMMM Do YYYY")
Monday, January 15th 2024
moment().format("LLL")
Localized long format
moment().toISOString()
ISO 8601 format

Manipulating Dates 5

moment().add(7, "days")
Add 7 days
moment().subtract(1, "month")
Subtract 1 month
moment().startOf("day")
Set to start of day (00:00:00)
moment().endOf("month")
Set to end of month (23:59:59)
moment().year(2025).month(0)
Set year and month

Queries & Comparison 8

moment().isBefore(other)
Whether before another
moment().isAfter(other)
Whether after another
moment().isSame(other, "day")
Whether on the same day
moment().isBetween(start, end)
Whether within a range
moment().isSameOrBefore(other)
Same or before
moment().isSameOrAfter(other)
Same or after
moment().diff(other, "days")
Compute the difference (in a unit)
moment().isValid()
Whether a valid date

Relative Time 6

moment().fromNow()
3 days ago
moment().fromNow(true)
3 days (no 'ago')
moment().toNow()
in 3 days
moment().calendar()
Today 14:30 / Yesterday 09:00
moment().from(other)
Offset relative to another moment
moment().to(other)
Offset to another moment

Timezones & Localization 5

moment().utc()
Convert to UTC
moment().local()
Convert to local time
moment.locale("zh-cn")
Set Chinese locale
moment.tz("2024-01-15", "America/New_York")
Specify a timezone (needs moment-timezone)
moment().utcOffset(480)
Set a UTC offset (minutes)

Duration 5

moment.duration(100, "minutes")
Create a duration from minutes
moment.duration({ hours: 2, minutes: 30 })
Create a duration from an object
duration.asHours()
Convert to total hours
duration.humanize()
Human-friendly display (e.g. '2 hours')
duration.add(1, "hour")
Add to a duration

Parameter matrix

ParameterEffectExample
moment(input, format)Parse with an explicit format, which is far more reliable than inferencemoment("2024-01-15", "YYYY-MM-DD")
format(pattern)Render a string from a pattern, e.g. YYYY-MM-DD, HH:mm:ssmoment().format("YYYY-MM-DD")
add(n, unit) / subtract(n, unit)Shift time by a number of units: days, month, hoursmoment().add(7, "days")
startOf(unit) / endOf(unit)Snap to a unit boundary, such as start of day or end of monthmoment().endOf("month")
isSame(other, unit?)Compare at a given granularity; without a unit it compares to the millisecondmoment().isSame(other, "day")
isBetween(start, end)Test membership in a range, common for timeline filteringmoment().isBetween(start, end)
diff(other, unit)Compute the signed difference between two moments in a given unitmoment().diff(other, "days")
isValid()Verify a parse actually succeeded — check this first on unexpected outputmoment().isValid()
fromNow(true) / calendar()Human-friendly relative output from nowmoment().fromNow(true)
moment.duration(100, "minutes").asHours()Build a duration and convert it to a total in another unitduration.asHours()

Common pitfalls

SymptomA brand-new project still installs moment and bundle-size warnings keep appearing in the build output.

Causemoment is in maintenance mode and receives no new features; the project recommends lighter alternatives for new work.

FixUse dayjs (tiny, near-identical API), date-fns (per-function imports, tree-shakeable) or native Temporal in new code; keep moment only for legacy modules and replace gradually.

Symptommoment({ year: 2024, month: 0, day: 15 }) yields 2023-12, one month short.

CauseThe object literal follows the JS Date convention where months are zero-based, so month: 0 is January.

FixRemember month is zero-based (0 = January), or parse a string instead: moment("2024-01-15", "YYYY-MM-DD").

SymptomAn illegal input such as moment("2024-13-45") leaks into calculations and the page shows Invalid date.

Causemoment parses leniently and produces an invalid object, which silently poisons any operation chained onto it.

FixValidate immediately with isValid() and take a fallback branch or return null; never pass an invalid moment into add / diff / format.

SymptomTwo timestamps on the same day make isSame() return false.

CauseWithout a unit, isSame compares down to the millisecond, so any time difference counts as unequal.

FixState the granularity: moment().isSame(other, "day"); for fuzzy boundaries use isSameOrBefore / isSameOrAfter.

SymptomThe server stores UTC but the page displays times offset by several hours.

CauseLocal time and UTC are mixed: moment() resolves to the local zone while the data is UTC.

FixPick one policy: store with moment().utc() or moment().toISOString() and call moment().local() before display; for cross-zone work use moment.tz("2024-01-15", "America/New_York"), which requires the moment-timezone package.

Symptomdiff returns one day fewer than expected, most visibly around month ends.

Causediff truncates toward whole units and does not count calendar days across month boundaries.

FixDefine the business rule explicitly: align with startOf("day") before diffing, or use isBetween(start, end) with startOf/endOf to decide range membership.

Troubleshooting

  1. 1Confirm the parse succeeded before formatting

    moment().isValid()

    Run any non-standard input through isValid() first; when false, take the fallback path instead of formatting.

  2. 2Give an explicit format when parsing non-standard strings

    moment("2024-01-15", "YYYY-MM-DD")

    Inference reads 2024/01/15 and 15/01/2024 as different dates; an explicit format removes the ambiguity.

  3. 3Test same-day rather than same-instant

    moment().isSame(other, "day")

    Both isSame and isBetween need a unit to match a day-level business rule.

  4. 4Settle on one time zone policy

    moment().utc()

    Store UTC and call moment().local() before display; cross-timezone scheduling needs moment.tz() from moment-timezone.

  5. 5Establish the semantics before migrating away

    moment().diff(other, "days")

    Spell out existing behaviour with this API set first, then replace with dayjs / date-fns / Temporal so behavioural drift stays controlled.

Command Examples

Parse, format, and diff in three lines

const d = moment("2024-01-15", "YYYY-MM-DD"); const s = d.format("YYYY-MM-DD HH:mm"); const days = moment().diff(d, "days");

Always pass a format for non-ISO strings: moment("15/01/2024") parses differently under different locales, and only the explicit "DD/MM/YYYY" form is deterministic. diff is measured against moment() right now, so pass both endpoints explicitly when you need a fixed baseline.

Output

2024-01-15 00:00
2024-01-15 00:00

Boundaries and relative display: startOf plus calendar

const begin = moment().startOf('day'); // today 00:00:00.000 const end = moment().endOf('month'); // last day of the month 23:59:59.999 const label = moment().calendar();

startOf("day") and endOf("month") are the standard way to express "today" and "this month" ranges, avoiding the timezone and millisecond-edge bugs of hand-built 00:00:00 values. endOf includes .999 milliseconds, so pair it with a startOf("day") when you need a clean upper bound for database comparisons.

Output

Today at 2:30 PM / Yesterday at 9:00 AM

Prefer isSameOrBefore over bare comparison

moment("2024-01-15").isSameOrBefore("2024-02-01"); moment("2024-01-15").isBetween("2024-01-01", "2024-02-01", "day", "[]");

isSameOrBefore folds the equality case in, preventing a missed boundary where isBefore returns false. The fourth argument "[ ]" makes the range inclusive; by default it is exclusive, so the very start and end of a day are dropped — the most common off-by-one in date filtering.

Time zones: store UTC, localise only for display

const iso = moment.utc().toISOString(); const shanghai = moment(iso).tz("Asia/Shanghai").format("YYYY-MM-DD HH:mm"); const offsetMin = moment().utcOffset(480);

moment() defaults to local time, so calling toISOString() without a preceding utc() bakes in your local offset. The safe pipeline is to normalise with moment.utc() at the boundary, store the ISO string, and call .tz(...) only when rendering for a specific audience. utcOffset(480) is the minute count for UTC+8.

Output

2024-01-15T06:00:00.000Z

Durations and localisation

const d = moment.duration({ hours: 2, minutes: 30 }); const hours = d.asHours(); // 2.5 moment.locale('en'); const text = d.humanize(); // 3 hours

A moment.duration models a span of time, not an instant; mixing it up with moment objects is a reliable source of wrong diff and add results. humanize() follows the global locale, so a UI mixing Chinese and English should set the locale per surface, or move to the Intl-based ecosystem.

Common Pitfalls

  • Moment.js is in maintenance mode: the project ships no new features and recommends migration. Choose dayjs, date-fns, or native Temporal for new work; extending a legacy codebase only compounds the eventual upgrade cost.
  • In the object-literal constructor month is zero-based: moment({ year: 2024, month: 0, day: 15 }) is January. Writing month: 1 for January silently yields February with no error at all.
  • moment() objects are mutable — set, add, subtract and format mutate in place and return the same instance. Storing one in state or a cache and forgetting .clone() lets a distant code path change your date, which is exceptionally hard to trace.
  • Feeding non-ISO strings such as moment("15/01/2024") parses differently per locale and can shift the date by a full day. Always pass an explicit format string, or convert to ISO 8601 first.
  • Server-side moment().format() emits a string stamped with the server local timezone, which the client then re-parses as something else. Send UTC over the wire via toISOString() and localise only at render time.
  • When locale data is missing, moment.locale("zh-cn") silently falls back to English, so fromNow() emits "3 days ago" instead of Chinese with no warning. Read .locale() back before shipping to confirm the language actually took effect.

Tips

  • Moment.js is in maintenance mode; new projects should prefer dayjs (compatible API, smaller size).
  • Months are zero-indexed: 0 = January, 11 = December.
  • Format tokens: YYYY=year, MM=month, DD=day, HH=hour, mm=minute, ss=second.
  • Date operations return a new moment object; the original is immutable.
  • fromNow/toNow auto-pick a unit (seconds, minutes, hours, days, months, years).
  • diff returns milliseconds by default; pass a second arg for the unit (days/months/years).
  • Duration measures a length of time, not a point in time; humanize outputs friendly text.

FAQ

When do I have to pass an explicit format string to moment()?

Whenever the input is not ISO 8601 shaped. moment("2024-01-15") goes through the built-in lenient parser and handles most standard formats, but strings such as 2024/01/15 15:30 or 15/01/2024 have ambiguous separators and ordering, and browsers or moment versions can disagree about them. In those cases write moment("2024-01-15", "YYYY-MM-DD") and let the second argument pin down the expected format so parsing is deterministic. The practical rule: parse standard API formats directly, and always supply a format for user input or any non-ISO string.

If the UI needs both an absolute time and a "3 days ago" style label, how do the methods combine?

Layer them by information density. moment().calendar() produces calendar-aware phrasing — it folds today and yesterday into short forms like "Today 14:30" or "Yesterday 09:00", which is ideal in a list you scan quickly, and falls back to a full date for anything further out. moment().fromNow() gives a relative offset regardless of distance, which suits a detail view or notification feed where the point is how long ago something happened. moment().fromNow(true) strips the direction word and leaves just "3 days", for when you compose the surrounding copy yourself. Match them to the width of the display slot.

Does duration.asHours() return a fractional number or a whole number?

It returns a fractional total. A duration built as moment.duration({ hours: 2, minutes: 30 }) yields 2.5 from asHours(), not 2 — the method normalises the entire duration into that unit rather than reporting only the completed portion. moment.duration(100, "minutes") likewise comes out around 1.667. If you need a floor-style "how many whole hours" figure, apply the truncation yourself on the returned value instead of expecting asHours to round. duration.add(1, "hour") operates on the duration itself, and the same asHours call then reports the new normalised total.

Can a duration and a moment object be used together?

Yes — both belong to the moment family and compose in the same expression. The most common use is expressing "how long until this moment": the result of differencing two instants is itself a duration, so you can pipe it straight into asHours() for a numeric figure or humanize() for readable text such as "2 hours". Going the other way, add a stretch of time to an instant with operations on a moment object like moment().add(7, "days"), while duration.add(1, "hour") does arithmetic purely among durations. The rule of thumb: describe a point in time with moment, describe a length of time with duration.

Should I use moment().format("LLL") or an explicit YYYY-MM-DD HH:mm pattern?

Decide between sortable and readable. An explicit token string — this sheet lists YYYY-MM-DD, HH:mm:ss, dddd, MMMM Do YYYY and others — produces fixed output that does not change with language, which makes it right for filenames, log lines, sort keys and anywhere a stable comparison matters. format("LLL") is a localised long form that follows moment.locale("zh-cn"), so the same code yields each locale own long date rendering — the better choice for user-facing screens. A workable split: explicit tokens for search and ordering, LLL wherever a human reads the result.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us