tl;dr
myPreparedStatement.setObject( // Pass java.time objects directly to database, as of JDBC 4.2.
… , // Indicate which placeholder in your SQL statement text.
OffsetDateTime.parse( // Parse input string as a `OffsetDateTime` as it indicates an offset-from-UTC but not a time zone.
"20180531_132001Z" , // Define a formatting pattern to match your particular input.
DateTimeFormatter.ofPattern( "uuuuMMdd_HHmmssX" ) // TIP: When exchanging date-time values as text, use use standard ISO 8601 formats rather than inventing your own.
) // Returns a `OffsetDateTime` object.
.toInstant() // Returns a `Instant` object, always in UTC by definition.
)
Details
There is some helpful information in the other Answers, but all of them have some misinformation which I tried to correct by posting comments.
Most importantly, your code is using the wrong Java class and the wrong database data type for that given input.
Below is explanation along with a complete code example, using the modern java.time classes with JDBC 4.2 or later.
Z
= UTC
DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss'Z'")
Never put single-quotes around vital parts of your input such as you did here with Z
. That Z
means UTC and is pronounced “Zulu”. It tells us the text of the date and time-of-day should be interpreted as using the wall-clock time of UTC rather than, say, America/Montreal
or Pacific/Auckland
time zones.
Do not use the LocalDateTime
for such inputs. That class lacks any concept of time zone or offset-from-UTC. As such, this class does not represent a moment, and is not a point on the timeline. A LocalDateTime
represents the set of potential moments along a range of about 26-27 hours (across all time zones). Use LocalDateTime
when you mean any or all time zones rather than one particular zone/offset. In contrast, the Z
tells us this input uses the wall-clock time of UTC specifically.
Parsing
Define a formatting pattern to match all important parts of your input string.
String input = "20180531_132001Z" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuuMMdd_HHmmssX" ) ;
By the way, whenever possible, use standard ISO 8601 formats rather than a custom format as seen in your Question. Those formats are wisely designed to be easy to parse by machine and easy to read by humans across cultures while eliminating ambiguity.
Parse as a OffsetDateTime
because your input indicates an offset-from-UTC (of zero hours). An offset-from-UTC is merely a number of hours and minutes, nothing more, nothing less.
Use the ZonedDateTime
class only if the input string indicates a time zone. A time zone has a Contintent/Region
name such as Africa/Tunis
. A zone represents the history of past, present, and future changes in the offset used by the people of a particular region.
OffsetDateTime odt = OffsetDateTime.parse( input , f ) ;
odt.toString(): 2018-05-31T13:20:01Z
Database
To communicate this moment to a database using JDBC 4.2 and later, we can directly pass the java.time object.
myPreparedStatement.setObject( … , odt ) ;
If your JDBC driver does not accept the OffsetDateTime
, extract the simpler class Instant
. An Instant
is in UTC always, by definition.
Instant instant = odt.toInstant() ;
myPreparedStatement.setObject( … , instant ) ;
And retrieval.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
Beware - Wrong datatype in your database
I am not a MS SQL Server user, but according to this documentation, the column data type DATETIME2
is not appropriate to your input. That data type seems to be equivalent to the SQL-standard type DATETIME WITHOUT TIME ZONE
. Such a type should never be used when recording a specific moment in history.
Lacking any concept of time zone or offset-from-UTC, that column type should only be used for three situations:
- The zone or offset is unknown.
This is bad. This is faulty data. Analogous to having a price/cost without knowing the currency. You should be rejecting such data, not storing it.
- The intention is “everywhere”, as in, every time zone.
Example, a corporate policy that states “All our factories will break for lunch at 12:30" means the factory in Delhi will break hours before the factory in Düsseldorf which breaks hours before the factory in Detroit.
- A specific moment in the future is intended, but we are afraid of politicians redefining the time zone.
Governments change the rules of their time zones with surprising frequency and with surprisingly little warning (even [no warning at all][10]). So if you want to book an appointment at 3 PM on a certain date, and you really mean 3 PM regardless of any crazy decision a government might make in the interim, then store a LocalDateTime
. To print a report or display a calendar, dynamically apply a time zone (ZoneId
) to generate a specific moment (ZonedDateTime
or Instant
). This must be done on-the-fly rather than storing the value.
Since your input is a specific moment, a certain point on the timeline, you should be storing it in the database using a column type akin to the SQL-standard type TIMESTAMP WITH TIME ZONE
.
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.