Search code examples
javascalajava-time

How to format properly a Java LocalDateTime for JSON (Javascript) using a DateTimeFormatter?


I need to format a Java java.time.LocalDateTime into a JSON string that will match this format:

2023-09-17T00:00:00.000+00:00

I was looking at DateTimeFormatter.ISO_DATE_TIME defining my LocalDateTime with:

date.plusDays(daysToAdd)
   .atZone(ZoneOffset.UTC)
   .format(DateTimeFormatter.ISO_DATE_TIME)

but that produces:

2023-09-17T00:00:00Z

Is there a predefined formatter for this date format? Do I need to create a custom DateTimeFormatter, if so could you provide an example for this format?

EDIT

I ended up using a formatter based on Mark Rotteveel's example solution and the subsequent comments to this question:

val jsonDateTimeFormatter: DateTimeFormatter = new DateTimeFormatterBuilder()
    .append(DateTimeFormatter.ISO_LOCAL_DATE)
    .appendLiteral('T')
    .appendPattern("HH:mm:ss.SSS")
    .appendOffset("+HH:MM", "+00:00")
    .toFormatter()

Solution

  • There is no standard formatter that gives the exact result you want. The use of Z for offset 0 is standard (probably derives from ISO 8601, but I'm too lazy to verify).

    For the format you want, you'll need to build it up (see first example), or use a pattern (see second example). My example is in Java, but I assume it will translate directly to Scala:

    public static void main(String[] args) {
        var date = LocalDateTime.parse("2023-09-17T00:00:00");
    
        var fm = new DateTimeFormatterBuilder()
                .append(DateTimeFormatter.ISO_LOCAL_DATE)
                .appendLiteral('T')
                .appendValue(HOUR_OF_DAY, 2)
                .appendLiteral(':')
                .appendValue(MINUTE_OF_HOUR, 2)
                .appendLiteral(':')
                .appendValue(SECOND_OF_MINUTE, 2)
                .appendFraction(MILLI_OF_SECOND, 3, 3, true)
                .appendOffset("+HH:MM", "+00:00")
                .toFormatter();
    
        System.out.println(date.atOffset(ZoneOffset.UTC).format(fm));
    }
    

    The most important parts here for your requirements are:

    • .appendFraction(MILLI_OF_SECOND, 3, 3, true)

      Ensures the fractional seconds are rendered in 3 digits.

    • .appendOffset("+HH:MM", "+00:00")

      Renders the offset as {+/-}HH:mm (e.g. -01:00), and offset 0 as +00:00.

    Output:

    2023-09-17T00:00:00.000+00:00
    

    You can also use a pattern-based formatter:

    DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSxxx")
    

    The use of .SSS ensures three fractional digits, and xxx renders the offset as {+/-}HH:mm and offset 0 as +00:00.

    Note that it is probably best to use either of these formatters for formatting dates only, and use ISO_DATE_TIME for parsing, as it is more flexible and lenient.