Skip to content

Migrate from date-fns to Temporal with GMT

Checked against date-fns 4.4.0 and @date-fns/tz 1.5.0 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.

date-fnsGMTNote
parseISO(s)
isValidDate@northguild/gmt/plain/validate
isValidDateTime@northguild/gmt/plain/validate
isValidZonedDateTime@northguild/gmt/zoned/validate
isValidUtc@northguild/gmt/utc/validate
The string is the value, so there is nothing to parse; check it with a validator. parseISO drops the offset before a [zone] and then reads the wall time as UTC (03:30Z). Temporal reads the basic format 20240212; GMT rejects it on purpose and follows the extended format of RFC 3339 §5.6. Measured in M9c, M10.
parse(s, "dd/MM/yyyy", ref)
parseDateWithPattern@northguild/gmt/plain/parse
yyyy is four digits in GMT. date-fns reads 2-12-12 as year 2. Measured in M9d.
parse(s, "yyyy MMMM", ref)
parseYearMonthWithPattern@northguild/gmt/plain/parse
A year and a month with no day, returned as YYYY-MM.
isValid(d)
isValidDate@northguild/gmt/plain/validate
GMT rejects 2024-02-30 and basic format. Measured in M9b, M9c.
format(d, "d MMMM yyyy")
formatDate@northguild/gmt/plain/format
GMT has no token formatter; pass a locale and dateStyle.
formatISO(d)noneNone for a GMT string, which is already ISO 8601. formatISO writes the host offset. Measured in M14.
The current time, from the built-in date object
getUtcNow@northguild/gmt/utc/get
getZonedNow@northguild/gmt/zoned/get
startOfToday()
getToday@northguild/gmt/plain/get
getZonedToday@northguild/gmt/zoned/get
addDays(d, n)
addDate@northguild/gmt/plain/calculate
addZoned@northguild/gmt/zoned/calculate
date-fns works in the host zone, so a day is a calendar day of the host, and the result depends on the host zone. GMT takes the zone from the value. Measured in M1, M2.
addMonths(d, n)
addDate@northguild/gmt/plain/calculate
The day is clamped to the month length. { overflow: "reject" } returns "". Measured in M7.
subDays(d, n), subMonths(d, n)
subtractDate@northguild/gmt/plain/calculate
addHours(d, n)
addZoned@northguild/gmt/zoned/calculate
addUtc@northguild/gmt/utc/calculate
Hours are exact time.
differenceInCalendarDays(later, earlier)
diffDate@northguild/gmt/plain/calculate
The arguments are in the other order: earlier first.
differenceInMonths(later, earlier)
diffDate@northguild/gmt/plain/calculate
From 2024-01-31 to 2024-02-29 date-fns counts 1 month and GMT counts 0. Measured in M8.
intervalToDuration({ start, end })
diffDate@northguild/gmt/plain/calculate
Pass the units you want, for example ["years", "months", "days"].
startOfMonth(d)
startOfDate@northguild/gmt/plain/calculate
endOfMonth(d)
endOfDate@northguild/gmt/plain/calculate
startOfDay(d, { in: tz(z) })
startOfZoned@northguild/gmt/zoned/calculate
The first instant of the day, which is not always 00:00. Measured in M6.
isBefore(a, b), isAfter(a, b)
isBeforeDate@northguild/gmt/plain/compare
isAfterDate@northguild/gmt/plain/compare
isBeforeZoned@northguild/gmt/zoned/compare
isEqual(a, b)
areDatesEqual@northguild/gmt/plain/compare
areUtcEqual@northguild/gmt/utc/compare
areZonedEqual@northguild/gmt/zoned/compare
areZonedEqual follows ZonedDateTime.prototype.equals. Measured in M3b, M13b, M13c.
isSameDay(a, b)
areDatesEqualBy@northguild/gmt/plain/compare
areZonedEqualBy@northguild/gmt/zoned/compare
isWithinInterval(d, { start, end })
isBetweenDate@northguild/gmt/plain/compare
Both ends are inclusive by default.
areIntervalsOverlapping(a, b)
intervalsOverlapDate@northguild/gmt/plain/interval
min(ds), max(ds)
minDate@northguild/gmt/plain/calculate
maxDate@northguild/gmt/plain/calculate
getDaysInMonth(d), isLeapYear(d)
getDaysInMonth@northguild/gmt/plain/calculate
getDaysInYearMonth@northguild/gmt/plain/calculate
isLeapYear@northguild/gmt/plain/validate
getISOWeek(d), getISOWeekYear(d)
getIsoWeekDate@northguild/gmt/calendar/calculate
One call returns year, week and weekday. getWeek is a locale week. Measured in M12.
getISOWeeksInYear(d)
getWeeksInYear@northguild/gmt/plain/calculate
isWeekend(d), addBusinessDays(d, n), differenceInBusinessDays(a, b)
isWeekend@northguild/gmt/plain/compare
addBusinessDays@northguild/gmt/plain/calculate
businessDaysBetween@northguild/gmt/calendar/business
date-fns fixes the weekend to Saturday and Sunday. isWeekend in GMT takes a locale. addBusinessDays takes an optional calendar and counts Monday to Friday without one. businessDaysBetween requires a calendar and reads the weekend from calendar.weekend. Measured in M15.
formatDistance(d, base), formatRelative(d, base)
formatRelativeDate@northguild/gmt/plain/format
formatRelativeZoned@northguild/gmt/zoned/format
The wording comes from Intl.RelativeTimeFormat.
  • date-fns takes and returns Date objects, or TZDate from @date-fns/tz. GMT takes and returns strings.
  • getMonth counts months from 0. GMT counts them from 1 to 12, as Temporal does.
  • The difference functions take the later date first, as in differenceInCalendarDays(later, earlier). diffDate takes the earlier date first.
  • Invalid input gives an Invalid Date, and format throws a RangeError. GMT returns "", null, false or [] instead of throwing.
