Migrate from Day.js to Temporal with GMT
Checked against Day.js 1.11.23 on 2026-10-27.
Migrate to Temporal with GMT explains the one change that applies to every call below: GMT takes strings in and gives strings out.
Call by call
Section titled “Call by call”| Day.js | GMT | Note |
|---|---|---|
dayjs(s) | isValidDate@northguild/isValidDateTime@northguild/ | The string is the value; check it with a validator. dayjs("2024-02-30") is 2024-03-01. Measured in M9, M9b. |
dayjs(s, "DD/ (customParseFormat) | parseDateWithPattern@northguild/ | Rewrite the tokens to dd/. Measured in M9d. |
dayjs(s, "YYYY-MM", true) (customParseFormat) | parseYearMonth@northguild/parseYearMonthWithPattern@northguild/ | YYYY-MM is the ISO form, which parseYearMonth reads. For another pattern, rewrite the tokens, as in MM/. |
.isValid() | isValidDate@northguild/ | |
.format("LL") (localizedFormat) | formatDate@northguild/ | GMT has no token formatter; pass a locale and dateStyle. |
.toISOString() | none | None for a GMT UTC string; it is already ISO 8601. Measured in M14. |
dayjs(), dayjs().tz(z) | getUtcNow@northguild/getZonedNow@northguild/ | |
dayjs().format("YYYY-MM-DD") | getToday@northguild/getZonedToday@northguild/ | |
.add(1, "day") | addDate@northguild/addZoned@northguild/ | With the timezone plugin, Day.js keeps the starting offset after a clock change: 2024-03-30 12:00 in Berlin plus one day gives 12:00+01:00; Temporal gives 12:00+02:00. Measured in M1, M2, M3, M5. |
.add(1, "month") | addDate@northguild/ | The day is clamped to the month length. Measured in M7. |
.subtract(n, unit) | subtractDate@northguild/subtractZoned@northguild/ | |
.add(1, "hour") | addZoned@northguild/ | |
b.diff(a, "day") | diffDate@northguild/ | The arguments are in the other order: earlier first. |
b.diff(a, "month") | diffDate@northguild/ | From 2024-01-31 to 2024-02-29 Day.js counts 1 month and GMT counts 0. Measured in M8. |
.startOf("month") | startOfDate@northguild/ | |
.endOf("month") | endOfDate@northguild/ | |
dayjs.tz(s, z).startOf("day") (timezone) | startOfZoned@northguild/ | On a day whose midnight is skipped, Day.js returns a 00:00 that does not exist unless the host zone is America/Santiago, so the result depends on the host zone. Measured in M6. |
.isBefore(b), .isAfter(b) | isBeforeDate@northguild/isAfterDate@northguild/ | |
.isSame(b) | areDatesEqual@northguild/areUtcEqual@northguild/ | Measured in M3b. |
.isSame(b, "day") | areDatesEqualBy@northguild/ | |
.isBetween(a, b) (isBetween) | isBetweenDate@northguild/ | Day.js excludes both ends by default; pass { inclusiveStart: false, inclusiveEnd: false }. Day.js also returns true when a is after b; GMT returns false. |
dayjs.min(…), dayjs.max(…) (minMax) | minDate@northguild/maxDate@northguild/ | |
.daysInMonth(), .isLeapYear() (isLeapYear) | getDaysInMonth@northguild/getDaysInYearMonth@northguild/isLeapYear@northguild/ | |
.isoWeek(), .isoWeekYear(), .isoWeeksInYear() (isoWeek) | getIsoWeekDate@northguild/getWeeksInYear@northguild/ | .week() (weekOfYear) is a locale week. Measured in M12. |
dayjs.tz(local, z), .tz(z) (timezone) | convertPlainDateTimeToZoned@northguild/convertZonedToZoned@northguild/ | GMT takes disambiguation for a wall time in a gap or an overlap. Measured in M4, M4b, M16. |
.fromNow() (relativeTime) | formatRelativeZoned@northguild/formatRelativeDate@northguild/ | |
dayjs.duration(s).humanize() (duration) | parseDuration@northguild/formatDuration@northguild/ | |
dayjs.unix(sec), .unix() | convertUnixToUtc@northguild/convertUtcToUnix@northguild/ | Pass { epochUnit: "seconds" }. |
What changes
Section titled “What changes”- Day.js values are immutable objects. Most calls in the table need a plugin from the same package; the plugin is named in brackets.
month()counts months from 0. GMT counts them from 1 to 12.b.diff(a, unit)subtracts the earlier value from the later one.diffDate(a, b, unit)takes the earlier value first.- Invalid input gives an object whose
format()returns"Invalid Date". GMT returns"",null,falseor[].
import { diffDate } from "@northguild/gmt/plain/calculate";
diffDate("2024-03-01", "2024-03-15", "days"); // 14Where Day.js differs from Temporal
Section titled “Where Day.js differs from Temporal”Each item is a case where the library’s result differs from Temporal’s. The full matrix also lists the cases where it matches.
M1 Add one calendar day across a DST gap
- Input
- 2024-03-30T02:30:00+01:00[Europe/Berlin], plus 1 day (02:30 does not exist on 2024-03-31)
- Library returns
2024-03-31T02:30:00+01:00- Temporal returns
2024-03-31T03:30:00+02:00[Europe/Berlin]
AddZonedDateTime (6.5.5) · iamkun/dayjs#1271 · iamkun/dayjs#1260 · iamkun/dayjs#1827 · Matrix row
M2 Add one calendar day, not 24 hours
- Input
- 2024-03-30T12:00:00+01:00[Europe/Berlin], plus 1 day (the day of 2024-03-31 has 23 hours)
- Library returns
2024-03-31T12:00:00+01:00- Temporal returns
2024-03-31T12:00:00+02:00[Europe/Berlin]
M5 Add one day across a 30-minute DST change
- Input
- 2024-10-05T02:15:00+10:30[Australia/Lord_Howe], plus 1 day (the clocks move 30 minutes on 2024-10-06)
- Library returns
Asia/Kolkata host: 2024-10-06T02:15:00+10:30, Europe/London host: 2024-10-06T02:15:00+10:30, Australia/Sydney host: 2024-10-06T03:15:00+10:30, America/Santiago host: 2024-10-06T02:15:00+10:30- Temporal returns
2024-10-06T02:45:00+11:00[Australia/Lord_Howe]
M6 Start of a day whose midnight does not exist
- Input
- 2024-09-08T12:00:00-03:00[America/Santiago], start of day (the clocks skip from 00:00 to 01:00)
- Library returns
Asia/Kolkata host: 2024-09-08T00:00:00-04:00, Europe/London host: 2024-09-08T00:00:00-04:00, Australia/Sydney host: 2024-09-08T00:00:00-04:00, America/Santiago host: 2024-09-08T01:00:00-03:00- Temporal returns
2024-09-08T01:00:00-03:00[America/Santiago]
GetStartOfDay (11.1.14) · iamkun/dayjs#1437 · iamkun/dayjs#2553 · iamkun/dayjs#2057 · Matrix row
M8 Whole months between January 31 and February 29
- Input
- 2024-01-31 until 2024-02-29
- Library returns
1- Temporal returns
0
M9 Parse a date whose year has one digit
- Input
- "2-12-12" read as an ISO 8601 date
- Library returns
2012-02-12- Temporal returns
rejected
M9b Parse a date that is not on the calendar
- Input
- "2024-02-30"
- Library returns
2024-03-01- Temporal returns
rejected
M10 Read a string with a bracketed time zone
- Input
- 2024-03-31T03:30:00+02:00[Europe/Berlin]
- Library returns
rejected- Temporal returns
2024-03-31T01:30:00Z
M10c Read a string with a calendar annotation
- Input
- 2024-03-31T03:30:00+02:00[Europe/Berlin][u-ca=japanese]
- Library returns
rejected- Temporal returns
accepted
M14 Offset written for a UTC instant
- Input
- 2024-03-31T01:30:00Z, written back as ISO 8601 with each library's default writer
- Library returns
Asia/Kolkata host: 2024-03-31T07:00:00+05:30, Europe/London host: 2024-03-31T02:30:00+01:00, Australia/Sydney host: 2024-03-31T12:30:00+11:00, America/Santiago host: 2024-03-30T22:30:00-03:00- Temporal returns
2024-03-31T01:30:00Z
More calls
Section titled “More calls”| Day.js | GMT | Note |
|---|---|---|
.quarter() (quarterOfYear) | getQuarterForDate@northguild/ | |
.month(1) | setDate@northguild/ | month() counts from 0; GMT counts from 1. |
.dayOfYear() (dayOfYear) | getDayOfYear@northguild/ | |
.utcOffset() | getZonedOffsetAs@northguild/ | Pass the unit minutes. |
Code examples: MIT-0. Packages: MIT. Third-party data and components: their own licences, credited where they appear. Everything else:© 2026 NorthGuild, all rights reserved.