将java.sql.Timestamp保存到mysql datetime列会更改时间

我正在研究使用MySQL数据库的Spring Boot REST API。我正在尝试保存从客户端应用“按原样”接收的日期(含时间),以UTC时间表示。基本上,已经达成共识,日期将以UTC来回发送,并且将在客户端上完成到适当时区的转换,因此我已经在JSON中有了UTC时间,并且我试图将其保存到DATETIME列中在MySQL中。

我之所以选择DATETIME,是因为它在MySQL reference中说TIMESTAMP会转换值,而DATETIME不会,这正是我所需要的。

我创建了所有必需的实体来映射我的表,from reference又一次,我已经看到DATETIME映射到java.sql.Timestamp,所以这就是我用作类型的东西。

我正在使用JpaRepository并调用它的save方法来保存我的实体。 会发生什么:当我将日期保存到MySQL时,它们会转换为UTC (我想是因为我在UTC + 1中,并且日期比我插入的日期早一小时)。

我已经阅读了关于SO的几个答案,并尝试在jdbc连接字符串中使用以下属性(如建议的here):noDatetimeStringSync=trueuseLegacyDatetimeCode=false,{{1 }},但没有一个起作用。

我不知道春天是否会对此造成困扰?

这是我在请求中收到的JSON的一部分:

sessionVariables=time_zone='-00:00'

我的实体具有以下属性:

{
 "dateFrom": "2019-12-01 16:00:00","dateTo": "2019-12-01 16:30:00"
}

db中的表列:

import java.sql.Timestamp;
...
private Timestamp dateFrom;
private Timestamp dateTo;

当我将String转换为Timestamp并调用save()方法时,实体内部具有正确的值。因此,JpaRepository和db本身之间的某些东西弄乱了日期。 UPDATE: 时间戳实际上不是正确的值,它在从String转换时添加了date_from datetime date_to datetime ZoneInfo with ID="Europe/Prague"

我最终得到的是数据库中的zoneOffset=36000002019-12-01 15:00:00

我想要的是完全按照收到日期的方式存储这些日期。我什至考虑过改用VARCHAR,因为我很沮丧,但是我不想这样做,因为我需要根据日期等执行查询。

有什么办法可以实现我想要的?乍一看似乎很简单,但是现在确实让我发疯。

[如果需要,还可以提供其他信息]我正在使用:

  • MySQL 5.7.27
  • Spring Boot(starter-parent)2.2.0.RELEASE
  • mysql-connector-java

编辑:

我可能会以错误的方式进行操作,因此,如果您可以提出如何以不同的方式进行操作的建议,那就太好了。 要点是:我的应用程序需要为不同时区的用户工作,并且他们需要通过日期相互通信。因此,如果欧洲/布拉格时区的一位用户对美国/芝加哥的另一位用户说“明天下午5点让我们来谈谈”,那么我需要以一种可以在当地时间为美国用户转换的方式来存储此信息(但对于任何其他时区的任何其他用户)。这就是为什么我选择将日期存储在UTC中,然后在客户端将其转换为用户的本地时间的原因。但是显然我对这一切的运作方式有误解。

qiaoshai 回答:将java.sql.Timestamp保存到mysql datetime列会更改时间

感谢deHaar和其他在评论中做出贡献的人,我终于按照我的意图使它工作了,所以我将发布摘要性答案,以防其他人可能需要它。 / p>

关键是在JSON请求/响应中使用标准化的日期字符串,以及正确的Java 8 DateTime对象。我最初发送的内容还不够。

因此,根据ISO-8601日期和时间格式:

  

时间以UTC(世界标准时间)表示,带有特殊的UTC代号(“ Z”)。

     

请注意,按照ISO 8601的规定,“ T”字面上出现在字符串中,以指示时间元素的开始。

意思是说,我的API应该仅以这些标准化的UTC日期字符串进行通讯,而不能使用自定义日期字符串的内容进行交流, :

{
    "dateFrom": "2019-12-01T16:00:00Z","dateTo": "2019-12-01T16:30:00Z"
}

正如评论中提到的,我所做的另一件事是将db时区设置为UTC,并且没有任何机会。自从我使用Spring Boot以来,有两种方法可以做到这一点:

  1. application.properties中的set属性: spring.jpa.properties.hibernate.jdbc.time_zone=UTC

  2. 或在jdbc连接字符串中: serverTimezone=UTC

我还在上面的字符串中添加了useLegacyDatetimeCode=false参数,因为我读到没有它,serverTimezone参数就没有效果(如果我输入错了,有人可以自由地纠正我)关于这个)。

现在,对于java.sql.Timestamp是否具有时区信息存在一些争论。我仔细阅读了SO的更多内容,结果发现以前的版本(在Java 8之前)确实没有时区信息(例如here)。但是,正如我在调试器中清楚看到的那样,Timestamp对象具有单独存储的区域信息,但是确实有。

