感谢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以来,有两种方法可以做到这一点:
-
在application.properties
中的set属性:
spring.jpa.properties.hibernate.jdbc.time_zone=UTC
-
或在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 列以存储在数据库中,该字段是与时区相关的字段。在不知道时区的情况下,您根本无法选择时间,因为不同时区的时间会有所不同。不知道时区就不可能转换。
例如:相同的值将转换为:
- 星期五,2019年11月8日22:12:40 GMT
- 2019年11月9日星期五格林尼治标准时间+05:30 3:li
转换将取决于服务器在哪个时区上运行。如果您一直希望将值存储在UTC中,那么在UTC上运行服务器似乎是合理的。
本文链接:https://www.f2er.com/3135716.html