import { diffDate } from "@northguild/gmt/plain/calculate";
diffDate("2024-03-01", "2024-03-15", "days"); // 14

parseISO reads 20240212, as Temporal does. GMT’s validators and parsers accept only the extended format, 2024-02-12, which is the format of RFC 3339 §5.6, so isValidDate("20240212") returns false. This is deliberate. Convert a basic-format string before you pass it on. The behaviour matrix shows the library’s result in case M9c.

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
    Asia/Kolkata host: 2024-03-31T03:30:00.000+02:00, Europe/London host: 2024-03-31T01:30:00.000+01:00, Australia/Sydney host: 2024-03-31T03:30:00.000+02:00, America/Santiago host: 2024-03-31T03:30:00.000+02:00
    Temporal returns
    2024-03-31T03:30:00+02:00[Europe/Berlin]
  • M3 Add one day into a repeated hour

    Input
    2024-10-26T02:30:00+02:00[Europe/Berlin], plus 1 day (02:30 happens twice on 2024-10-27)
    Library returns
    Asia/Kolkata host: 2024-10-27T02:30:00.000+02:00, Europe/London host: 2024-10-27T02:30:00.000+01:00, Australia/Sydney host: 2024-10-27T02:30:00.000+02:00, America/Santiago host: 2024-10-27T02:30:00.000+01:00
    Temporal returns
    2024-10-27T02:30:00+02:00[Europe/Berlin]
  • M4 Resolve a wall time that does not exist

    Input
    2024-03-31T02:30:00 in Europe/Berlin (the clocks skip from 02:00 to 03:00)
    Library returns
    Asia/Kolkata host: 2024-03-31T01:30:00.000+00:00, Europe/London host: 2024-03-31T00:30:00.000+00:00, Australia/Sydney host: 2024-03-31T01:30:00.000+00:00, America/Santiago host: 2024-03-31T01:30:00.000+00:00
    Temporal returns
    2024-03-31T01:30:00Z
  • 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:45:00.000+11:00, Europe/London host: 2024-10-06T02:45:00.000+11:00, Australia/Sydney host: 2024-10-06T03:15:00.000+11:00, America/Santiago host: 2024-10-06T02:45:00.000+11:00
    Temporal returns
    2024-10-06T02:45:00+11:00[Australia/Lord_Howe]
  • M8 Whole months between January 31 and February 29

    Input
    2024-01-31 until 2024-02-29
    Library returns
    1
    Temporal returns
    0
  • M9d Read a year of one digit with a pattern

    Input
    "2-12-12" read with the pattern year-month-day
    Library returns
    0002-12-12
    Stated result
    rejected
  • M10 Read a string with a bracketed time zone

    Input
    2024-03-31T03:30:00+02:00[Europe/Berlin]
    Library returns
    2024-03-31T03:30:00.000Z
    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
    accepted, reads 2024-03-31T03:30:00Z
    Temporal returns
    accepted
  • M10d Read a string whose offset does not match its time zone

    Input
    2022-07-08T00:14:07+01:00[Europe/Paris] (Paris is at +02:00 on that date)
    Library returns
    2022-07-08T00:14:07.000Z
    Temporal returns
    rejected
  • 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
date-fnsGMTNote
getQuarter(d)
getQuarterForDate@northguild/gmt/plain/calculate
set(d, { month: 1 })
setDate@northguild/gmt/plain/calculate
Months are 1 to 12 in GMT.
fromUnixTime(sec)
convertUnixToUtc@northguild/gmt/unix/convert
Pass { epochUnit: "seconds" }.
getUnixTime(d)
convertUtcToUnix@northguild/gmt/utc/convert
Pass { epochUnit: "seconds" }.
eachDayOfInterval({ start, end })
mapDatesInRange@northguild/gmt/plain/map
The end date is included.
getDayOfYear(d)
getDayOfYear@northguild/gmt/plain/calculate
formatDuration(d)
formatDuration@northguild/gmt/duration/format
new TZDate(…, z)
convertUtcToZoned@northguild/gmt/utc/convert
convertPlainDateTimeToZoned@northguild/gmt/zoned/convert

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.