java 防止MySQL JDBC驱动程序将DateTime转换为本地时间

rpppsulh  于 2023-01-15  发布在  Java
关注(0)|答案(2)|浏览(199)

我在Stack Overflow和邮件列表上找到了很多文章,记录了MySQL JDBC在时区、日期时间等方面的问题。但即使如此,我仍然无法解决这个问题。

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

我使用以下格式插入日期:

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

而且日期确实正确地存储在MySQL中的一个UTC时间的DateTime列中。我可以从控制台执行SELECT,并以我发送给服务器的时间格式查看它。问题是当我在Java/JDBC中执行SELECT时,由于某种原因,它将其转换为我的本地时区!我使用的Scala代码如下:

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))
            }

这是旧的Scala代码,所以不要批评我。我意识到我不应该使用retval,还有更多的“Scala”方法来编写:)
无论如何,我已经尝试了变化,我检查,看看它是否是专门的“时间”列,并使用getTimestamp代替,无论有或没有日历对象选项,我仍然得到时间转换为本地!
这是我在BigSense项目中尝试支持多个数据库(目前完全支持Postgres和MS SQL),因此我尝试尽可能保持代码的通用性/不可知性。

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

哦,我还尝试了以下方法:

noTimezoneConversionForTimeType=true

我的本地机器设置为NZST,所以如果我从JDBC URL中遗漏了“serverTimezone=UTC”,它会抱怨。插入工作正常,是返回的SELECT在不应该转换的时候转换了。

qojgxg4l

qojgxg4l1#

Java时间的东西很奇怪,时间戳没有一个时区可以设置......但是它们有一个时区偏移......这会影响到toString......但是这是不能改变的......除非转换成一个字符串,然后再转换回来。
Joda-time似乎解决了其中的一些问题,但也需要一点时间来实现,而且我确信它会导致更多的问题。
我的PostgreSQL和Microsoft SQL JDBC驱动程序根本不向返回的Timestamp添加时区,而MySQL会添加时区(或者至少添加一个偏移量,影响toString呈现Timestamp的方式)。

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
              }))
            }

这是相当丑陋的,而且是因为非标准的和有点bug/技术债务缠身的MySQL JDBC实现。

8gsdolmq

8gsdolmq2#

JVM是这样设置的,任何从数据库中获取的“日期-时间”对象,它都会将该对象转换为相应的本地日期和时间。
假设您的数据库中有一个带Zone Column的Timestap,并且它设置为MST时间,那么如果我正在运行我的应用程序India,那么每当应用程序使用Spring Data JPA或JDBC模板从数据库中获取数据时,它将向该日期-时间数据添加12小时30分钟,因此它将产生一个大混乱。
如果您使用的是Sping Boot 应用程序,那么请使用以下代码。您需要将应用程序设置在与数据库“Timestamp”列相同的时区中:-

@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"));
    }}

在这里,在执行Sping Boot 应用程序之前运行init()方法非常重要,您也可以将此函数放在Config类中,该类可用于JPA,但不用于JDBC模板。

相关问题