# Migrate from Day.js to Temporal with GMT

Checked against Day.js 1.11.23 on 2026-10-27.

[Migrate to Temporal with GMT](/gmt/migrate/) explains the one change that applies to every call below: GMT takes strings in and gives strings out.

## Call by call

| Day.js | GMT | Note |
| --- | --- | --- |
| `dayjs(s)` | [`isValidDate`](/gmt/reference/plain/validate/isValidDate), [`isValidDateTime`](/gmt/reference/plain/validate/isValidDateTime) | The string is the value; check it with a validator. `dayjs("2024-02-30")` is 2024-03-01. |
| `dayjs(s, "DD/MM/YYYY", true)` (customParseFormat) | [`parseDateWithPattern`](/gmt/reference/plain/parse/parseDateWithPattern) | Rewrite the tokens to `dd/MM/yyyy`. |
| `dayjs(s, "YYYY-MM", true)` (customParseFormat) | [`parseYearMonth`](/gmt/reference/plain/parse/parseYearMonth), [`parseYearMonthWithPattern`](/gmt/reference/plain/parse/parseYearMonthWithPattern) | `YYYY-MM` is the ISO form, which `parseYearMonth` reads. For another pattern, rewrite the tokens, as in `MM/yyyy`. |
| `.isValid()` | [`isValidDate`](/gmt/reference/plain/validate/isValidDate) |  |
| `.format("LL")` (localizedFormat) | [`formatDate`](/gmt/reference/plain/format/formatDate) | GMT has no token formatter; pass a locale and `dateStyle`. |
| `.toISOString()` | none | None for a GMT UTC string; it is already ISO 8601. |
| `dayjs()`, `dayjs().tz(z)` | [`getUtcNow`](/gmt/reference/utc/get/getUtcNow), [`getZonedNow`](/gmt/reference/zoned/get/getZonedNow) |  |
| `dayjs().format("YYYY-MM-DD")` | [`getToday`](/gmt/reference/plain/get/getToday), [`getZonedToday`](/gmt/reference/zoned/get/getZonedToday) |  |
| `.add(1, "day")` | [`addDate`](/gmt/reference/plain/calculate/addDate), [`addZoned`](/gmt/reference/zoned/calculate/addZoned) | 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`. |
| `.add(1, "month")` | [`addDate`](/gmt/reference/plain/calculate/addDate) | The day is clamped to the month length. |
| `.subtract(n, unit)` | [`subtractDate`](/gmt/reference/plain/calculate/subtractDate), [`subtractZoned`](/gmt/reference/zoned/calculate/subtractZoned) |  |
| `.add(1, "hour")` | [`addZoned`](/gmt/reference/zoned/calculate/addZoned) |  |
| `b.diff(a, "day")` | [`diffDate`](/gmt/reference/plain/calculate/diffDate) | The arguments are in the other order: earlier first. |
| `b.diff(a, "month")` | [`diffDate`](/gmt/reference/plain/calculate/diffDate) | From 2024-01-31 to 2024-02-29 Day.js counts 1 month and GMT counts 0. |
| `.startOf("month")` | [`startOfDate`](/gmt/reference/plain/calculate/startOfDate) |  |
| `.endOf("month")` | [`endOfDate`](/gmt/reference/plain/calculate/endOfDate) |  |
| `dayjs.tz(s, z).startOf("day")` (timezone) | [`startOfZoned`](/gmt/reference/zoned/calculate/startOfZoned) | 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. |
| `.isBefore(b)`, `.isAfter(b)` | [`isBeforeDate`](/gmt/reference/plain/compare/isBeforeDate), [`isAfterDate`](/gmt/reference/plain/compare/isAfterDate) |  |
| `.isSame(b)` | [`areDatesEqual`](/gmt/reference/plain/compare/areDatesEqual), [`areUtcEqual`](/gmt/reference/utc/compare/areUtcEqual) |  |
| `.isSame(b, "day")` | [`areDatesEqualBy`](/gmt/reference/plain/compare/areDatesEqualBy) |  |
| `.isBetween(a, b)` (isBetween) | [`isBetweenDate`](/gmt/reference/plain/compare/isBetweenDate) | 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`](/gmt/reference/plain/calculate/minDate), [`maxDate`](/gmt/reference/plain/calculate/maxDate) |  |
| `.daysInMonth()`, `.isLeapYear()` (isLeapYear) | [`getDaysInMonth`](/gmt/reference/plain/calculate/getDaysInMonth), [`getDaysInYearMonth`](/gmt/reference/plain/calculate/getDaysInYearMonth), [`isLeapYear`](/gmt/reference/plain/validate/isLeapYear) |  |
| `.isoWeek()`, `.isoWeekYear()`, `.isoWeeksInYear()` (isoWeek) | [`getIsoWeekDate`](/gmt/reference/calendar/calculate/getIsoWeekDate), [`getWeeksInYear`](/gmt/reference/plain/calculate/getWeeksInYear) | `.week()` (weekOfYear) is a locale week. |
| `dayjs.tz(local, z)`, `.tz(z)` (timezone) | [`convertPlainDateTimeToZoned`](/gmt/reference/zoned/convert/convertPlainDateTimeToZoned), [`convertZonedToZoned`](/gmt/reference/zoned/convert/convertZonedToZoned) | GMT takes `disambiguation` for a wall time in a gap or an overlap. |
| `.fromNow()` (relativeTime) | [`formatRelativeZoned`](/gmt/reference/zoned/format/formatRelativeZoned), [`formatRelativeDate`](/gmt/reference/plain/format/formatRelativeDate) |  |
| `dayjs.duration(s).humanize()` (duration) | [`parseDuration`](/gmt/reference/duration/parse/parseDuration), [`formatDuration`](/gmt/reference/duration/format/formatDuration) |  |
| `dayjs.unix(sec)`, `.unix()` | [`convertUnixToUtc`](/gmt/reference/unix/convert/convertUnixToUtc), [`convertUtcToUnix`](/gmt/reference/utc/convert/convertUtcToUnix) | Pass `{ epochUnit: "seconds" }`. |

