TIL: Cypher has a format() function for dates and times

A deprecation warning taught me that Cypher now formats dates, times, and durations natively. No APOC required.

I was sweeping GraphAcademy course content for deprecated Cypher this week. The checker prepends EXPLAIN to every query in every lesson and collects any notifications Neo4j sends back.

The EXPLAIN keyword instructs Neo4j to plan the query without executing it. This is a quick way to test the syntax of a query. In our case, it allows us to highlight any deprecated keywords or functions in the .

The following query was run to test a query in Cypher Intermediate Queries that formatted a date and time using apoc.temporal.format.

cypher
EXPLAIN
WITH datetime() AS datetime
RETURN apoc.temporal.format(datetime, 'HH:mm:ss.SSSS') AS formattedDateTime

In running this query, we were informed that:

error
apoc.temporal.format has been replaced by Cypher's format function.

When issues like this come up, we use a built-in MCP server to fact-check against the Neo4j documentation and confirm the correct syntax. In this case, we needed to replace apoc.temporal.format() with the format() function.

The Cypher format() function

The format() function, available in Cypher 25 and introduced in version Neo4j 2025.09, takes a temporal value — DATE, LOCAL TIME, ZONED TIME, LOCAL DATETIME, ZONED DATETIME, or a DURATION — and returns a STRING.

The pattern follows the same syntax as Java's DateTimeFormatter, so anything you wrote for apoc.temporal.format carries over unchanged:

cypher
WITH datetime('1986-11-18T6:04:45.123456789+01:00[Europe/Berlin]') AS dt
RETURN format(dt, "MM/dd/yyyy") AS US, // (1) 
    format(dt, "dd/MM/yyyy") AS EU, // (2)
    format(dt) AS ISO // (3)
  1. 1

    Returns the date as {month}/{day}/{year} - eg: 11/18/1986

  2. 2

    Returns the date as {day}/{month}/{year} - eg: 18/11/1986

  3. 3

    Returns the ISO 8601 format - eg 1986-11-18T06:04:45.123456789+01:00[Europe/Berlin]

If you omit the pattern, an ISO 8601 string is returned, the same result as calling the toString() function.

Pattern tokens at a glance

The full table is in thedocumentation, but these cover most formatting work. Repeating a letter controls width and text style: M gives 7, MM gives 07, MMM gives Jul, MMMM gives July.

TokenMeaningExample output
yYear-of-era2004; 04
MMonth7; 07; Jul; July
dDay-of-month10
HHour-of-day (0-23)0
mMinute-of-hour30
sSecond-of-minute55
SFraction-of-second978
EDay-of-weekTue; Tuesday
QQuarter-of-year3; Q3; 3rd quarter
GEraAD
zTime zone namePST; Pacific Standard Time
BPeriod-of-dayin the morning

Literal text goes in single quotes. A documented example combines day-of-year (D) and localised day-of-week (c): the pattern "DDD'nd day of the year,' c'rd day of the week'" produces 322nd day of the year, 3rd day of the week.

It formats durations too

The format() function also provides something that APOC didn't: the ability to format a duration. Pass a DURATION and the tokens become units, with larger components rolling into smaller ones when you leave them out:

cypher
WITH duration({years: 1, months: 4}) AS d
RETURN format(d, "y 'years' q 'quarters' M 'months'") AS withYears, // (1)
       format(d, "q 'quarters' M 'months'") AS withoutYears // (2)
  1. 1

    Returns 1 years 1 quarters 1 months

  2. 2

    Returns 5 quarters 1 months

Migrating from APOC

The migration is close to mechanical, and we've already updated the Cypher Intermediate Queries course to match. Here's a reference table for the common replacements:

Legacy APOCCypher replacementNotes
apoc.temporal.format(datetime, 'HH:mm:ss.SSSS')format(datetime, 'HH:mm:ss.SSSS')Drop the apoc.temporal. prefix and keep the pattern.
apoc.date.toISO8601(datetime.epochMillis, "ms")toString(datetime)Pass the temporal value straight to toString().

Learn more with GraphAcademy

If you want to learn more about intermediate Cypher topics like this, take the Cypher Intermediate Queries course. The working-with-Cypher-data module covers dates, times, and durations from the ground up.

Until next time...

Comments (0)

Loading comments...