Travis Wellman
Travis Wellman

Reputation: 375

Parse ISO timestamp using Java 8 java.time api (standard edition only)

I'm having trouble getting milliseconds from the epoch out of the string in the example. I have tried this three different ways so far, and the example shows the latest attempt. It always seems to come down to that the TemporalAccessor does not support ChronoField. If I could successfully construct an instance of Instant, I could use toEpochMilli().

String dateStr = "2014-08-16T05:03:45-05:00"
TemporalAccessor creationAccessor = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(dateStr);
Instant creationDate = Instant.from(creationAccessor);

Please give concise answers (don't construct a formatter from scratch) and use only the java 8 standard distribution (I can do it with Joda, but want to avoid dependencies).

Edit: Instant.from in the code above throws: java.time.DateTimeException: Unable to obtain Instant from TemporalAccessor: {OffsetSeconds=-18000},ISO resolved to 2014-08-16T05:03:45 of type java.time.format.Parsed

Upvotes: 14

Views: 18937

Answers (4)

Basil Bourque
Basil Bourque

Reputation: 340230

Your Code Works, As Of Java 8 Update 51

Your code is working now, as of Java 8 Update 51 on Mac OS X Mountain Lion. The Answer by Holger that there may have been a bug in earlier versions of Java. Understandable as the java.time framework is brand-new in Java 8.

Here is a modified copy of your code.

String dateStr = "2014-08-16T05:03:45-05:00";
TemporalAccessor creationAccessor = DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse( dateStr );
Instant instant = Instant.from( creationAccessor );
long millisSinceEpoch = instant.toEpochMilli( );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant, ZoneOffset.of( "-05:00" ) );

Dump to console.

System.out.println( "dateStr: " + dateStr );
System.out.println( "instant: " + instant );
System.out.println( " millis: " + millisSinceEpoch );
System.out.println( "    zdt: " + zdt );

When run.

dateStr: 2014-08-16T05:03:45-05:00
instant: 2014-08-16T10:03:45Z
 millis: 1408183425000
    zdt: 2014-08-16T05:03:45-05:00

Canonical Method:
parse(CharSequence text, TemporalQuery<T> query)

You may want to accomplish your parsing using an alternate method.

The class doc for DateTimeFormatter mentions that the usual way to parse should be a call to DateTimeFormatter::parse(CharSequence text, TemporalQuery<T> query) rather than DateTimeFormatter::parse(CharSequence text).

So instead of this:

String input = "2007-12-03T10:15:30+01:00[Europe/Paris]" ;
TemporalAccessor temporalAccessor = DateTimeFormatter.ISO_DATE_TIME.parse( input ) ;

…do this, where we add a second argument, the argument being a method reference in Java 8 syntax, to call the conversion from method (in this example, ZonedDateTime :: from):

String input = "2007-12-03T10:15:30+01:00[Europe/Paris]" ;
ZonedDateTime zdt = DateTimeFormatter.ISO_DATE_TIME.parse( input , ZonedDateTime :: from ) ;

Dump to console.

System.out.println("input: " + input );
System.out.println("  zdt: " + zdt );

When run.

input: 2007-12-03T10:15:30+01:00[Europe/Paris]
  zdt: 2007-12-03T10:15:30+01:00[Europe/Paris]

Upvotes: 11

Holger
Holger

Reputation: 298579

This seems to be a bug which I found in all tested versions before and including jdk1.8.0_20b19 but not in the final jdk1.8.0_20. So downloading an up-to-date jdk version would solve the problem. It’s also solved in the most recent jdk1.9.

Note that the good old Java 7 way works in all versions:

long epochMillis = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX")
                  .parse(dateStr).getTime();

It also supports getting an Instant:

Instant i=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").parse(dateStr).toInstant();
long epochMillis = i.toEpochMilli();

But, as said, a simple update makes your Java 8 code working.

Upvotes: 11

hd1
hd1

Reputation: 34677

Instant.from(creationAccessor).toEpochMili() should sort you, at least according to the ThreeTen javadoc for TemporalInstant. ThreeTen is the reference implementation of javax.time. Let me know if this works for you or not by leaving a comment.

Upvotes: 0

Matt
Matt

Reputation: 11815

Since instant can't properly be represented using a long (they've designed the API to not have Y 2040 issues, when a long is no longer sufficient), you have to use a combination of two methods

getEpochSecond()

and

getNano()

The former gets you the # of seconds from epoch, and the latter gives you the # of nanoseconds that have passed since that same second.

Upvotes: 0

Related Questions