Migrate from Moment.js to Temporal with GMT
Checked against Moment.js 2.31.0 and moment-timezone 0.6.5 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”| Moment.js | GMT | Note |
|---|---|---|
moment(s, moment.ISO_8601, true) | isValidDate@northguild/isValidDateTime@northguild/isValidZonedDateTime@northguild/ | The string is the value; check it with a validator. Strict ISO mode rejects a string with a [zone]. Measured in M10. |
moment(s, "DD/ | parseDateWithPattern@northguild/ | Rewrite the tokens to dd/. Measured in M9d. |
moment(s, "YYYY MMM", locale) | parseYearMonthWithPattern@northguild/ | Rewrite the tokens to yyyy MMM. The result is YYYY-MM, with no day. |
.isValid() | isValidDate@northguild/ | |
.format("LL") | 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. |
moment(), moment.tz(z) | getUtcNow@northguild/getZonedNow@northguild/ | |
.clone() | none | Not needed: a string cannot change. |
.add(1, "day") | addDate@northguild/addZoned@northguild/ | Moment changes the object and returns it; GMT returns a new string. The Berlin gap result is the same. 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, "days") | diffDate@northguild/ | The arguments are in the other order: earlier first. |
b.diff(a, "months") | diffDate@northguild/ | From 2024-01-31 to 2024-02-29 Moment counts 1 month and GMT counts 0. Measured in M8. |
.startOf("month") | startOfDate@northguild/ | |
.endOf("month") | endOfDate@northguild/ | |
moment.tz(s, z).startOf("day") | startOfZoned@northguild/ | The Santiago result is the same. Measured in M6. |
.isBefore(b), .isAfter(b) | isBeforeDate@northguild/isAfterDate@northguild/isBeforeZoned@northguild/ | |
.isSame(b) | areDatesEqual@northguild/areUtcEqual@northguild/ | Measured in M3b. |
.isSame(b, "day") | areDatesEqualBy@northguild/areZonedEqualBy@northguild/ | |
.isBetween(a, b) | isBetweenDate@northguild/ | Moment excludes both ends by default; pass { inclusiveStart: false, inclusiveEnd: false }. |
moment.min(…), moment.max(…) | minDate@northguild/maxDate@northguild/ | |
.daysInMonth(), .isLeapYear() | getDaysInMonth@northguild/getDaysInYearMonth@northguild/isLeapYear@northguild/ | |
.isoWeek(), .isoWeekYear(), .isoWeeksInYear() | getIsoWeekDate@northguild/getWeeksInYear@northguild/ | .week() is a locale week. Measured in M12. |
moment.tz(local, z), .tz(z) | convertPlainDateTimeToZoned@northguild/convertZonedToZoned@northguild/ | Moment has no disambiguation option; GMT takes disambiguation. Measured in M4, M4b, M16. |
.fromNow(), .calendar() | formatRelativeZoned@northguild/formatCalendar@northguild/ | |
moment.duration(s).humanize() | parseDuration@northguild/formatDuration@northguild/ | |
moment.unix(sec), .unix() | convertUnixToUtc@northguild/convertUtcToUnix@northguild/ | Pass { epochUnit: "seconds" }. |
.isDST(), .utcOffset(), moment.tz.zone(z) | isInDaylightSaving@northguild/getZonedOffsetAs@northguild/isValidTimeZone@northguild/ |
What changes
Section titled “What changes”- Moment objects are mutable:
a.add(1, "day")changesaand returns it. GMT returns a new string and never changes its input. 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[]. - Time zones come from the companion package
moment-timezone.
import { diffDate } from "@northguild/gmt/plain/calculate";
diffDate("2024-03-01", "2024-03-15", "days"); // 14Where Moment.js differs from Temporal
Section titled “Where Moment.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.
M8 Whole months between January 31 and February 29
- Input
- 2024-01-31 until 2024-02-29
- Library returns
1- Temporal returns
0
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”| Moment.js | GMT | Note |
|---|---|---|
.quarter() | getQuarterForDate@northguild/ | |
.month(1) | setDate@northguild/ | month() counts from 0; GMT counts from 1. |
.dayOfYear() | getDayOfYear@northguild/ |
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.