我在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代码,所以不要评判我。我意识到我不应该使用检索,有更多的"Scala"方法来编写它:)
无论如何,我已经尝试了变化,我检查,看看它是否是具体的"时间"列,并使用getTimestamp代替,无论是有和没有日历对象选项,我仍然得到时间翻译为本地!
这是BigSense项目,我试图支持多个数据库(目前有Postgres和MS SQL完全支持),所以我试图保持代码的通用/不可知论尽可能。我的DB材料的完整源代码可以在这里找到:
https://github.com/sumdog/BigSense/blob/master/src/main/scala/io/bigsense/db/DataHandlerTrait.scala
哦,我还试过以下方法:
noTimezoneConversionForTimeType=true
,仍然得到相同的结果。我的本地机器设置为NZST,所以如果我从JDBC URL中遗漏了"serverTimezone=UTC",它就会报错。插入工作良好,如果SELECT返回转换时,他们不应该。
Java的时间很奇怪。时间戳没有可以设置的时区…但他们确实有时区偏移…会影响toString…但这是无法改变的……不需要转换成字符串再转换回来。
Joda-time似乎解决了其中的一些问题,但也需要一点时间来实现,我相信它会导致额外的问题。
我的PostgreSQL和Microsoft SQL JDBC驱动程序根本不会在返回的时间戳中添加时区,而MySQL会(或者至少会影响toString呈现时间戳的方式)。我终于得到了这个解决方案: 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实现而存在。但这似乎是有效的。
JVM是这样设置的:任何" date - time ";对象,它会将该对象转换为相应的本地日期和时间。
所以假设在你的数据库中有一个时区列的时间戳,并且它被设置为MST时间,所以如果我正在运行我的应用程序印度,那么每次当应用程序使用Spring data JPA或JDBC模板从DB获取数据时,它将添加12小时30分钟到该日期-时间数据,因此它将创建一个大混乱。
如果您正在使用Spring Boot应用程序,则使用以下代码。您需要将应用程序设置在与数据库"时间戳"相同的时区。列为:-
@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"));
}}
在这里,在执行Spring Boot应用程序之前运行init()
方法是非常重要的,您也可以将此函数放在Config类中,它可以与JPA一起工作,但不能与JDBC模板一起工作。