根据评论中的Mark Rotteveel

  

java.sql.Timestamp按规范表示默认JVM时区中的时间

这很有意义,因为当我更改JVM时区时,Timestamp值也更改了。

现在,要解决此问题,我看到了很多建议将默认时区设置为UTC(因为如果未设置默认时区,它自然会回退到JVM)。我也通过使用以下代码尝试了这一点

System.setProperty("user.timezone","UTC");

之所以起作用,是因为它现在将db值转换为UTC,但是我对这种方法不满意的是,它把一切更改为UTC时间(duh),包括我的日志。像这样“有力”地这样做并不自然。

另一个对我来说非常有意义的发现是,每个人都不断说完全切换到java.time及其类,这对80%的SO答案相当不推荐并且毫无用处,因为他们建议使用旧类来进行解析,转换等操作。(仍然不确定我是否也能完全抛弃java.sql.Timestamp,因为目前the docs说这是要从{{1}映射到的类型}格式的数据库,但现在我将其保留。)

最后,我要做的是创建一个util类,以帮助我进行所需的转换。

datetime

第一个函数用于解析JSON请求中的字符串日期。

我选择了import java.sql.Timestamp; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; public class DateTimeUtil { public static Timestamp getTimestamp(String utcDateTime) { LocalDateTime localDateTime = LocalDateTime.parse(utcDateTime,DateTimeFormatter.ISO_DATE_TIME); return Timestamp.from(localDateTime.atZone(ZoneId.of("UTC")).toInstant()); } public static String getUTCString(Timestamp dbTimestamp) { return dbTimestamp.toInstant().atZone(ZoneId.of("UTC")).toString(); } } 而不是LocalDateTime,因为来自ZonedDateTime文档的信息:

  

在可能的情况下,建议使用没有时区的简单类。时区的广泛使用往往会给应用程序增加相当大的复杂性。

EDIT: 使用java.time更新的代码是完全不正确的,因为它会剥离UTC的时区,然后表现得与JVM情况完全一样。 ,我对此进行了错误的测试)

第二个函数用于以JSON发送响应。在将值以应有的方式存储在数据库中的同时,当我取回它们时,它们被转换为我的本地时区(因为JVM处于不同的时区)。这就是为什么需要说“嘿,这个值是UTC时区,这样显示,而不是在我的本地时区”。

因此,现在,在将我的实体保存到数据库之前,我用LocalDateTime设置了它的时间戳,将其保存为UTC,当我检索该值并准备DTO来回响应时,我使用了getTimestamp()它还使它成为ISO格式的UTC,同时数据库的行为就像它位于UTC中一样,因此它无法添加自己的内容,并且客户端和API之间的通信已标准化,每个人都确切知道期望什么。

,

如果要确保您正在处理UTC时间,则应将其照此存储在数据库中。您可以在Java中指定区域或偏移量:

<div class="some-class"></div>

尝试这些方法或编写类似的方法(也有public ZonedDateTime utcDateTimeFrom(Timestamp timestamp) { return timestamp.toLocalDateTime().atZone(ZoneId.of("UTC")); } public Timestamp toUtcTimestamp(LocalDateTime localDateTime) { return Timestamp.from(localDateTime.atZone(ZoneId.of("UTC")).toInstant()); } ),但是我认为您的JSON响应/请求中的日期时间不足,您将必须发送时区或客户端也是如此,否则您可能会遇到时区问题。

,

在将数据库的DateTime转换为java.sql.Timestamp时使用应用程序的时区。您将需要传递环境变量或在应用程序中进行设置。

-Duser.timezone=America/Chicago

TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago"));

编辑:

我认为您遗漏了非常重要的一点。

java.sql.Timestamp只是一个长值,它是自1971年以来经过的毫秒数。这与时区无关,仅当您要获取字符串表示形式时才需要它们。

当您通过某种字符串表示形式创建java.sql.Timestamp的对象时, 2019-12-01 16:00:00 本身就会使用您的服务器时区。

第二个问题。

例如-1573251160是我编写此答案的时间。此值表示在所有时区的时间都相同。

现在,此字段需要转换为 DateTime 列以存储在数据库中,该字段是与时区相关的字段。在不知道时区的情况下,您根本无法选择时间,因为不同时区的时间会有所不同。不知道时区就不可能转换。 例如:相同的值将转换为:

  1. 星期五,2019年11月8日22:12:40 GMT
  2. 2019年11月9日星期五格林尼治标准时间+05:30 3:li

转换将取决于服务器在哪个时区上运行。如果您一直希望将值存储在UTC中,那么在UTC上运行服务器似乎是合理的。

本文链接:https://www.f2er.com/3135716.html

大家都在问