Java 使用此选项,调用并且不执行任何时区转换

Java 使用此选项,调用并且不执行任何时区转换,java,sql,date,jdbc,timezone,Java,Sql,Date,Jdbc,Timezone,H2 JDBC驱动程序返回get(columnIndex).getDate()。返回指定列的H2。由于列类型为DATE,因此H2使用。调用,在时区转换后最终创建java.sql.Date 使用这种方法,代码要简单得多;它只是使用存储在数据库中的longDate值返回一个java.sql.Date 因此,我们看到H2 JDBC驱动程序在处理带日期的时区转换方面非常聪明,而SQLite JDBC驱动程序则不然(不是说这个决定不聪明,它可能很适合SQLite设计决策)。如果您查找您提到的其他RDBMS

H2 JDBC驱动程序返回
get(columnIndex).getDate()
。返回指定列的H2。由于列类型为
DATE
,因此H2使用。调用,在时区转换后最终创建
java.sql.Date

使用这种方法,代码要简单得多;它只是使用存储在数据库中的
long
Date值返回一个
java.sql.Date

因此,我们看到H2 JDBC驱动程序在处理带日期的时区转换方面非常聪明,而SQLite JDBC驱动程序则不然(不是说这个决定不聪明,它可能很适合SQLite设计决策)。如果您查找您提到的其他RDBMS JDBC驱动程序的源代码,您可能会发现大多数驱动程序都以与H2相似的方式接近日期和时区

虽然JDBC规范没有详细说明时区处理,但RDBMS和JDBC实现设计者考虑了时区并将正确处理它是有道理的;尤其是如果他们想让自己的产品在全球市场上畅销的话。这些设计师非常聪明,即使在没有具体规范的情况下,他们中的大多数人也能做到这一点,我并不感到惊讶


我发现了这个Microsoft SQL Server博客,它解释了时区如何使事情复杂化:

时区是一个复杂的领域,每个应用程序都需要解决如何处理时区数据,以使程序更加用户友好

不幸的是,目前没有时区名称和值的国际标准机构。每个系统都需要使用自己选择的系统,在没有国际标准之前,尝试让SQL Server提供一个系统是不可行的,最终会导致比解决问题更多的问题


java.util.Date和Oracle/MySQL日期对象都只是一个时间点的表示,与位置无关。这意味着它很可能在内部存储为自“纪元”(GMT 1970-01-01 00:00)以来的毫秒/纳秒数

从resultset读取时,对“rs.getDate()”的调用告诉resultset获取包含时间点的内部数据,并将其转换为java日期对象。这个日期对象是在您的本地机器上创建的,所以Java将选择您的本地时区,即苏黎世的CET


你所看到的差异是代表性上的差异,而不是时间上的差异。

我认为时区问题过于复杂了。将日期/时间传递给prepared语句时,它只向数据库服务器发送日期和/或时间信息。根据事实上的标准,它不会操纵数据库服务器和jvm之间的时区差异。然后从结果集中读取日期/时间。它只是从数据库中读取日期时间信息,并在jvm时区中创建相同的日期/时间。

JDBC 4.2和java.time 我没有进行测试,但除非JDBC驱动程序中存在错误,否则所有时区的输出都是:

1970-01-01

LocalDate
是一个没有时间和时区的日期。因此,没有毫秒值可获取,也没有时间可混淆
LocalDate
是现代java日期和时间API java.time的一部分。JDBC 4.2指定您可以通过代码段中使用的方法
setObject
getObject
将java.time对象(包括
LocalDate
getDate
)传输到SQL数据库或从SQL数据库传输java.time对象

我认为现在几乎所有人都在使用JDBC4.2驱动程序

你的代码出了什么问题?
java.sql.Date
是一种试图(不是很成功)将
java.util.Date
伪装成没有时间的日期的黑客。我建议您不要使用该类

从文件中:

为符合SQL
DATE
的定义,毫秒值 由
java.sql.Date
实例包装的必须通过设置 中到零的小时、分钟、秒和毫秒 实例关联的特定时区

“关联”可能是模糊的。我认为,这意味着毫秒值必须表示JVM默认时区(我想在您的情况下是欧洲/苏黎世)中一天的开始。因此,当您创建
新java.sql.Date(0)
等于GMT 1970-01-01 00:00:00时,实际上是您创建了一个不正确的
Date
对象,因为这个时间在您的时区是01:00:00。JDBC忽略了一天中的时间(我们可能预期会出现错误消息,但没有收到任何错误消息)。所以SQL获取并返回的日期是1970-01-01。JDBC正确地将其翻译回包含CET 1970-01-01 00:00:00的日期。或毫秒值-360000。是的,令人困惑。正如我所说的,不要使用那个类

如果您的GMT偏移量为负(例如美洲的大多数时区),
new java.sql.Date(0)
将被解释为1969-12-31,因此这将是您传递到sql并从sql返回的日期

更糟糕的是,想想当JVM的时区设置改变时会发生什么。您的程序的任何部分以及在同一JVM中运行的任何其他程序都可以随时执行此操作。这通常会导致在更改之前创建的所有
java.sql.Date
对象无效,有时可能会将其日期更改一天(在极少数情况下更改两天)

链接
  • 解释如何使用java.time

我不太确定,但我看到的唯一一件事是,在调用getTime()方法时,它被转换为当前时区。@newer\u at\u work:这是怎么回事
newjava.sql.Date(0.getTime()
产生
0
。那里没有时区问题。。。?转化
public Date(long date) {
    // If the millisecond date value contains time info, mask it out.
    super(date);

}
Calendar gmt = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setDate(1, new Date(0)); // I assume this is always GMT
ResultSet rs = stmt.executeQuery();
rs.next();

//This will output 0 as expected
System.out.println(rs.getDate(1, gmt).getTime());
Calendar gmt = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
PreparedStatement stmt = connection.prepareStatement(sql);
gmt.setTimeInMillis(0) ;
stmt.setDate(1, gmt.getTime()); //getTime returns a Date object
ResultSet rs = stmt.executeQuery();
rs.next();

//This will output 0 as expected
System.out.println(rs.getDate(1).getTime());
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setObject(1, LocalDate.EPOCH); // The epoch year LocalDate, '1970-01-01'.
ResultSet rs = stmt.executeQuery();
rs.next();

// It doesnt matter where you live or your JVM’s time zone setting
// since a LocalDate doesn’t have or use time zone
System.out.println(rs.getObject(1, LocalDate.class));