Skip to content

Getting Started

moment-less is a zero-dependency TypeScript library that brings Moment.js-style token formatting to the native TC39 Temporal API. If you already know YYYY-MM-DD format strings, you already know moment-less.

Installation

sh
npm install moment-less
sh
pnpm add moment-less
sh
yarn add moment-less

Temporal availability: Node 22+, Chrome 127+, Firefox 139+, Safari 18.2+, and Deno 2.1+ all ship Temporal natively. For older environments, see Browser & Runtime Support.

Your First Format

ts
import { format } from 'moment-less'

const date = Temporal.PlainDate.from('2026-04-09')
console.log(format(date, 'MMMM Do, YYYY'))
// → "April 9th, 2026"

That's it. No configuration, no locale setup, no timezone wrangling.

All Five Functions

format(temporalObj, formatString, options?)

Token-based formatting for any Temporal type.

ts
import { format } from 'moment-less'

const dt = Temporal.PlainDateTime.from('2026-04-09T14:05:30')
format(dt, 'YYYY-MM-DD')           // → "2026-04-09"
format(dt, 'h:mm A')               // → "2:05 PM"
format(dt, 'dddd, MMMM Do, YYYY')  // → "Thursday, April 9th, 2026"
format(dt, 'ddd MMM D YYYY')       // → "Thu Apr 9 2026"

// Escape literal text with square brackets
format(dt, '[Today is] dddd')      // → "Today is Thursday"

fromNow(temporalObj, reference?, locale?)

Human-readable relative time string.

The optional reference is a Temporal.Instant. Pass one for deterministic output (great for tests); omit it to measure against the real current time.

ts
import { fromNow } from 'moment-less'

const now = Temporal.Instant.from('2026-04-09T14:00:00Z')
const posted = now.subtract({ hours: 3 })

fromNow(posted, now)                // → "3 hours ago"
fromNow(now.add({ days: 2 }), now)  // → "in 2 days"

// Omit the reference to measure against Temporal.Now
fromNow(Temporal.Now.instant().subtract({ minutes: 5 }))  // → "5 minutes ago"

// Locale support
fromNow(posted, now, 'fr')  // → "il y a 3 heures"
fromNow(posted, now, 'es')  // → "hace 3 horas"

calendar(temporalObj, reference?, options?)

Context-aware calendar label, like chat apps and file managers use.

The optional reference is a Temporal.PlainDate. A PlainDate input yields a date-only label; a datetime input appends the time.

ts
import { calendar } from 'moment-less'

const today = Temporal.PlainDate.from('2026-04-09') // a Thursday

calendar(Temporal.PlainDateTime.from('2026-04-09T14:05'), today) // → "Today at 2:05 PM"
calendar(Temporal.PlainDateTime.from('2026-04-08T09:30'), today) // → "Yesterday at 9:30 AM"
calendar(Temporal.PlainDate.from('2026-04-06'), today)           // → "Monday"
calendar(Temporal.PlainDate.from('2025-11-15'), today)           // → "Nov 15, 2025"

humanizeDuration(duration, locale?)

Converts a Temporal.Duration to a human-readable string, picking the most significant unit.

ts
import { humanizeDuration } from 'moment-less'

humanizeDuration(Temporal.Duration.from({ minutes: 45 }))            // → "45 minutes"
humanizeDuration(Temporal.Duration.from({ hours: 2, minutes: 30 })) // → "3 hours"
humanizeDuration(Temporal.Duration.from({ days: 3 }))               // → "3 days"
humanizeDuration(Temporal.Duration.from({ years: 1, months: 2 }))   // → "1 year"

// Localized
humanizeDuration(Temporal.Duration.from({ hours: 2 }), 'fr') // → "2 heures"
humanizeDuration(Temporal.Duration.from({ hours: 2 }), 'es') // → "2 horas"

fromDate(date)

Bridges a legacy JavaScript Date object to a Temporal.Instant.

ts
import { format, fromDate } from 'moment-less'

const inst = fromDate(new Date('2026-04-09T14:05:00Z'))
format(inst, 'YYYY-MM-DD HH:mm')  // → "2026-04-09 14:05"

// Works with Date.now() too
const now = fromDate(new Date(Date.now()))

Token Quick Reference

TokenOutput exampleDescription
YYYY20264-digit year
YY262-digit year
MMMMAprilFull month name
MMMAprShort month name
MM04Month, zero-padded
M4Month, no padding
Do9thDay of month with ordinal suffix
DD09Day, zero-padded
D9Day, no padding
ddddThursdayFull weekday name
dddThuShort weekday name
HH1424-hour hour, zero-padded
H1424-hour hour, no padding
hh0212-hour hour, zero-padded
h212-hour hour, no padding
mm05Minutes, zero-padded
ss30Seconds, zero-padded
SSS042Milliseconds, zero-padded
APMAM/PM uppercase
apmam/pm lowercase
Z+05:30UTC offset, with colon (ZonedDateTime/Instant)
ZZ+0530UTC offset, no colon (ZonedDateTime/Instant)
X1775723730Unix timestamp, seconds (ZonedDateTime/Instant)
x1775723730000Unix timestamp, ms (ZonedDateTime/Instant)
[…]literal textEscaped literal characters

See the full format() API reference for details on which tokens are available for each Temporal type.

Next Steps

Released under the MIT License.