Java 8 epoch-millis time stamp to formatted date, how?

11,277

Solution 1

An Instant does not contain any information about the time-zone, and unlike in other places, the default time-zone is not automatically used. As such, the formatter cannot figure out what the year is, hence the error message.

Thus, to format the instant, you must add the time-zone. This can be directly added to the formatter using withZone(ZoneId) - there is no need to manually convert to ZonedDateTime *:

ZoneId zone = ZoneId.systemDefault();
DateTimeFormatter df = DateTimeFormatter.ofPattern("...pattern...").withZone(zone);
df.format(Instant.ofEpochMilli(timestamp))

* regrettably, in early Java 8 versions, the DateTimeformatter.withZone(ZoneId) method did not work, however this has now been fixed, so if the code above doesn't work, upgrade to the latest Java 8 patch release.

Edit: Just to add that Instant is the right class to use when you want to store an instant in time without any other context.

Solution 2

The error you have when formatting an Instant using a formatter built with a year or other fields is expected; an Instant does not know which year or month or day it is, it only knows how much milliseconds have elapsed since the Epoch. For the same instant, it could be 2 different days on 2 different places of the Earth.

So you need to add a time zone information if you want to print the day. With an Instant, you can call atZone(zone) to combine it with a ZoneId in order to form a ZonedDateTime. This is very much like an instant, only that it has a time zone information. If you want to use the system time zone (the one of the running VM), you can get it with ZoneId.systemDefault().

To print it, you can use the two built-in formatter ISO_OFFSET_DATE_TIME or ISO_ZONED_DATE_TIME. The difference between the two is that the zoned date time formatter will add the zone id to the output.

Instant instant = Instant.now();
DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
System.out.println(formatter.format(instant.atZone(ZoneId.systemDefault())));
System.out.println(formatter.format(instant.atZone(ZoneId.of("America/Los_Angeles"))));

when run on my machine, which has a system time zone of "Europe/Paris", you'll get:

2016-07-31T18:58:54.108+02:00
2016-07-31T09:58:54.108-07:00

You can of course build your own formatter if those one do not suit you, using ofPattern or the builder DateTimeFormatterBuilder.

Solution 3

I agree that this is somewhat confusing, especially when compared with it's predecessor Joda DateTime.

The most confusing thing is that the documentation for LocalDateTime says that it is "A date-time without a time-zone", and yet LocalDateTime.ofInstant method takes both an instant and a timezone as parameters.

That said, I think that you can achieve what you want by using Instant and LocalDateTime.ofInstant by using the UTC timezone.

public LocalDateTime millisToDateTime(long millis) {
  return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of("Z");
} 
Share:
11,277

Related videos on Youtube

Harald
Author by

Harald

formerly Enterprise Search Engineer now financial markets

Updated on September 15, 2022

Comments

  • Harald
    Harald over 1 year

    Before Java-8 I got accustomed to always keep anything date/time related as milliseconds since Epoch and only ever deal with human readable dates/times on the way out, i.e. in a UI or a log file, or when parsing user generated input.

    I think this is still safe with Java-8, and now I am looking for the most concise way to get a formatted date out of a milliseconds time stamp. I tried

    df = Dateformatter.ofPattern("...pattern...");
    df.format(Instant.ofEpochMilli(timestamp))
    

    but it bombs out with Unsupported field: YearOfEra in Instant.getLong(...) which I half understand. Now what to use instead of Instant?

    LocalDateTime.ofEpoch(Instant, ZoneId) seems wrong, since I don't care to have local time. I just want to see the local time zone when applying the formatter. Internally it should be just the Instant.

    The same goes for ZonedDateTime.ofInstant(Instant, ZoneId), I thought to apply the ZoneId only when formatting. But I notice that the DateTimeFormatter does not itself deal anymore with time zones, it seems, so I reckon I need to use one of the above.

    Which one is preferred and why? Or should I use yet another way to format an epoch-millis time stamp as a date/time with time zone?