Raymond Chenon
Raymond Chenon

Reputation: 12662

How to format the time as following "2018-03-15T23:47:15+01:00"

With java.time , I'm trying to format the time as the following "2018-03-15T23:47:15+01:00" . With this formatter I'm close to the result in Scala.

val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssZ")
ZonedDateTime.now() // 2018-03-14T19:25:23.397+01:00
ZonedDateTime.now().format(formatter) // => 2018-03-14 19:25:23+0100

But I cannot insert the extra character "T" between the day and hour.

What does this "T" mean BTW ?

How to format as "2018-03-15T23:47:15+01:00" ?

Notes:

In case you wonder why LocalDateTime cannot be formatted Format LocalDateTime with Timezone in Java8

Upvotes: 0

Views: 953

Answers (3)

wiam
wiam

Reputation: 15

Converting the ZonedDateTime to OffsetDateTime - as suggested in the other answers - works, but if you want to use a DateTimeFormatter, there's a built-in constant that does the job:

ZonedDateTime.now().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)

But it's important to note some differences between all the approaches. Suppose that the ZonedDateTime contains a date/time equivalent to 2018-03-15T23:47+01:00 (the seconds and milliseconds are zero).

All the approaches covered in the answers will give you different results.

toString() omits seconds and milliseconds when they are zero. So this code:

ZonedDateTime zdt = // 2018-03-15T23:47+01:00
zdt.toOffsetDateTime().toString()

prints:

2018-03-15T23:47+01:00
only hour and minute, because seconds and milliseconds are zero

The built-in formatter will omit only the milliseconds if it's zero, but it'll print the seconds, regardless of the value. So this:

zdt.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)

prints:

2018-03-15T23:47:00+01:00
seconds printed, even if it's zero; milliseconds ommited

And the formatter that uses an explicit pattern will always print all the fields specified, regardless of their values. So this:

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

prints:

2018-03-15T23:47:00.000+01:00
seconds and milliseconds are printed, regardless of their values


You'll also find a difference in values such as 2018-03-15T23:47:10.120+01:00 (note the 120 milliseconds). toString() and ofPattern will give you:

2018-03-15T23:47:10.120+01:00

While the built-in DateTimeFormatter.ISO_OFFSET_DATE_TIME will print only the first 2 digits:

2018-03-15T23:47:10.12+01:00

Just be aware of these details when choosing which approach to use.

Upvotes: 1

Anonymous
Anonymous

Reputation: 86232

As your question already shows, you may just rely on ZonedDateTime.toString() for getting a string like 2018-03-14T19:25:23.397+01:00. BTW, that string is in ISO 8601 format, the international standard. Only two minor modifications may be needed:

  • If you don’t want the fraction of second — well, I don’t see what harm it does, it agrees with ISO 8601, so whoever receives your ISO 8601 string should be happy to have it. But if you don’t want it, you may apply myZonedDateTime.truncatedTo(ChronoUnit.SECONDS) to get rid of it.
  • ZonedDateTime.toString() often appends a zone name, for example 2018-03-14T19:25:23+01:00[Europe/Paris], which is not part of the ISO 8601 standard. To avoid that, convert to OffsetDateTime before using its toString method: myZonedDateTime.toOffsetDateTime().toString() (or myZonedDateTime.truncatedTo(ChronoUnit.SECONDS).toOffsetDateTime().toString()).

Building your own formatter through a format pattern string is very flexible when this is what you need. However, very often we can get through with less (and then should do for the easier maintainability of our code): toString methods or built-in formatters including both the ISO ones and the localized ones that we can get from DateTimeFormatter.ofLocalizedPattern().

What does this "T" mean BTW ?

The T is part of the ISO 8601 format. It separates the date part from the time-of-day part. You may think of it as T for time since it denotes the start of the time part. If there is only a date (2018-04-25) or only a time-of-day (21:45:00), the T is not used, but when we have both, the T is required. You may think that the format might have been specified without the T, and you are probably right. When it comes to the format for periods/durations it is indispensable, however, and also needed when there are no days: P3M means a period of 3 months, while PT3M means 3 minutes.

Link: Read more in the Wikipedia article on ISO 8601.

Upvotes: 0

Evgeny
Evgeny

Reputation: 1770

Try this

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

ZonedDateTime.now().format(ZONED_DATE_TIME_ISO8601_FORMATTER3)
// 2018-03-14T19:35:54.321+01:00

See here

Offset X and x: This formats the offset based on the number of pattern letters. One letter outputs just the hour, such as '+01', unless the minute is non-zero in which case the minute is also output, such as '+0130'. Two letters outputs the hour and minute, without a colon, such as '+0130'. Three letters outputs the hour and minute, with a colon, such as '+01:30'. Four letters outputs the hour and minute and optional second, without a colon, such as '+013015'. Five letters outputs the hour and minute and optional second, with a colon, such as '+01:30:15'. Six or more letters throws IllegalArgumentException. Pattern letter 'X' (upper case) will output 'Z' when the offset to be output would be zero, whereas pattern letter 'x' (lower case) will output '+00', '+0000', or '+00:00'.

Upvotes: 3

Related Questions