Day.js is a minimalist JavaScript library for parsing, validating, manipulating, and displaying dates and times. It ships a largely Moment.js-compatible API in a ~2ย KB immutable, chainable package, with optional plugins and 140+ locales loaded only when you need them.
If you already know Moment.js, you already know how to use Day.js.
dayjs().startOf('month').add(1, 'day').set('year', 2018).format('YYYY-MM-DD HH:mm:ss')
This guide covers everything you can do with Day.js from application code: installing it, creating date objects, reading and changing their parts, formatting and comparing them, enabling each of the 37 official plugins, and switching locales. Architecture, the plugin authoring model, build internals, and security considerations live in the companion Admin Guide.
Table of Contents
- Quick Start
- Why Day.js
- Installation
- Core Concepts
- Parsing โ Creating Day.js Objects
- Get and Set
- Manipulating Dates
- Displaying and Formatting
- Format Token Reference
- Querying and Comparing
- Plugins
- Internationalization (i18n)
- Common Tasks and Recipes
- Troubleshooting
- FAQ
- Quick Reference
1. Quick Start
Install the library and create your first date object.
npm install dayjs
import dayjs from 'dayjs'
// The current date and time
dayjs().format('YYYY-MM-DD HH:mm:ss')
'2019-03-06 15:25:00'
// Parse a date, manipulate it, and display it โ all chainable
dayjs('2018-08-08') // parse
.add(1, 'day') // manipulate (returns a NEW object)
.format('dddd, MMMM D, YYYY')
'Thursday, August 9, 2018'
Note
Calling
dayjs()with no arguments returns an object for the current date and time. Every Day.js object wraps a native JavaScriptDateinternally but never exposes mutable state.
Tip
Day.js objects are immutable. Methods like
add,subtract,set, andstartOfreturn a new object and leave the original untouched. Always capture the return value:const tomorrow = today.add(1, 'day').
2. Why Day.js
- ๐ Familiar API โ methods and patterns mirror Moment.js, so migration is largely a drop-in replacement.
- ๐ช Immutable โ every operation returns a new instance; you cannot accidentally mutate a shared date.
- ๐ฅ Chainable โ most methods return a Day.js object, so calls compose left to right.
- ๐ I18n support โ 140+ locales, each loaded on demand.
- ๐ฆ Tiny โ the core is roughly 2ย KB minified and gzipped; plugins and locales are opt-in.
- ๐ซ Broad browser support โ built on the native
Dateobject.
The core library deliberately ships only the most common functionality. Everything else โ relative time, UTC, time zones, durations, custom parsing, ISO weeks, and more โ is delivered as a plugin you extend at runtime, so your bundle only carries what you use.
3. Installation
npm
npm install dayjs --save
Yarn
yarn add dayjs
pnpm
pnpm add dayjs
CDN
<script src="https://unpkg.com/dayjs@1/dayjs.min.js"></script>
<script>
dayjs().format()
</script>
Importing
ES Modules / bundlers
import dayjs from 'dayjs'
dayjs().format()
CommonJS (Node.js)
const dayjs = require('dayjs')
dayjs().format()
Browser global
When loaded from a <script> tag, the library is exposed as the global dayjs.
<script src="https://unpkg.com/dayjs@1/dayjs.min.js"></script>
<script>
console.log(dayjs().format())
</script>
The package's package.json declares "main": "dayjs.min.js" and "types": "index.d.ts", so both the runtime entry point and the bundled TypeScript declarations resolve automatically.
Important
TypeScript declarations are bundled with the package โ there is no separate
@types/dayjsto install. Type definitions for plugins live underdayjs/plugin/<name>and augment the coreDayjsinterface automatically when you import them.
Verifying the install
import dayjs from 'dayjs'
console.log(dayjs.isDayjs(dayjs())) // true
console.log(typeof dayjs().valueOf()) // 'number'
true
number
4. Core Concepts
Immutability
A Day.js object never changes after it is created. Manipulation methods build and return a fresh object:
const original = dayjs('2019-01-25')
const changed = original.add(1, 'day')
original.format('YYYY-MM-DD') // '2019-01-25' โ unchanged
changed.format('YYYY-MM-DD') // '2019-01-26'
2019-01-25
2019-01-26
Warning
Because objects are immutable, a statement like
date.add(1, 'day')on its own does nothing observable โ the new object is discarded. You must use the return value.
If you genuinely need mutable behavior (for example when porting Moment.js code that relies on in-place mutation), the optional badMutable plugin restores it. It is named "bad" on purpose: mutation is discouraged.
Chaining
Methods that return a Day.js object can be chained:
dayjs()
.set('hour', 9)
.set('minute', 0)
.add(7, 'day')
.startOf('day')
.format()
Cloning
Passing a Day.js object to the dayjs() factory, or calling .clone(), produces an independent copy:
const a = dayjs('2019-01-25')
const b = a.clone() // explicit clone
const c = dayjs(a) // cloning by passing to the constructor
Note
You rarely need to clone manually. Since manipulation already returns new objects, cloning matters mainly when interoperating with the mutable
badMutableplugin or when you want a defensive copy.
Validity
Every object reports whether it holds a real date:
dayjs('not a date').isValid() // false
dayjs('2019-01-25').isValid() // true
An invalid object formats to the string Invalid Date (unless the active locale overrides invalidDate), and its toJSON() returns null.
5. Parsing โ Creating Day.js Objects
The dayjs() factory accepts several input types. The core library handles the most common cases; less common formats require a plugin.
No argument โ now
dayjs() // current date and time
ISO 8601 string
The core parser recognizes ISO 8601 date and date-time strings:
dayjs('2018-04-04T16:00:00.000Z')
dayjs('2018-04-04')
dayjs('2018-04') // defaults to the 1st of the month
dayjs('2018-04-13 19:18') // space separator is accepted
Internally the core matches strings against this pattern (year, optional month, optional day, optional time components):
YYYY[-/]MM[-/]DD[T or space]HH:mm:ss[.sss]
Important
The core parser only understands ISO 8601-like strings. To parse an arbitrary custom format such as
'31-12-2024'or'12/31/2024', enable thecustomParseFormatplugin. Passing a non-ISO string to the core parser falls through to the nativenew Date(string), whose behavior is implementation-dependent and may produceInvalid Date.
Native Date object
dayjs(new Date(2018, 8, 18)) // months are zero-indexed: month 8 = September
Unix timestamp (milliseconds)
dayjs(1318781876406) // a number is treated as milliseconds since the epoch
Unix timestamp (seconds)
Use the static dayjs.unix() helper for second-precision timestamps:
dayjs.unix(1318781876) // seconds โ Day.js object
dayjs.unix(1318781876.721) // fractional seconds are supported
Cloning an existing object
dayjs(dayjs('2019-01-25')) // clones the passed object
Null and undefined
dayjs(null) // Invalid Date โ null is explicitly rejected
dayjs(undefined) // current date and time (same as dayjs())
Caution
dayjs(null)is invalid by design, whiledayjs(undefined)returns the current time. Guard against accidentally passingnullwhere you expected a date.
Parsing table
| Input | Result |
|---|---|
dayjs() |
Current date and time |
dayjs('2018-04-04') |
Parsed from ISO 8601 string |
dayjs('2018-04') |
First day of the month |
dayjs(new Date()) |
From a native Date |
dayjs(1318781876406) |
From milliseconds since epoch |
dayjs.unix(1318781876) |
From seconds since epoch |
dayjs(otherDayjs) |
Clone of an existing object |
dayjs(null) |
Invalid Date |
dayjs(undefined) |
Current date and time |
Tip
Plugins extend parsing:
customParseFormatfor custom string formats,arraySupportfordayjs([2010, 1, 14]),objectSupportfordayjs({ year: 2010, month: 1 }),utcfor UTC-mode parsing, andbigIntSupportfor BigInt timestamps.
6. Get and Set
Getters
Call a unit method with no argument to read it:
const d = dayjs('2019-01-25 12:34:56.789')
d.year() // 2019
d.month() // 0 (January โ months are zero-indexed)
d.date() // 25 (day of the month, 1โ31)
d.day() // 5 (day of the week, 0=Sunday โฆ 6=Saturday)
d.hour() // 12
d.minute() // 34
d.second() // 56
d.millisecond() // 789
Warning
month()is zero-indexed (January = 0, December = 11), matching the nativeDateAPI.date()is the day of the month (1โ31);day()is the day of the week (0โ6). These three are easy to confuse.
Setters
Pass a value to return a new object with that unit changed:
dayjs().year(2020)
dayjs().month(3) // April
dayjs().date(1)
dayjs().hour(12)
dayjs().minute(0)
dayjs().second(30)
dayjs().millisecond(0)
Setters bubble over boundaries. Setting a date beyond the month length rolls into the next month; setting an hour past 23 rolls into the next day:
dayjs('2019-01-25').date(33).format('YYYY-MM-DD') // '2019-02-02'
Generic get and set
get(unit) and set(unit, value) are equivalent to the named methods and accept long, short, and plural unit names (case-insensitive):
dayjs().get('year') // same as dayjs().year()
dayjs().set('date', 1) // same as dayjs().date(1)
dayjs().set('month', 3) // April
dayjs().set('second', 30)
// These are all equivalent:
dayjs().set('day', 0)
dayjs().set('d', 0)
Recognized units
| Unit | Short | Plural (with pluralGetSet) |
Notes |
|---|---|---|---|
year |
y |
years |
|
month |
M |
months |
Zero-indexed (0โ11) |
date |
D |
dates |
Day of month (1โ31) |
day |
d |
days |
Day of week (0โ6) |
hour |
h |
hours |
|
minute |
m |
minutes |
|
second |
s |
seconds |
|
millisecond |
ms |
milliseconds |
Note
The plural getter/setter aliases (
years(),months(), โฆ) require thepluralGetSetplugin. The singular and short forms are always available.
Days in month
dayjs('2019-01-25').daysInMonth() // 31
dayjs('2020-02-01').daysInMonth() // 29 (leap year)
7. Manipulating Dates
All manipulation methods return a new Day.js object.
Add
dayjs().add(7, 'day')
dayjs().add(1, 'month')
dayjs().add(3, 'year')
dayjs().add(30, 'minute')
Units accept long, short, and (with pluralGetSet) plural forms:
dayjs().add(1, 'd') // same as 'day'
dayjs().add(1, 'M') // month (capital M)
dayjs().add(1, 'm') // minute (lowercase m)
Caution
Token case matters for
add/subtractunits passed as short forms:'M'is month and'm'is minute, mirroring the format tokens. When in doubt, spell out the unit ('month','minute').
Subtract
dayjs().subtract(7, 'year')
dayjs().subtract(1, 'month')
subtract(n, unit) is exactly add(-n, unit).
Start of / End of
Snap to the beginning or end of a unit:
dayjs().startOf('year') // Jan 1, 00:00:00.000
dayjs().startOf('month') // 1st of month, 00:00:00.000
dayjs().startOf('week') // first day of the week (locale-aware)
dayjs().startOf('day') // 00:00:00.000
dayjs().endOf('month') // last day of month, 23:59:59.999
dayjs().endOf('day') // 23:59:59.999
Note
startOf('week')respects the active locale'sweekStart(e.g. Sunday foren, Monday for many European locales). For ISO weeks (always Monday), use theisoWeekplugin andstartOf('isoWeek').
Chaining manipulations
dayjs('2019-01-25')
.add(1, 'day')
.subtract(1, 'year')
.startOf('month')
.format('YYYY-MM-DD') // '2018-01-01'
Tip
Adding months or years clamps the day of month so it never overflows.
dayjs('2019-01-31').add(1, 'month')yields the last day of February, not March 3.
8. Displaying and Formatting
format()
With no template, format() returns an ISO 8601 string using the default template YYYY-MM-DDTHH:mm:ssZ:
dayjs('2019-01-25').format()
'2019-01-25T00:00:00+02:00'
Pass a template string of tokens:
dayjs('2019-01-25').format('YYYY-MM-DD') // '2019-01-25'
dayjs('2019-01-25').format('DD/MM/YYYY') // '25/01/2019'
dayjs('2019-01-25').format('dddd, MMMM D, YYYY') // 'Friday, January 25, 2019'
dayjs('2019-01-25 14:30').format('h:mm A') // '2:30 PM'
Escaping literal text
Wrap literal characters in square brackets so they are not interpreted as tokens:
dayjs('2019-01-25').format('[Year] YYYY') // 'Year 2019'
dayjs('2019-01-25').format('[Today is] dddd')
diff() โ difference between two dates
const a = dayjs('2019-01-25')
const b = dayjs('2018-06-05')
a.diff(b) // 20214000000 (milliseconds, default)
a.diff(b, 'month') // 7
a.diff(b, 'day') // 234
a.diff(b, 'year') // 0
By default diff truncates toward zero (returns a whole number). Pass true as the third argument for a floating-point result:
a.diff(b, 'month', true) // 7.645... (fractional months)
Native conversions
dayjs('2019-01-25').valueOf() // 1548381600000 โ milliseconds since epoch
dayjs('2019-01-25').unix() // 1548381600 โ seconds since epoch (floored)
dayjs('2019-01-25').toDate() // native Date object
dayjs('2019-01-25').toJSON() // '2019-01-25T02:00:00.000Z' (ISO, or null if invalid)
dayjs('2019-01-25').toISOString() // '2019-01-25T02:00:00.000Z'
dayjs('2019-01-25').toString() // 'Fri, 25 Jan 2019 02:00:00 GMT'
Tip
valueOf()returns milliseconds;unix()returns seconds. Useunix()when talking to APIs that expect Unix epoch seconds. BecausevalueOf()exists, you can also coerce with the unary plus operator:+dayjs().
UTC offset
dayjs().utcOffset() // offset in minutes, e.g. -300 for UTC-5
Note
Relative time strings ("4 years ago", "in a minute"), calendar phrasing ("Today at 2:30 PM"), and named-token formats (
LLLL,Do,Q,X) require plugins โ seerelativeTime,calendar,localizedFormat, andadvancedFormat.
9. Format Token Reference
These tokens are recognized by the core format() method. Output for name-based tokens (MMMM, dddd, A, โฆ) depends on the active locale.
| Token | Example | Description |
|---|---|---|
YY |
18 |
Two-digit year |
YYYY |
2018 |
Four-digit year |
M |
1โ12 |
Month, 1-indexed, no leading zero |
MM |
01โ12 |
Month, 1-indexed, padded |
MMM |
Jan |
Abbreviated month name |
MMMM |
January |
Full month name |
D |
1โ31 |
Day of month, no leading zero |
DD |
01โ31 |
Day of month, padded |
d |
0โ6 |
Day of week (0 = Sunday) |
dd |
Su |
Minimal weekday name |
ddd |
Sun |
Short weekday name |
dddd |
Sunday |
Full weekday name |
H |
0โ23 |
Hour, 24-hour, no leading zero |
HH |
00โ23 |
Hour, 24-hour, padded |
h |
1โ12 |
Hour, 12-hour, no leading zero |
hh |
01โ12 |
Hour, 12-hour, padded |
m |
0โ59 |
Minute, no leading zero |
mm |
00โ59 |
Minute, padded |
s |
0โ59 |
Second, no leading zero |
ss |
00โ59 |
Second, padded |
SSS |
000โ999 |
Millisecond, 3 digits |
A |
AM / PM |
Meridiem, uppercase |
a |
am / pm |
Meridiem, lowercase |
Z |
+05:00 |
UTC offset, with colon |
ZZ |
+0500 |
UTC offset, no colon |
Important
Additional tokens require plugins:
advancedFormataddsQ,Do,gggg,GGGG,wo,w/ww,W/WW,k/kk,X,x,z,zzz.localizedFormataddsLT,LTS,L,LL,LLL,LLLL(and lowercasel,ll,lll,llll).
Localized format tokens (localizedFormat plugin)
| Token | English output |
|---|---|
LT |
8:02 PM |
LTS |
8:02:18 PM |
L |
08/16/2018 |
LL |
August 16, 2018 |
LLL |
August 16, 2018 8:02 PM |
LLLL |
Thursday, August 16, 2018 8:02 PM |
Advanced format tokens (advancedFormat plugin)
| Token | Description |
|---|---|
Q |
Quarter, 1โ4 |
Do |
Day of month with ordinal (e.g. 1st) |
w / ww |
Week of year (needs weekOfYear) |
W / WW |
ISO week of year (needs isoWeek) |
wo |
Week of year with ordinal |
gggg |
Week year (needs weekYear) |
GGGG |
ISO week year (needs isoWeek) |
k / kk |
Hour in 1โ24 range |
X |
Unix timestamp in seconds |
x |
Unix timestamp in milliseconds |
z / zzz |
Time zone name (needs timezone) |
10. Querying and Comparing
The core provides three comparison methods plus validity checking. Several more comparisons are available via plugins.
Core comparisons
const d = dayjs('2019-01-25')
d.isBefore(dayjs('2019-02-01')) // true
d.isAfter(dayjs('2019-01-01')) // true
d.isSame(dayjs('2019-01-25')) // true
d.isValid() // true
Granular comparison with units
Pass a unit as the second argument to compare only down to that granularity:
dayjs('2019-01-25').isSame('2019-01-30', 'month') // true (same month)
dayjs('2019-01-25').isBefore('2019-01-26', 'year') // false (same year)
dayjs('2019-01-25').isAfter('2018-12-31', 'year') // true
Note
Without a unit, comparisons are exact to the millisecond. With a unit, Day.js compares against the
startOf/endOfthat unit, soisSame(other, 'month')means "in the same calendar month."
Comparison plugins
| Method | Plugin | Purpose |
|---|---|---|
isSameOrBefore |
isSameOrBefore |
<= comparison |
isSameOrAfter |
isSameOrAfter |
>= comparison |
isBetween |
isBetween |
Range check with inclusivity control |
isToday / isTomorrow / isYesterday |
isToday etc. |
Relative-day checks |
isLeapYear |
isLeapYear |
Leap-year check |
isDayjs (static) |
core | Type check |
dayjs.isDayjs(dayjs()) // true
dayjs.isDayjs(new Date()) // false
11. Plugins
Plugins extend Day.js with extra functionality. The pattern is always the same: import the core, import the plugin, extend, then use the new methods.
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
dayjs.extend(relativeTime)
dayjs('1999-01-01').fromNow() // now available
Important
dayjs.extend(plugin)installs a plugin once per process โ calling it again with the same plugin is a no-op. Callextendduring app startup, before you use the plugin's methods. Some plugins accept an option object as a second argument:dayjs.extend(plugin, options).
Tip
Only extend the plugins you actually use. Each plugin you skip is code that never enters your bundle โ this is the whole reason the core stays around 2ย KB.
The 37 official plugins follow.
advancedFormat
Adds extra format tokens to format(): Q, Do, gggg, GGGG, wo, w, ww, W, WW, k, kk, X, x, z, zzz.
import advancedFormat from 'dayjs/plugin/advancedFormat'
dayjs.extend(advancedFormat)
dayjs('2019-01-25').format('Q Do k X') // e.g. '1 25th 24 1548381600'
Note
Some advanced tokens depend on other plugins:
w/woneedweekOfYear,W/GGGGneedisoWeek,ggggneedsweekYear, andz/zzzneedtimezone.
arraySupport
Lets the dayjs() factory accept an array [year, month, day, hour, minute, second, millisecond] (month is zero-indexed).
import arraySupport from 'dayjs/plugin/arraySupport'
dayjs.extend(arraySupport)
dayjs([2010, 1, 14, 15, 25, 50, 125]) // Feb 14 2010, 15:25:50.125
dayjs([2010]) // a single element is parsed as a year string
badMutable
Makes Day.js objects mutable โ manipulation methods modify the original in place instead of returning a new object. Restores Moment.js-style mutation for migration.
import badMutable from 'dayjs/plugin/badMutable'
dayjs.extend(badMutable)
const d = dayjs('2019-01-25')
d.add(1, 'day') // d itself is now mutated
d.format('YYYY-MM-DD') // '2019-01-26'
Caution
The name is a warning. Mutability defeats the safety guarantee that makes Day.js predictable. Use this only to ease a migration, and remove it once your code no longer relies on mutation.
bigIntSupport
Allows BigInt values as timestamps, both in the factory and in dayjs.unix().
import bigIntSupport from 'dayjs/plugin/bigIntSupport'
dayjs.extend(bigIntSupport)
dayjs(1318781876406n)
dayjs.unix(1318781876n)
buddhistEra
Adds the BBBB and BB format tokens for the Buddhist Era (year + 543).
import buddhistEra from 'dayjs/plugin/buddhistEra'
dayjs.extend(buddhistEra)
dayjs('2019-01-25').format('BBBB-MM-DD') // '2562-01-25'
dayjs('2019-01-25').format('BB') // '62'
calendar
Adds calendar() for human-friendly relative phrasing within about a week of a reference time.
import calendar from 'dayjs/plugin/calendar'
dayjs.extend(calendar)
dayjs().calendar() // 'Today at 2:30 PM'
dayjs().add(1, 'day').calendar() // 'Tomorrow at 2:30 PM'
dayjs().subtract(1, 'day').calendar()
You can pass a reference time and custom format strings:
dayjs().calendar(dayjs('2008-01-01'), {
sameDay: '[Today at] h:mm A',
nextDay: '[Tomorrow at] h:mm A',
nextWeek: 'dddd [at] h:mm A',
lastDay: '[Yesterday at] h:mm A',
lastWeek: '[Last] dddd [at] h:mm A',
sameElse: 'MM/DD/YYYY'
})
The default format keys are sameDay, nextDay, nextWeek, lastDay, lastWeek, and sameElse.
customParseFormat
Parses date strings that match a supplied format. This is the plugin you need for any non-ISO input.
import customParseFormat from 'dayjs/plugin/customParseFormat'
dayjs.extend(customParseFormat)
dayjs('05/02/69 1:02:03 PM -05:00', 'MM/DD/YY H:mm:ss A Z')
dayjs('2018 5ๆ', 'YYYY Mๆ')
dayjs('31-12-2024', 'DD-MM-YYYY')
Strict mode (third argument true) rejects input that doesn't match the format exactly:
dayjs('1970-00-00', 'YYYY-MM-DD').isValid() // true (lenient)
dayjs('1970-00-00', 'YYYY-MM-DD', true).isValid() // false (strict)
Pass a locale, or an array of formats to try in order:
dayjs('2018 ไธๆ', 'YYYY MMMM', 'zh-cn')
dayjs('2012-05-28', ['YYYY-MM-DD', 'YYYY/MM/DD'])
Supported parsing tokens include YY, YYYY, M, MM, MMM, MMMM, D, DD, Do, H/HH, h/hh, m/mm, s/ss, S/SS/SSS, Z/ZZ, A/a, Q, w/ww, and X/x. A parseTwoDigitYear option customizes how two-digit years are interpreted.
Warning
The format tokens here are for parsing input, not formatting output. To both parse a custom format and re-display it, combine
customParseFormatwithformat().
dayOfYear
Adds dayOfYear() to get or set the day of the year (1โ366).
import dayOfYear from 'dayjs/plugin/dayOfYear'
dayjs.extend(dayOfYear)
dayjs('2010-01-01').dayOfYear() // 1
dayjs('2010-12-31').dayOfYear() // 365
dayjs('2010-01-01').dayOfYear(200) // sets to the 200th day
devHelper
A development-only plugin that prints console.warn hints for common mistakes โ passing a millisecond timestamp as a string, passing a 4-digit year as a number, using a custom parse format without customParseFormat, or calling diff() with an invalid date.
import devHelper from 'dayjs/plugin/devHelper'
dayjs.extend(devHelper)
Note
The warnings are suppressed when
process.env.NODE_ENV === 'production', so this plugin is safe to leave in code that gets a production build, but it is most useful during development.
duration
Adds a Duration object for representing lengths of time (independent of any specific date), plus dayjs.duration() and dayjs.isDuration().
import duration from 'dayjs/plugin/duration'
dayjs.extend(duration)
dayjs.duration(100) // 100 milliseconds
dayjs.duration(2, 'days')
dayjs.duration({ days: 2, hours: 12 })
dayjs.duration('P1Y2M3DT4H5M6S') // ISO 8601 duration string
const d = dayjs.duration(2, 'days')
d.asHours() // 48
d.days() // 2
d.toISOString() // 'P2D'
d.format('D [days]')
With relativeTime also extended, durations can be humanized, and durations can be added to or subtracted from a date:
dayjs.duration(1, 'minutes').humanize() // 'a minute' (needs relativeTime)
dayjs('2019-01-01').add(dayjs.duration(1, 'month'))
Available accessors: milliseconds/asMilliseconds, seconds/asSeconds, minutes/asMinutes, hours/asHours, days/asDays, weeks/asWeeks, months/asMonths, years/asYears, plus get(unit), as(unit), add, subtract, clone, locale, format, toJSON, and toISOString.
isBetween
Adds isBetween() to test whether a date falls within a range, with inclusivity control.
import isBetween from 'dayjs/plugin/isBetween'
dayjs.extend(isBetween)
dayjs('2010-10-20').isBetween('2010-10-19', '2010-10-25') // true
dayjs('2010-10-20').isBetween('2010-10-19', '2010-10-25', 'year') // false
dayjs('2016-10-30').isBetween('2016-01-01', '2016-10-30', null, '[]') // inclusive
The fourth argument controls inclusivity: '()' (default, both exclusive), '[]' (both inclusive), '[)', or '(]'.
isLeapYear
Adds isLeapYear().
import isLeapYear from 'dayjs/plugin/isLeapYear'
dayjs.extend(isLeapYear)
dayjs('2000-01-01').isLeapYear() // true
dayjs('2019-01-01').isLeapYear() // false
isMoment
Adds the static dayjs.isMoment() (an alias of dayjs.isDayjs) to ease migration of code that called moment.isMoment().
import isMoment from 'dayjs/plugin/isMoment'
dayjs.extend(isMoment)
dayjs.isMoment(dayjs()) // true
isoWeek
Adds ISO 8601 week support: isoWeek(), isoWeekday(), isoWeekYear(), and startOf('isoWeek')/endOf('isoWeek'). ISO weeks always start on Monday.
import isoWeek from 'dayjs/plugin/isoWeek'
dayjs.extend(isoWeek)
dayjs('2019-01-25').isoWeek() // ISO week number
dayjs('2019-01-25').isoWeekday() // 1 (Mon) โ 7 (Sun)
dayjs('2019-01-25').startOf('isoWeek')
isoWeeksInYear
Adds isoWeeksInYear(), returning 52 or 53.
import isoWeeksInYear from 'dayjs/plugin/isoWeeksInYear'
import isLeapYear from 'dayjs/plugin/isLeapYear'
dayjs.extend(isLeapYear) // required dependency
dayjs.extend(isoWeeksInYear)
dayjs('2004-01-01').isoWeeksInYear() // 53
Note
isoWeeksInYearrelies onisLeapYear; extend both.
isSameOrAfter
Adds isSameOrAfter() (a >= comparison).
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'
dayjs.extend(isSameOrAfter)
dayjs('2010-10-20').isSameOrAfter('2010-10-19', 'year') // true
isSameOrBefore
Adds isSameOrBefore() (a <= comparison).
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
dayjs.extend(isSameOrBefore)
dayjs('2010-10-20').isSameOrBefore('2010-10-21') // true
isToday, isTomorrow, isYesterday
Three small plugins that each add one boolean method, comparing the calendar day against the current day.
import isToday from 'dayjs/plugin/isToday'
import isTomorrow from 'dayjs/plugin/isTomorrow'
import isYesterday from 'dayjs/plugin/isYesterday'
dayjs.extend(isToday)
dayjs.extend(isTomorrow)
dayjs.extend(isYesterday)
dayjs().isToday() // true
dayjs().add(1, 'day').isTomorrow() // true
dayjs().subtract(1, 'day').isYesterday() // true
localeData
Exposes locale data (month names, weekday names, meridiem, ordinal, first day of week, long date formats) through instance and static localeData() accessors.
import localeData from 'dayjs/plugin/localeData'
dayjs.extend(localeData)
dayjs.months() // ['January', 'February', โฆ]
dayjs.weekdaysShort() // ['Sun', 'Mon', โฆ]
dayjs().localeData().firstDayOfWeek()
localizedFormat
Adds locale-aware format tokens LT, LTS, L, LL, LLL, LLLL (and lowercase l, ll, lll, llll).
import localizedFormat from 'dayjs/plugin/localizedFormat'
dayjs.extend(localizedFormat)
dayjs('2019-01-25 22:00').format('LLLL') // 'Friday, January 25, 2019 10:00 PM'
dayjs('2019-01-25').format('L LT')
minMax
Adds static dayjs.max() and dayjs.min() to find the latest/earliest of several dates.
import minMax from 'dayjs/plugin/minMax'
dayjs.extend(minMax)
dayjs.max(dayjs(), dayjs('2018-01-01'), dayjs('2019-01-01'))
dayjs.min([dayjs(), dayjs('2018-01-01'), dayjs('2019-01-01')]) // array form
Both accept either a list of arguments or a single array.
negativeYear
Handles parsing of negative-year strings (years BCE), e.g. '-2020-01-01'.
import negativeYear from 'dayjs/plugin/negativeYear'
dayjs.extend(negativeYear)
dayjs('-1000-01-01').year() // -1000
objectSupport
Lets the factory, set, add, and subtract accept plain objects of units.
import objectSupport from 'dayjs/plugin/objectSupport'
dayjs.extend(objectSupport)
dayjs({ year: 2010, month: 1, day: 12 }) // month is zero-indexed
dayjs().set({ hour: 9, minute: 0 })
dayjs().add({ days: 7, months: 1 })
pluralGetSet
Adds plural-form getter/setter aliases: years(), months(), dates(), weeks(), days(), hours(), minutes(), seconds(), milliseconds(), isoWeeks(), quarters().
import pluralGetSet from 'dayjs/plugin/pluralGetSet'
dayjs.extend(pluralGetSet)
dayjs().years() // same as dayjs().year()
dayjs().minutes(30) // same as dayjs().minute(30)
preParsePostFormat
A hook plugin that runs the active locale's preparse function on input strings before parsing and its postformat function on output strings after formatting โ used mainly for locales with non-Latin numerals (e.g. Arabic-Indic digits).
import preParsePostFormat from 'dayjs/plugin/preParsePostFormat'
dayjs.extend(preParsePostFormat)
quarterOfYear
Adds quarter() to get or set the quarter, plus 'quarter' support in add, subtract, startOf, and endOf.
import quarterOfYear from 'dayjs/plugin/quarterOfYear'
dayjs.extend(quarterOfYear)
dayjs('2010-04-01').quarter() // 2
dayjs('2010-04-01').add(1, 'quarter')
dayjs('2010-05-01').startOf('quarter') // April 1
relativeTime
Adds human-friendly relative phrasing: fromNow(), from(), toNow(), to().
import relativeTime from 'dayjs/plugin/relativeTime'
dayjs.extend(relativeTime)
dayjs('1999-01-01').fromNow() // 'over a year ago' style phrasing
dayjs('2000-01-01').from(dayjs('2000-01-02')) // 'a day ago'
dayjs().toNow()
Pass true to omit the suffix ("4 years" instead of "4 years ago"):
dayjs('1999-01-01').fromNow(true) // omits 'ago'
You can customize rounding and thresholds when extending:
dayjs.extend(relativeTime, {
thresholds: [
{ l: 's', r: 1 },
{ l: 'm', r: 1 },
{ l: 'mm', r: 59, d: 'minute' }
// โฆ
],
rounding: Math.floor
})
Default phrasings include a few seconds, a minute, %d minutes, an hour, %d hours, a day, %d days, a month, %d months, a year, %d years, with in %s / %s ago for future/past.
timezone
Adds IANA time-zone support on top of the utc plugin (which it requires): .tz(), .offsetName(), dayjs.tz(), dayjs.tz.guess(), and dayjs.tz.setDefault().
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'
dayjs.extend(utc)
dayjs.extend(timezone) // depends on utc
dayjs('2014-06-01 12:00').tz('America/New_York')
dayjs.tz('2014-06-01 12:00', 'America/New_York')
dayjs.tz.guess() // the user's IANA zone
dayjs.tz.setDefault('America/New_York')
dayjs().tz().offsetName('long') // e.g. 'Eastern Daylight Time'
Important
timezonerequires theutcplugin and uses the runtime's built-inIntl.DateTimeFormat. Zone lookups are cached internally for performance. Extendutcbeforetimezone.
toArray
Adds toArray() returning [year, month, date, hour, minute, second, millisecond] (month zero-indexed).
import toArray from 'dayjs/plugin/toArray'
dayjs.extend(toArray)
dayjs('2019-01-25').toArray() // [2019, 0, 25, 0, 0, 0, 0]
toObject
Adds toObject() returning { years, months, date, hours, minutes, seconds, milliseconds }.
import toObject from 'dayjs/plugin/toObject'
dayjs.extend(toObject)
dayjs('2019-01-25').toObject()
// { years: 2019, months: 0, date: 25, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 }
updateLocale
Adds static dayjs.updateLocale() to override specific properties of an already-loaded locale without redefining the whole thing.
import updateLocale from 'dayjs/plugin/updateLocale'
dayjs.extend(updateLocale)
dayjs.updateLocale('en', {
weekStart: 1,
months: ['Jan', 'Feb', /* โฆ */]
})
Note
The target locale must already be loaded;
updateLocalemerges your changes into it and returnsundefinedif the locale isn't found.
utc
Adds UTC mode: dayjs.utc(), .utc(), .local(), .isUTC(), and an overloaded .utcOffset(offset) setter.
import utc from 'dayjs/plugin/utc'
dayjs.extend(utc)
dayjs.utc('2019-01-25T02:00:00.000Z').format() // UTC ISO output
dayjs('2019-01-25').utc().format()
dayjs.utc().local() // back to local time
dayjs().utc().isUTC() // true
dayjs().utcOffset(8) // set offset to +8 hours
UTC-mode objects format with a default template of YYYY-MM-DDTHH:mm:ss[Z] and read their parts via the native getUTC* methods.
weekday
Adds weekday() โ a locale-aware day-of-week getter/setter where 0 is the first day of the week for the active locale (unlike day(), where 0 is always Sunday).
import weekday from 'dayjs/plugin/weekday'
dayjs.extend(weekday)
dayjs().weekday() // 0 = first day of the locale's week
dayjs().weekday(0) // set to the first day of this week
weekOfYear
Adds week() to get or set the week of the year (locale-aware), plus the weeks() alias.
import weekOfYear from 'dayjs/plugin/weekOfYear'
dayjs.extend(weekOfYear)
dayjs('2019-06-01').week() // week number
dayjs('2019-06-01').week(1) // set to week 1
weekYear
Adds weekYear(), the year that owns the current locale week. Requires weekOfYear.
import weekOfYear from 'dayjs/plugin/weekOfYear'
import weekYear from 'dayjs/plugin/weekYear'
dayjs.extend(weekOfYear)
dayjs.extend(weekYear)
dayjs('2019-12-31').weekYear()
Plugin dependency cheat sheet
| Plugin | Requires first |
|---|---|
timezone |
utc |
weekYear |
weekOfYear |
isoWeeksInYear |
isLeapYear |
advancedFormat w/W/gggg/z tokens |
weekOfYear / isoWeek / weekYear / timezone |
duration humanize |
relativeTime |
12. Internationalization (i18n)
Day.js ships 143 locale files (English is built in). Locales are loaded on demand, so they never bloat your bundle unless you import them.
Loading and switching locales
Import a locale, then set it globally or per-object:
import dayjs from 'dayjs'
import 'dayjs/locale/zh-cn' // load the locale (registers it)
dayjs.locale('zh-cn') // set globally
dayjs('2018-08-08').format('YYYYๅนดMMๆDDๆฅ')
Per-object locale (does not change the global default):
import 'dayjs/locale/es'
dayjs().locale('es').format('dddd') // Spanish weekday, global locale unchanged
Read the current global locale:
dayjs.locale() // 'en'
Tip
dayjs.locale('xx')changes the global default for new objects.someDate.locale('xx')returns a new object using that locale and leaves the global default alone. Prefer the per-object form in libraries so you don't surprise the host application.
Defining a custom locale
A locale object provides names and formatting rules:
dayjs.locale('custom', {
name: 'custom',
weekdays: ['Sunday', 'Monday', /* โฆ */],
months: ['January', 'February', /* โฆ */],
weekStart: 1, // Monday
yearStart: 4, // first week containing Jan 4
ordinal: (n) => `${n}.`,
formats: {
LT: 'HH:mm',
L: 'DD/MM/YYYY',
LL: 'D MMMM YYYY'
}
})
The English locale is intentionally minimal โ it omits weekdaysShort, weekdaysMin, and monthsShort because format() derives short names by slicing the full ones when a locale doesn't supply explicit short forms.
Tweaking an existing locale
Use the updateLocale plugin to override only certain fields (for example, to make the week start on Monday):
import updateLocale from 'dayjs/plugin/updateLocale'
dayjs.extend(updateLocale)
dayjs.updateLocale('en', { weekStart: 1 })
Locale object fields
| Field | Purpose |
|---|---|
name |
Locale identifier (e.g. 'zh-cn') |
weekdays |
Full weekday names (Sun โ Sat) |
weekdaysShort |
Short weekday names (optional) |
weekdaysMin |
Minimal weekday names (optional) |
months |
Full month names |
monthsShort |
Short month names (optional) |
weekStart |
First day of the week (0 = Sunday) |
yearStart |
Day-of-Jan that anchors week 1 |
ordinal |
Function turning a number into an ordinal string |
formats |
Long date formats (LT, L, LL, โฆ) for localizedFormat |
relativeTime |
Phrasing map for relativeTime |
meridiem |
Function producing AM/PM equivalents |
calendar |
Phrasing map for the calendar plugin |
13. Common Tasks and Recipes
Format the current date
dayjs().format('YYYY-MM-DD HH:mm:ss')
Get the start and end of today
dayjs().startOf('day') // 00:00:00.000
dayjs().endOf('day') // 23:59:59.999
Add business-friendly offsets
dayjs().add(2, 'week').format('YYYY-MM-DD')
dayjs().subtract(1, 'month').endOf('month').format('YYYY-MM-DD')
Difference in days between two dates
dayjs('2024-12-31').diff('2024-01-01', 'day') // 365
"Time ago" label
import relativeTime from 'dayjs/plugin/relativeTime'
dayjs.extend(relativeTime)
dayjs(post.createdAt).fromNow() // 'a few seconds ago', '2 hours ago', โฆ
Parse a non-ISO string
import customParseFormat from 'dayjs/plugin/customParseFormat'
dayjs.extend(customParseFormat)
dayjs('31/12/2024', 'DD/MM/YYYY').format('YYYY-MM-DD') // '2024-12-31'
Work in a specific time zone
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'
dayjs.extend(utc); dayjs.extend(timezone)
dayjs.tz('2024-06-01 09:00', 'America/New_York').format()
Validate user input
import customParseFormat from 'dayjs/plugin/customParseFormat'
dayjs.extend(customParseFormat)
function isValidDate(input) {
return dayjs(input, 'YYYY-MM-DD', true).isValid() // strict mode
}
Iterate over a date range
function eachDay(start, end) {
const days = []
let cursor = dayjs(start)
const last = dayjs(end)
while (cursor.isBefore(last) || cursor.isSame(last, 'day')) {
days.push(cursor.format('YYYY-MM-DD'))
cursor = cursor.add(1, 'day')
}
return days
}
Tip
Because objects are immutable, range loops must reassign the cursor (
cursor = cursor.add(1, 'day')). A barecursor.add(1, 'day')would loop forever.
14. Troubleshooting
Error: Invalid Date returned from dayjs()
Cause: The input couldn't be parsed โ most often a non-ISO string passed to the core parser, or dayjs(null).
Solution: Verify the input matches ISO 8601, or enable customParseFormat and pass the format explicitly. Check with .isValid() before using the result.
Prevention: Validate user-supplied dates with strict-mode customParseFormat. Remember dayjs(null) is intentionally invalid while dayjs(undefined) is "now."
My add/subtract/set call seems to do nothing
Cause: Day.js is immutable; these methods return a new object and leave the original unchanged.
Solution: Assign the result: date = date.add(1, 'day').
Prevention: Treat every Day.js object as read-only. If you truly need mutation, the badMutable plugin exists โ but reassignment is the idiomatic fix.
month() returns the wrong number
Cause: Months are zero-indexed (January = 0). This matches native Date.
Solution: Add 1 for human display, or format with MM/MMMM which are 1-indexed/named.
Prevention: Use format('M') (1-indexed) for output rather than month() + 1 string-building.
Error: fromNow is not a function
Cause: The relativeTime plugin wasn't extended before use.
Solution:
import relativeTime from 'dayjs/plugin/relativeTime'
dayjs.extend(relativeTime)
Prevention: Centralize all dayjs.extend(...) calls in one startup module that runs before any date logic.
Error: tz is not a function
Cause: The timezone plugin (and its required utc plugin) weren't extended.
Solution: Extend utc first, then timezone.
Prevention: Follow the plugin dependency cheat sheet โ timezone always needs utc.
Console warning: "To parse a date-time string โฆ you should enable customParseFormat plugin first"
Cause: You passed a format string as the second argument without extending customParseFormat. The message comes from the devHelper plugin.
Solution: Extend customParseFormat.
Prevention: These warnings only appear with devHelper in non-production builds; treat them as a checklist before shipping.
Console warning: "Guessing you may want to use locale โฆ you have to load it before using it"
Cause: You called dayjs.locale('xx') without first importing dayjs/locale/xx.
Solution: import 'dayjs/locale/xx' before setting the locale.
Prevention: Import each locale module you intend to activate.
startOf('week') starts on the wrong day
Cause: Week start is locale-dependent (weekStart). The default en locale starts on Sunday.
Solution: Switch locale, override with updateLocale({ weekStart: 1 }), or use startOf('isoWeek') (always Monday) via the isoWeek plugin.
Prevention: Decide on calendar vs. ISO week semantics up front.
A custom-format string parses but re-formats incorrectly
Cause: customParseFormat tokens are for reading input; format() tokens are for writing output. They are separate steps.
Solution: dayjs(input, parseFormat).format(displayFormat).
Prevention: Keep parse format and display format as distinct variables.
Two-digit years parse to the wrong century
Cause: The default rule maps YY > 68 to the 1900s and otherwise to the 2000s.
Solution: Supply a parseTwoDigitYear function in the customParseFormat options.
Prevention: Prefer four-digit years (YYYY) in stored data.
Duration humanize() throws or returns nothing
Cause: humanize() depends on relativeTime.
Solution: Extend both duration and relativeTime.
Prevention: See the dependency cheat sheet.
diff returns 0 for dates that differ
Cause: diff truncates to a whole number by default, so sub-unit differences round toward zero.
Solution: Pass true as the third argument for a floating-point result, or use a smaller unit.
Prevention: Choose the unit and float flag deliberately for the precision you need.
15. FAQ
Q: Is Day.js a drop-in replacement for Moment.js?
A: For most common operations, yes โ the API is intentionally Moment-compatible. Some Moment features live behind plugins (UTC, time zones, durations, custom parsing) that you extend on demand. Mutation-dependent Moment code needs the badMutable plugin or a refactor, since Day.js is immutable by default.
Q: How big is Day.js really? A: The core is roughly 2ย KB minified and gzipped. Plugins and locales add only what you import, so a typical app stays small.
Q: Do I need to install @types/dayjs for TypeScript?
A: No. Type declarations ship with the package (index.d.ts), and plugin types under dayjs/plugin/* augment the core types automatically when imported.
Q: Why is month() zero-indexed?
A: To match the native JavaScript Date API. Use the MM/MMMM format tokens for human-readable, 1-indexed output.
Q: How do I get the current Unix timestamp?
A: dayjs().unix() for seconds, dayjs().valueOf() for milliseconds.
Q: How do I parse a date in a specific format?
A: Extend customParseFormat and pass the format string: dayjs('31-12-2024', 'DD-MM-YYYY').
Q: How do I display "3 hours ago"?
A: Extend relativeTime and call .fromNow().
Q: How do I work with time zones?
A: Extend utc then timezone, and use .tz('Area/City') or dayjs.tz(...).
Q: Are Day.js objects safe to share across my app?
A: Yes โ they're immutable, so no caller can change an object another part of your code holds (unless badMutable is active).
Q: How many locales are available?
A: 143 locale files ship with the package; English is built in and the rest load on demand via import 'dayjs/locale/<name>'.
Q: How do I make the week start on Monday?
A: Use a locale whose weekStart is 1, override with updateLocale('en', { weekStart: 1 }), or use the isoWeek plugin's startOf('isoWeek').
Q: What's the difference between day(), date(), and weekday()?
A: day() is the day of week (0 = Sunday, always). date() is the day of the month (1โ31). weekday() (plugin) is the day of week relative to the locale's first day.
Q: How do I add a quarter or a week?
A: Weeks work in the core (add(1, 'week')). Quarters need the quarterOfYear plugin (add(1, 'quarter')).
Q: Can I represent a length of time, not a moment?
A: Yes โ the duration plugin provides dayjs.duration(...) objects with asHours(), asDays(), ISO output, and more.
Q: My loop over a date range never ends โ why?
A: You're calling cursor.add(1, 'day') without reassigning. Immutability means you must write cursor = cursor.add(1, 'day').
16. Quick Reference
Core static methods
| Method | Purpose |
|---|---|
dayjs(input?) |
Create an object (now, string, Date, ms number, or clone) |
dayjs.unix(seconds) |
Create from a seconds timestamp |
dayjs.extend(plugin, opts?) |
Install a plugin (once) |
dayjs.locale(name?, obj?) |
Get or set the global locale |
dayjs.isDayjs(value) |
Type check |
Core instance methods
| Method | Returns |
|---|---|
clone() |
New identical object |
isValid() |
Boolean |
year/month/date/day/hour/minute/second/millisecond() |
Getter (no arg) / setter (value) |
get(unit) / set(unit, value) |
Generic getter / setter |
add(n, unit) / subtract(n, unit) |
New shifted object |
startOf(unit) / endOf(unit) |
New snapped object |
format(template?) |
String |
diff(date?, unit?, float?) |
Number |
valueOf() / unix() |
Milliseconds / seconds |
daysInMonth() |
Number |
toDate() / toJSON() / toISOString() / toString() |
Native Date / strings |
utcOffset() |
Minutes |
isBefore/isSame/isAfter(date, unit?) |
Boolean |
locale(name?, obj?) |
Locale string / new localized object |
Plugins at a glance
| Plugin | Adds |
|---|---|
advancedFormat |
Extra format tokens (Q, Do, X, โฆ) |
arraySupport |
Array input to the factory |
badMutable |
In-place mutation |
bigIntSupport |
BigInt timestamps |
buddhistEra |
BBBB/BB tokens |
calendar |
calendar() |
customParseFormat |
Custom-format parsing |
dayOfYear |
dayOfYear() |
devHelper |
Dev-time warnings |
duration |
dayjs.duration() objects |
isBetween |
isBetween() |
isLeapYear |
isLeapYear() |
isMoment |
dayjs.isMoment() |
isoWeek |
ISO week methods |
isoWeeksInYear |
isoWeeksInYear() |
isSameOrAfter |
isSameOrAfter() |
isSameOrBefore |
isSameOrBefore() |
isToday / isTomorrow / isYesterday |
Relative-day checks |
localeData |
Locale data accessors |
localizedFormat |
L/LL/LLLL tokens |
minMax |
dayjs.max() / dayjs.min() |
negativeYear |
Negative-year parsing |
objectSupport |
Object input to factory/set/add |
pluralGetSet |
Plural getter/setter aliases |
preParsePostFormat |
Locale preparse/postformat hooks |
quarterOfYear |
quarter() and 'quarter' unit |
relativeTime |
fromNow() / from() / to() / toNow() |
timezone |
IANA time-zone support |
toArray |
toArray() |
toObject |
toObject() |
updateLocale |
dayjs.updateLocale() |
utc |
UTC mode |
weekday |
Locale-relative weekday |
weekOfYear |
week() |
weekYear |
weekYear() |
Setup template
import dayjs from 'dayjs'
// Plugins
import relativeTime from 'dayjs/plugin/relativeTime'
import customParseFormat from 'dayjs/plugin/customParseFormat'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'
// Locale (optional)
import 'dayjs/locale/en'
dayjs.extend(relativeTime)
dayjs.extend(customParseFormat)
dayjs.extend(utc)
dayjs.extend(timezone) // after utc
dayjs.locale('en')
export default dayjs