I have a String of this format: "2019-08-17T09:51:41.775+00:00". I need to convert it into Epoch microseconds, but my conversion is always an hour off.
This is my code at the moment:
String timestamp = "2019-08-17T09:51:41.775+00:00"
ZonedDateTime date = ZonedDateTime.parse(timestamp)
Long epoch = date.toInstant().toEpochMilli() * 1000
So when I run this code, I get the result 1566035501775000. Now if I put 1566035501775 in a converter like this one: https://www.freeformatter.com/epoch-timestamp-to-date-converter.html to reverse it, I get this date: 8/17/2019, 10:51:41 AM.
Why is it an hour off and how can I change that?
I also tried it with a DateTimeFormatter:
DateTimeFormatter pat = DateTimeFormatter.ofPattern("YYYY-MM-dd'T'hh:mm:ss.SSSXXX")
Instant.from(pat.parse(timestamp))
But this gives me this exception:
java.time.DateTimeException: Unable to obtain Instant from TemporalAccessor: {SecondOfMinute=41, MicroOfSecond=775000, DayOfMonth=17, HourOfAmPm=9, NanoOfSecond=775000000, MonthOfYear=8, OffsetSeconds=0, MinuteOfHour=51, MilliOfSecond=775, WeekBasedYear[WeekFields[SUNDAY,1]]=2019},ISO of type java.time.format.Parsed
at java.time.Instant.from(Instant.java:378)
at com.amazon.hailstonetracing.controller.chrome.WorkflowEventMapperTest.test(WorkflowEventMapperTest.kt:114)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: InstantSeconds
at java.time.format.Parsed.getLong(Parsed.java:203)
at java.time.Instant.from(Instant.java:373)
... 24 more
After researching I presume this is because I didn't define a ZoneId? I only have the offset from the timestamp String. But I get the same result if I specify .withZone(ZoneId.of("UTC"))
on the DateTimeFormatter
.
Thanks for your help!
Your microsecond value is correct.
What happens is: The freeformatter.com Epoch & Unix Timestamp Converter detects your time zone and gives you your local time, 10:51:41 AM. Apparently you are (or your browser thinks that you are) in a time zone that is at offset +01:00 from UTC. It therefore adds an hour compared to the time in your string, which was specified to be at offset +00:00.
I am in Europe/Copenhagen time zone, currently at offset +02:00, and when I try the same on freeformatter.com, I get 17.8.2019 11.51.41, the correct local time for my time zone.
In your attempt with an explicit formatter you have the wrong case of a few of your format pattern letters. Since it works without constructing your own formatter, that’s certainly the solution I recommend.
And you need no time zone ID. The offset in your string is fully sufficient.
Since your string has an offset (+00:00) and no time zone (like for example Pacific/Tarawa) using ZonedDateTime
is overkill. I suggest using OffsetDateTime
instead. It goes in the same way:
OffsetDateTime date = OffsetDateTime.parse(timestamp);
long epoch = date.toInstant().toEpochMilli() * 1000;
I also changed the type of epoch
from a Long
object to a primitive long
.
As an aside, had your string had a finer precision than milliseconds, it would have been lost in your conversion. Here’s a way to get microsecond precision through:
String timestamp = "2019-08-17T09:51:41.7754321+00:00";
OffsetDateTime date = OffsetDateTime.parse(timestamp);
Instant asInstant = date.toInstant();
long epoch = TimeUnit.SECONDS.toMicros(asInstant.getEpochSecond())
+ asInstant.get(ChronoField.MICRO_OF_SECOND);
System.out.println(epoch);
As you can see, I have added more decimals in your string for the demonstration. Output is:
1566035501775432