52

Consider a code:

TemporalAccessor date = DateTimeFormatter.ofPattern("yyyy-MM-dd").parse("9999-12-31");
Instant.from(date);

The last line throws an exception:

Unable to obtain Instant from TemporalAccessor: {},ISO resolved to 9999-12-31 of type java.time.format.Parsed

How to create Instant from yyyy-MM-dd pattern?

Do Nhu Vy
  • 33,131
  • 37
  • 143
  • 202
Cherry
  • 25,428
  • 40
  • 160
  • 286
  • 2
    First I would try a year lower then 9999 ;D for example try it with 2016. – kdoteu Jun 07 '16 at 06:38
  • 1
    Use LocalDate.parse(), transform it to LocalDateTime by choosing a time (atStartOfDay?), transform it to a ZonedDateTime by choosing a time zone (does the date represent an instant in France? In the UK? in Japan?), then transform the ZonedDateTime to an Instant. – JB Nizet Jun 07 '16 at 06:45
  • 2
    An instant is, well, an instant. 31-12-9999 is a date but not an instant. So you can either parse it to a LocalDate if a date is all you need, or you will have to specify the missing bits (a time + a zone offset or time zone). – assylias Jun 07 '16 at 06:47
  • Related http://stackoverflow.com/questions/23215299/how-to-convert-a-localdate-to-an-instant – Tunaki Jun 07 '16 at 06:51

4 Answers4

78

The string "9999-12-31" only contains information about a date. It does not contain any information about the time-of-day or offset. As such, there is insufficient information to create an Instant. (Other date and time libraries are more lenient, but java.time avoids defaulting these values)

Your first choice is to use a LocalDate instead of an `Instant:

LocalDate date = LocalDate.parse("9999-12-31");

Your second choice is to post process the date to convert it to an instant, which requires a time-zone, here chosen to be Paris:

LocalDate date = LocalDate.parse("9999-12-31");
Instant instant = date.atStartOfDay(ZoneId.of("Europe/Paris")).toInstant();

Your third choice is to add the time-zone to the formatter, and default the time-of-day:

static final DateTimeFormatter FMT = new DateTimeFormatterBuilder()
    .appendPattern("yyyy-MM-dd")
    .parseDefaulting(ChronoField.NANO_OF_DAY, 0)
    .toFormatter()
    .withZone(ZoneId.of("Europe/Paris"));
Instant instant = FMT.parse("9999-31-12", Instant::from);

(If this doesn't work, ensure you have the latest JDK 8 release as a bug was fixed in this area).

It is worth noting that none of these possibilities use TemporalAccessor directly, because that type is a low-level framework interface, not one for most application developers to use.

JodaStephen
  • 53,561
  • 13
  • 87
  • 108
  • 3
    Contrary to what you've shown the [`parse`](https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html#parse-java.lang.CharSequence-) method of `java.util.Instant` does not take a `DateTimeFormatter`. Instead you have to do `FMT.parse("9999-31-12", Instant::from)`. As the author could you explain why `Instant` is different to the other classes, e.g. `LocalDateTime`, in this respect? – George Hawkins Jan 24 '17 at 12:47
  • Originally, `Instant` did not work with `DateTimeFormatter` at all. This was changed during development, but the method you refer to was not added. That said, because many formatters don't work with `Instant`, there is a case to say that adding such a method would be adding something that would often fail, and annoy users. – JodaStephen Jan 24 '17 at 12:58
  • Thank you! This answer helped me get my Java code to parse ISO 8601 timestamps with no minutes field, ie "2019-06-11T08:00Z" : `Instant.from((new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd'T'HH':'mm'Z'").parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0).parseDefaulting(ChronoField.NANO_OF_SECOND, 0).toFormatter().withZone(ZoneId.of("UTC"))).parse(string));` – M. Leonhard Jun 11 '19 at 01:47
23

The problem isn't the fact that you are using the year 9999. The Instant.MAX field evaluates to the timestamp 1000000000-12-31T23:59:59.999999999Z, so 9999 as a year is fine.

Dealing with TemporalAccessors instead of the more semantically rich types like LocalDateTime or ZonedDateTime is like using a Map to model an object and its properties instead of writing a class -- you have to assure that the value has the fields (like seconds, nanoseconds, etc) that are expected by something receiving it, rather than depending on formally declared operations in a higher level class to prevent dependencies from going unmet.

In your case it is likely that the temporal accessor contained the parsed date fields it was given, but didn't have a "seconds" field that the Instant needed. It is best to use the more semantically rich types like LocalDateTime in most instances.

Since you only have date fields, you should parse it as a date, then add the time fields before converting it to an Instant. Here is one way, using LocalDate to parse the date:

LocalDate localDate = LocalDate.parse("2016-04-17");
LocalDateTime localDateTime = localDate.atStartOfDay();
Instant instant = localDateTime.toInstant(ZoneOffset.UTC);
Hank D
  • 5,654
  • 2
  • 20
  • 31
6
public static void main(String[] args) throws ParseException {
        System.out.println(new SimpleDateFormat("yyyy-MM-dd").parse("2016-12-31").toInstant());
}

the above code gives the following output:

2016-12-31T00:00:00Z

i have answered this question using features('toInstant' method) of java 8. hope this answers your question...

Abhishek
  • 2,345
  • 2
  • 16
  • 24
  • 5
    That code sets a lot of things arbitrarily (and mixes the legacy Date API with the new Java time API)... – assylias Jun 07 '16 at 06:56
  • @assylias yes the 'toInstant()' method is available only in the latest version of java. will mention in my answer. – Abhishek Jun 07 '16 at 07:01
  • @downvoter could you care to explain yourself. may i know the reason for the down vote. it would help me improve my answer. – Abhishek Jun 07 '16 at 07:10
  • 2
    I think the reason for the downvote is what I said above: (i) you are unnecessarily mixing API, which is confusing and (ii) the output of your example depends on the user time zone (if you are in Hawaii, it will print `2016-12-31T10:00:00Z` for example). – assylias Jun 07 '16 at 07:22
3

Either you are only interested in the date itself (31st of December 9999), in which case the appropriate type would be a LocalDate:

LocalDate date = LocalDate.parse("9999-12-31");

Or you do want an Instant, in which case you need to set a time and time zone, for example, 00:00 in Tokyo:

Instant instant = date.atStartOfDay(ZoneId.of("Asia/Tokyo")).toInstant();
assylias
  • 297,541
  • 71
  • 621
  • 741