Unable to obtain ZonedDateTime from TemporalAccessor using DateTimeFormatter and ZonedDateTime in Java 8
Asked Answered
A

5

73

I recently moved to Java 8 to, hopefully, deal with local and zoned times more easily.

However, I'm facing an, in my opinion, simple problem when parsing a simple date.

public static ZonedDateTime convertirAFecha(String fecha) throws Exception {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
            ConstantesFechas.FORMATO_DIA).withZone(
            obtenerZonaHorariaServidor());

    ZonedDateTime resultado = ZonedDateTime.parse(fecha, formatter);
    return resultado;
}

In my case:

  • fecha is '15/06/2014'
  • ConstantesFechas.FORMATO_DIA is 'dd/MM/yyyy'
  • obtenerZonaHorariaServidor returns ZoneId.systemDefault()

So, this is a simple example. However, the parse throws this exception:

java.time.format.DateTimeParseException: Text '15/06/2014' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor: {},ISO resolved to 2014-06-15 of type java.time.format.Parsed

Any tips? I've been trying different combinations of parsing and using TemporalAccesor, but without any luck so far.

Andante answered 11/5, 2014 at 18:45 Comment(2)
These can be fechas indeed :) !Skipjack
I have discussed the theory behind java.time at length here: https://mcmap.net/q/54330/-how-can-i-parse-format-dates-with-localdatetime-java-8Erminiaerminie
F
78

This does not work because your input (and your Formatter) do not have time zone information. A simple way is to parse your date as a LocalDate first (without time or time zone information) then create a ZonedDateTime:

public static ZonedDateTime convertirAFecha(String fecha) {
  DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
  LocalDate date = LocalDate.parse(fecha, formatter);

  ZonedDateTime resultado = date.atStartOfDay(ZoneId.systemDefault());
  return resultado;
}
Fetlock answered 11/5, 2014 at 18:54 Comment(2)
Yes, the problem occurs because the formatter does not supply a time zone. I edited your answer to stress this. You were right :-).Symbolism
Incidentally, the other solution, apart from using LocalDate, is to use DateTimeFormatter.withZone(x) to set a default time zone for the Formatter. Then you can use ZonedDateTime, and it will always have the time zone x.Symbolism
S
19

This is a bug, see JDK-bug-log. According to that information the problem was solved for Java 9 and Java 8u20. Try to download the latest Java 8 - version. Today on 2014-05-12: There is an early access release 8u20 available.

UPDATE:

Personally I think, since you only have and expect "dd/MM/yyyy" as pattern you should use LocalDate as your primary type as @assylias has already proposed. Regarding your context, it is almost sure a design failure to use ZonedDateTime. What do you want to do with objects of this type? I can only think of specialized timezone calculations as use-case. And you cannot even directly store these ZonedDateTime-objects in a database, so this type is far less useful than many people believe.

What I described as your use-case problem is indeed a new aspect introduced with Java-8 compared with the old GregorianCalendar-class (which is an all-in-one-type). Users have to start thinking about choosing the proper temporal type for their problems and use-cases.

Sulphate answered 12/5, 2014 at 9:43 Comment(5)
Nice catch - although I believe the op would still need to provide a time to be able to parse the input into a ZonedDateTime.Fetlock
@Fetlock Yep, OP should extend the pattern at least also for the time part or choose your suggestion. This might also be dependent on lenient parsing configuration etc. Am actually not able to test these details with Java-8 on my current place.Sulphate
I was using ZonedDateTime for "dd/MM/yyyy" dates. This solved my problem since you only have and expect "dd/MM/yyyy" as pattern you should use LocalDateAmalgam
@Symbolism You say: "...OP's code does not use withZone…" But the code presented by OP does use withZone(…), see above. So the mentioned JDK-bug still matches the problem of not really setting the zone in those early JDK-versions. Anyway, the other big problems are: Usage of ZonedDateTime.parse(…) instead of LocalDate.parse(…) and the lack of clock-time-pattern part (if insisting on ZonedDateTime.parse(…)).Sulphate
Oops, sorry, misread the question... my mistake. I deleted my comment.Symbolism
L
16

In simple words, the line

ZonedDateTime.parse('2014-04-23', DateTimeFormatter.ISO_OFFSET_DATE_TIME)

throws an exception:

Text '2014-04-23' could not be parsed at index 10
java.time.format.DateTimeParseException: Text '2014-04-23' could not be parsed at index 10

It looks like a bug for me.

I used this workaround:

String dateAsStr = '2014-04-23';
if (dateAsStr.length() == 10) {
    dateAsStr += 'T00:00:00';
}
ZonedDateTime.parse(dateAsStr, DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.systemDefault()));
Lucius answered 4/12, 2014 at 3:36 Comment(0)
A
8

If coming from Google:

Instead of doing:

ZonedDateTime.from(new Date().toInstant());

Try this:

ZonedDateTime.ofInstant(new Date(), ZoneId.of("UTC")); 
Adhern answered 2/3, 2018 at 0:19 Comment(0)
S
2

Just an example conversions, I believe some folks will get the exception below

(java.time.DateTimeException: Unable to obtain LocalDateTime from TemporalAccessor: 2014-10-24T18:22:09.800Z of type java.time.Instant)

if they try

LocalDateTime localDateTime = LocalDateTime.from(new Date().toInstant());

to resolve the issue, please pass in Zone -

LocalDateTime localDateTime = LocalDateTime.from(new Date()
        .toInstant().atZone(ZoneId.of("UTC")));
Submiss answered 27/10, 2015 at 13:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.