Format LocalDateTime with Timezone in Java8
Asked Answered
K

3

161

I have the this simple code:

DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss.SSSSSS Z");
LocalDateTime.now().format(FORMATTER)

Then I will get following exception:

java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: OffsetSeconds
at java.time.LocalDate.get0(LocalDate.java:680)
at java.time.LocalDate.getLong(LocalDate.java:659)
at java.time.LocalDateTime.getLong(LocalDateTime.java:720)
at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
at java.time.format.DateTimeFormatterBuilder$OffsetIdPrinterParser.format(DateTimeFormatterBuilder.java:3315)
at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2182)
at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1745)
at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1719)
at java.time.LocalDateTime.format(LocalDateTime.java:1746)

How to resolve this issue?

Krupp answered 29/8, 2014 at 3:42 Comment(0)
P
283

LocalDateTime is a date-time without a time-zone. You specified the time zone offset format symbol in the format, however, LocalDateTime doesn't have such information. That's why the error occured.

If you want time-zone information, you should use ZonedDateTime.

DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss.SSSSSS Z");
ZonedDateTime.now().format(FORMATTER);
=> "20140829 14:12:22.122000 +09"
Penuche answered 29/8, 2014 at 4:7 Comment(1)
Super helpful. Not sure I would have caught the timezone piece so easily. Great answer. Thank you!Septuor
H
48

The prefix "Local" in JSR-310 (aka java.time-package in Java-8) does not indicate that there is a timezone information in internal state of that class (here: LocalDateTime). Despite the often misleading name such classes like LocalDateTime or LocalTime have NO timezone information or offset.

You tried to format such a temporal type (which does not contain any offset) with offset information (indicated by pattern symbol Z). So the formatter tries to access an unavailable information and has to throw the exception you observed.

Solution:

Use a type which has such an offset or timezone information. In JSR-310 this is either OffsetDateTime (which contains an offset but not a timezone including DST-rules) or ZonedDateTime. You can watch out all supported fields of such a type by look-up on the method isSupported(TemporalField).. The field OffsetSeconds is supported in OffsetDateTime and ZonedDateTime, but not in LocalDateTime.

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss.SSSSSS Z");
String s = ZonedDateTime.now().format(formatter);
Hildredhildreth answered 29/8, 2014 at 5:8 Comment(2)
With what prefix would you have replaced the prefix "Local" to avoid confusion?Culberson
@Culberson There was also a debate initiated by Bank Credit Suisse suggesting the name PlainDateTime etc. Probably better because the prefix "plain" does indicate that there is nothing more than just date-time. If we were still before Java v1.0 then no prefix would have been better, but names like Date etc. are already reserved by old JDK.Hildredhildreth
F
4

The existing answers are correct but have missed some crucial information.

  1. While Z represents the time-zone offset only, V represents the time-zone ID which gives a ZonedDateTime its identity. Learn more about it from DateTimeFormatter documentation. While a time-zone ID of a place is fixed, the time-zone offset varies if the place observes DST (e.g. Europe/London has +01:00 offset in summer and +00:00, also represented as Z, offset in winter).
  2. One should always use Locale while using date-time parsing/formatting API because they are Locale-sensitive. Check Never use Date-Time formatting/parsing API without a Locale to learn more about it.
  3. Since LocalDateTime does not contain any information about time-zone, it should be formatted without any letter that represents time-zone related information. It is obvious that a formatter for LocalDateTime can always be used to format a ZonedDateTime but the reverse may fail (if it contains time-zone related character). If required, a ZonedDateTime representing the system's time zone can be derived from a LocalDateTime.
  4. I also prefer u to y with DateTimeFormatter.

Demo:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter ldtFormatter = DateTimeFormatter.ofPattern("uuuuMMdd HH:mm:ss.SSSSSS", Locale.ENGLISH);
        System.out.println(LocalDateTime.now().format(ldtFormatter));

        // ZZZZZ can be replaced with XXX
        DateTimeFormatter zdtFormatter = DateTimeFormatter.ofPattern("uuuuMMdd HH:mm:ss.SSSSSS VV ZZZZZ",
                Locale.ENGLISH);
        System.out.println(ZonedDateTime.now().format(zdtFormatter));
        System.out.println(ZonedDateTime.now().getZone());

        // Deriving ZonedDateTime from LocalDateTime using system's time zone
        LocalDateTime ldt = LocalDateTime.of(LocalDate.of(2022, 12, 15), LocalTime.of(10, 20, 30));
        ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
        System.out.println(zdt.format(zdtFormatter));
    }
}

Output:

20221013 20:55:13.466830
20221013 20:55:13.468847 Europe/London +01:00
Europe/London
20221215 10:20:30.000000 Europe/London Z

Learn more about the the modern date-time API from Trail: Date Time.

Financier answered 13/10, 2022 at 19:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.