I'm trying to parse an offset time using Java 8 DateTimeFormatter.
I live in EST time which is UTC-5, so when I try to convert
2019-01-22T13:09:54.620-05:00 should be --> 2019-01-22T18:09:54.620
However, with my code, it gets the current time and goes back 5 hours, resulting in 2019-01-22 08:09:54.620
Code:
import java.sql.Timestamp
import java.time._
import java.time.format.DateTimeFormatter
import scala.util.{Failure, Success, Try}
class MyTimeFormatter(parser: DateTimeFormatter) {
def parse(input: String): Try[Timestamp] = {
Try(new Timestamp(Instant.from(parser.withZone(ZoneOffset.UTC).parse(input)).toEpochMilli))
}
}
Test:
new MyTimeFormatter(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSxxx")).parse("2019-01-22T13:09:54.620-05:00") shouldEqual Timestamp.valueOf("2019-01-22T18:09:54.620")
where parser is of type DateTimeFormatter
and input string is just "2019-01-22T13:09:54.620-05:00"
I want to use this parser.parse
method and not with specific temporalAccessors like OffsetDateTime.parse(input, parser)
so I can handle all cases like LocalTime, LocalDateTime, ZonedDateTime, OffsetDateTime, etc..
It seems like the code just grabs the time, subtracts the offset, and brands it as UTC instead of calculating the offset with respect to UTC.
Also, is there a way to apply this UTC conversion only if the input format is of ZonedDateTime/OffsetDateTime format? If I input a LocalDateTime (which doesn't have an offset) such as 2017-01-01 12:45:00
the parser will still apply the UTC offset conversion because I told the parser to parse with zone UTC.
While I cannot reproduce your issue precisely (even with changing my clock to EST), this is what I am observing:
This is producing the time you would expect (2019-01-22T18:09:54.620Z).
Because this is based on
java.util.Date
, which displays as your local time.A better way to convert an
Instant
to aTimestamp
is via theLocalDateTime
, like so:tl;dr
Use modern java.time classes. Convert to legacy class only if necessary to work with old code.
Specifically, parse your input string as a
OffsetDateTime
object, adjust to UTC by extracting anInstant
, and lastly, convert tojava.sql.Timestamp
(only if you must).See this code run live at IdeOne.com, resulting in:
If using JDBC 4.2 or later, skip the
Timestamp
altogether.Zulu
If you meant that second value to represent a moment in UTC, append the offset-from-UTC to indicate that fact. Either
+00:00
orZ
(pronounced “Zulu”):2019-01-22T18:09:54.620Z
.Reporting a moment without an offset-from-UTC or time zone indicator is like reporting an amount of money without a currency indicator.
OffsetDateTime
A string with an offset-from-UTC should be parsed as a
OffsetDateTime
object.Your input string happens to comply with the ISO 8601 standard formats for textual date-time values. The java.time classes use ISO 8601 formats by default when parsing/generating strings. So no need to specify a formatting pattern.
Timestamp
Apparently you want a
java.sql.Timestamp
object. This is one of the terrible date-time classes bundled with the earliest versions of Java. These classes are now legacy, supplanted entirely by the modern java.time classes with the adoption of JSR 310. Avoid these legacy classes whenever possible.If you must have a
Timestamp
to interoperate with old code not yet updated to work with java.time, you can convert. To convert, call new methods added to the old classes.Instant
The
java.sql.Timestamp
class carries afrom( Instant )
method. AnInstant
is a moment in UTC. To adjust from the offset of ourOffsetDateTime
to UTC, just extract anInstant
.We have three objects (
odt
,instant
, &ts
) that all represent the same moment. The first has a different wall-clock time. But all three are the same simultaneous point on the timeline.JDBC 4.2
As of JDBC 4.2, we can directly exchange java.time objects with the database. So no need to use
Timestamp
.…and…
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as
java.util.Date
,Calendar
, &SimpleDateFormat
.The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for
java.sql.*
classes.Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as
Interval
,YearWeek
,YearQuarter
, and more.