Question

I've found an number of posts here on Stack Overflow and mailing lists documenting the MySQL JDBC issue with timezones, datetimes, etc. But even with all that I still can't figure this out. I'm using a connection string like the following:

 jdbc:mysql://localhost:3306/BigSense?useLegacyDatetimeCode=false&serverTimezone=UTC

I insert the date using the following:

 stmt.setTimestamp(x, s, Calendar.getInstance(TimeZone.getTimeZone("UTC")))

And the date does correctly get stored in MySQL in a DateTime column in UTC time. I can do a SELECT from the console and see it in the time format I sent it to the server as. The trouble is when I do a SELECT within Java/JDBC, it converts it to my local timezone for some reason! I use Scala code like the following:

        using(stmt.getResultSet()) {
      ret =>
        if (ret != null) {

          val meta = ret.getMetaData()

          var retbuf = new ListBuffer[Map[String, Any]]()
          while (ret.next) {
            val rMap = scala.collection.mutable.Map[String, Any]()

            for (i <- 1 to meta.getColumnCount()) {
              rMap += (meta.getColumnLabel(i) -> ret.getObject(i))
            }

It's old Scala code, so don't judge me. I realize I shouldn't be using a retval and there are more "Scala" ways to write this :)

Anyway, I've tried variation where I check to see if it's specifically the "time" column and use getTimestamp instead, both with and without the Calendar object option and I still get the time translated to local!

This is for the BigSense project where I'm trying to support multiple databases (currently have Postgres and MS SQL fully supported) and so I'm trying to keep the code as generic/agnostic as possible. The full source for my DB stuff can be found here:

https://github.com/sumdog/BigSense/blob/master/src/main/scala/io/bigsense/db/DataHandlerTrait.scala

Oh and I've also tried the following:

 noTimezoneConversionForTimeType=true

and still get the same result. My local machine is setup as NZST, so it complains if I leave out the the "serverTimezone=UTC" from the JDBC URL. Inserts are working fine, it the SELECT that are coming back converted when they shouldn't.

Was it helpful?

Solution

The Java time stuff is bizarre. Timestamps don't have a Timezone you can set...but they do have a Timezone offset...that affects the toString...but that can't be changed...without converting out to a string and back.

Joda-time seems to fix some of these issues, but also would take a bit to implement and I'm sure it'd cause additional issues.

My PostgreSQL and Microsoft SQL JDBC drivers don't add a timezone at all to the returned Timestamp, by MySQL does (or at least an offset that affects the way toString renders the Timestamp). I finally got this solution working:

                for (i <- 1 to meta.getColumnCount()) {
              rMap += (meta.getColumnLabel(i) -> (ret.getObject(i) match {
                case null => null
                case ts : Timestamp  => {
                  if(dbDialect == DB_MYSQL) {
                    //Ensure UTC (MySQL is the only driver that has trouble with this)
                    val dateFormatGmt = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss.SSS")
                    dateFormatGmt.setTimeZone(TimeZone.getTimeZone("UTC"))
                    dateFormatGmt.format(ts)
                  }
                  else ts
                }
                case x:Any => x
              }))
            }

It's pretty ugly, and is in place because of the non-standard and somewhat buggy/technical debt ridden MySQL JDBC implementation. But it seems to work.

OTHER TIPS

JVM is set in such a way that any "Date-Time" object you take from the DataBase, it will convert that object into the corresopding local Date and time.

So suppose if there is a Timestap with Zone Column in your Database, and it is set in lets say MST time, so if I am running my application India, then everytime when the application will fetch the data from the DB using Spring Data JPA or JDBC template, it will add 12 hours and 30 minutes to that Date-Time data, and so it will create a big mess.

If you are using a Spring Boot application, then use the following code. You need to set your application in the same timezone as your DataBase "Timestamp" column is : -

@SpringBootApplication
public class SampleSpringBootApplication {

    public static void main(String[] args) {
        this.init();
        SpringApplication.run(SampleSpringBootApplication.class, args);
    }

    @PostConstruct
    public void init(){

      TimeZone.setDefault(TimeZone.getTimeZone("MST"));
    }}

Here, it is very much important to run the init() method before the execution of the Spring Boot application, you can put this function inside a Config class as well, that worked with JPA but was not working with JDBC template.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top