## 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`, `false` or `[]`.

```js
import { diffDate } from "@northguild/gmt/plain/calculate";

diffDate("2024-03-01", "2024-03-15", "days"); // 14
```

## Where Day.js differs from Temporal

Each item is a case where the library's result differs from Temporal's. The [full matrix](/gmt/compare/behaviour/) 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). The library returns 2024-03-31T02:30:00+01:00; Temporal returns 2024-03-31T03:30:00+02:00\[Europe/Berlin\]. Specification: [AddZonedDateTime (6.5.5)](https://tc39.es/proposal-temporal/#sec-temporal-addzoneddatetime). Upstream: [iamkun/dayjs#1271](https://github.com/iamkun/dayjs/issues/1271), [iamkun/dayjs#1260](https://github.com/iamkun/dayjs/issues/1260), [iamkun/dayjs#1827](https://github.com/iamkun/dayjs/issues/1827). [Matrix row](/gmt/compare/behaviour/#m1-dayjs).
- **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). The library returns 2024-03-31T12:00:00+01:00; Temporal returns 2024-03-31T12:00:00+02:00\[Europe/Berlin\]. Specification: [AddZonedDateTime (6.5.5)](https://tc39.es/proposal-temporal/#sec-temporal-addzoneddatetime). Upstream: [iamkun/dayjs#1271](https://github.com/iamkun/dayjs/issues/1271). [Matrix row](/gmt/compare/behaviour/#m2-dayjs).
- **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). The 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\]. Specification: [AddZonedDateTime (6.5.5)](https://tc39.es/proposal-temporal/#sec-temporal-addzoneddatetime). [Matrix row](/gmt/compare/behaviour/#m5-dayjs).
- **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). The 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\]. Specification: [GetStartOfDay (11.1.14)](https://tc39.es/proposal-temporal/#sec-temporal-getstartofday). Upstream: [iamkun/dayjs#1437](https://github.com/iamkun/dayjs/issues/1437), [iamkun/dayjs#2553](https://github.com/iamkun/dayjs/issues/2553), [iamkun/dayjs#2057](https://github.com/iamkun/dayjs/issues/2057). [Matrix row](/gmt/compare/behaviour/#m6-dayjs).
- **M8. Whole months between January 31 and February 29** Input: 2024-01-31 until 2024-02-29. The library returns 1; Temporal returns 0. Specification: [CompareSurpasses (3.5.5)](https://tc39.es/proposal-temporal/#sec-temporal-comparesurpasses). [Matrix row](/gmt/compare/behaviour/#m8-dayjs).
- **M9. Parse a date whose year has one digit** Input: "2-12-12" read as an ISO 8601 date. The library returns 2012-02-12; Temporal returns rejected. Specification: [ISO 8601 grammar (13.31)](https://tc39.es/proposal-temporal/#sec-temporal-iso8601grammar). [Matrix row](/gmt/compare/behaviour/#m9-dayjs).
- **M9b. Parse a date that is not on the calendar** Input: "2024-02-30". The library returns 2024-03-01; Temporal returns rejected. Specification: [IsValidDate (13.31.2)](https://tc39.es/proposal-temporal/#sec-temporal-iso8601grammar-static-semantics-isvaliddate). [Matrix row](/gmt/compare/behaviour/#m9b-dayjs).
- **M10. Read a string with a bracketed time zone** Input: 2024-03-31T03:30:00+02:00\[Europe/Berlin\]. The library returns rejected; Temporal returns 2024-03-31T01:30:00Z. Specification: [RFC 9557 section 4.1](https://www.rfc-editor.org/rfc/rfc9557#section-4.1). [Matrix row](/gmt/compare/behaviour/#m10-dayjs).
- **M10c. Read a string with a calendar annotation** Input: 2024-03-31T03:30:00+02:00\[Europe/Berlin\]\[u-ca=japanese\]. The library returns rejected; Temporal returns accepted. Specification: [RFC 9557 section 3.3](https://www.rfc-editor.org/rfc/rfc9557#section-3.3). [Matrix row](/gmt/compare/behaviour/#m10c-dayjs).
- **M14. Offset written for a UTC instant** Input: 2024-03-31T01:30:00Z, written back as ISO 8601 with each library's default writer. The 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. Specification: [Temporal.Instant.prototype.toString (8.3.11)](https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tostring). [Matrix row](/gmt/compare/behaviour/#m14-dayjs).

## More calls

| Day.js | GMT | Note |
| --- | --- | --- |
| `.quarter()` (quarterOfYear) | [`getQuarterForDate`](/gmt/reference/plain/calculate/getQuarterForDate) |  |
| `.month(1)` | [`setDate`](/gmt/reference/plain/calculate/setDate) | `month()` counts from 0; GMT counts from 1. |
| `.dayOfYear()` (dayOfYear) | [`getDayOfYear`](/gmt/reference/plain/calculate/getDayOfYear) |  |
| `.utcOffset()` | [`getZonedOffsetAs`](/gmt/reference/zoned/parse/getZonedOffsetAs) | Pass the unit `minutes`. |
