2

I am trying to use consistently the Java 8 date time API, I am looking for explications behind this behaviour :

Instant.from(LocalDateTime.of(2017,01,01,0,0,0,0))

Compiles just fine but yields to :

Exception in thread "main" java.time.DateTimeException: Unable to obtain Instant from TemporalAccessor: 2017-01-01T00:00 of type java.time.LocalDateTime

My question is :if these types are not compatible why did the API let me code that and compile it without screaming ?

Antonin
  • 879
  • 2
  • 10
  • 27
  • In my personal interpretation (and I am probably missing something) JSR-310 tries to balance convenience of code writing and reading with both static and dynamic checking. There are some very general interfaces that allow you to pass many kinds of objects to many methods, but if you’re doing something meaningless (in the eyes of the designers), it will fail at runtime. `TemporalAccessor`, the argument type of `Instant.of()`, is one of those interfaces, and yes, `LocalDateTime` is declared to implement it, which is why your code compiles. – Ole V.V. Dec 08 '17 at 09:22
  • `LocalDateTime` doesn’t define a point in time, so what you were trying to do does not make sense. Therefore it fails — unfortunately only at runtime. – Ole V.V. Dec 08 '17 at 09:23
  • 1
    A `TemporalAccessor` supports a number of fields. You can query its supported fields through its `isSupported()` method. `Instant.from()` is documented to require the fields `INSTANT_SECONDS` and `NANO_OF_SECOND`, so you may check for these before trying the conversion if you prefer this over getting and handling the exception. – Ole V.V. Dec 08 '17 at 09:29
  • 1
    You could obtain compile-time checking if the one interface `TemporalAccessor` was to be replaced by an interface for each thinkable combination of supported fields. Think what a mess it would be – Ole V.V. Dec 08 '17 at 09:31
  • 3
    The new `java.time`-API has not been designed to be type-safe at compile-time. Especially all static `from(...)`-methods are unsafe and can throw a runtime-exception. As far as I know, the main author of the new API does not like generics which would have been a good mean to increase compile-time type-safety. @OleV.V. It is not such a mess to design sensible signatures for `from(...)`-methods. Really doable. Only some more specific interfaces than `TemporalAccessor` as method parameter would be sufficient. – Meno Hochschild Dec 11 '17 at 11:55
  • On the other side, if someone wants he/she can construct/design an implementation of `TemporalAccessor` which contains all fields of `LocalDateTime` **plus zone-id** but not the field `INSTANT_SECONDS` then the conversion will fail although the field combination can indeed be understood as kind of `Instant`. – Meno Hochschild Dec 11 '17 at 12:03
  • 1
    We did all succeed at using the time API correctly in the end of the day, but on a 10 students (grown up developers, already coding older java) course, 6 encountered such runtime exception. We now know how to use this API but I wouldn't call it a strongly typed API in the end of the day. – Antonin Dec 11 '17 at 21:54
  • For comparison, if you are interested to see how a strongly typed time-API looks like then you can watch out my time library [Time4J](http://time4j.net/javadoc-en/index.html) It uses a lot of generics (but mainly for abstract super types, generally not for concrete final types) and demonstrates that it is possible to have an API with more compile-time type-safety. – Meno Hochschild Dec 13 '17 at 13:00

1 Answers1

4

It compiles fine because Instant.from(TemporalAccessor temporal) accepts a TemporalAccessor and LocalDateTime is a subclass from TemporalAccessor.

At runtime you get an exception because to create a Instant the TemporalAccessor must have the fields INSTANT_SECONDS and NANO_OF_SECOND, but LocalDateTime doesn't provide INSTANT_SECONDS only NANO_OF_SECOND.

To create an Instant from a LocalDateTime better use (for example):

LocalDateTime localDateTime = LocalDateTime.now();
ZoneId zoneId = ZoneId.systemDefault();
Instant instant = localDateTime.atZone(zoneId).toInstant();

or

LocalDateTime localDateTime = LocalDateTime.now();
Instant instant = localDateTime.toInstant(ZoneOffset.ofHours(0));
  • Definitely, I get that I can do additional checks to make sure that it is in fact possible use different objects from the new time package together. The question is : aren't class hierarchies and types made to prevent such use of the language. To me it's like using a map to code an object : obviously all my object would be compatible, but I would have to do tons of checks to make sure they are actually working together and do not blow at runtime ? I though it was definitely not the "Javaïst" way ? – Antonin Dec 11 '17 at 21:51
  • I guess that `Instant.from(TemporalAccessor)` is less intended to be used with the other `java.time` classes, there a better ways to convert them. It looks more for an attempt to make the new time api dynamic, so you can create your own time object and convert it to all the others, without creating any convert methods. –  Dec 12 '17 at 06:13