【问题标题】:Java.util.date get the actual date at client timezoneJava.util.date 获取客户端时区的实际日期
【发布时间】:2019-03-12 08:41:43
【问题描述】:

上下左右搜索——找不到这个问题的简单答案:

我有 java.util.Date 实例,它从 mySQL 获取它的值。

我还有登录用户的时区代码。

我需要获取用户时区的实际时间。

例如:

我的服务器机器时区是 GMT+2。

我在DB中的日期值是:2017-02-09 16:38:58.000

根据我的服务器机器时区,我将其放入 date instance 为:2017-02-09T16:38:58.000+0200

现在我需要知道如果:

如果,例如,我的客户端时区代码是 GMT+4,我想得到:

2017-02-09 20:38:58.000

纯日期,与我的时区一致,不包含“+4”或“GMT”指示。

简而言之:将我的 java.util.date 转换为特定时区的纯日期。

听起来很简单?在阅读了很多文档之后,我已经不确定这是否真的很简单。

【问题讨论】:

  • 我建议你不要使用Date。该课程设计不良且早已过时。而是使用LocalDateTimeOffsetDateTimeZonedDateTime,均来自java.time, the modern Java date and time API
  • 那么我是否正确理解了 UTC 数据库中的日期时间,但是当您检索它时,您(错误地)收到 2017-02-09T16:38:58.000+0200?那么我们需要补偿错误转换到客户端的时区吗?当然我们可以做到。

标签: java timezone java.util.date


【解决方案1】:

时间戳(带时区)

据我了解,您数据库中的日期时间为 UTC,但是当您检索它时,您(错误地)收到 2017-02-09T16:38:58.000+02:00。

首先,如果可以的话,将 MySQL 数据库列的数据类型更改为 timestamp(在其他一些数据库中,它会被称为 timestamp with time zone)。这将确保 MySQL 知道时间是 UTC,并且应该使您能够将它们检索为正确的时间点,而不是错误时区中的正确时间。这反过来将为您提供转换到客户端时区的最佳起点。

java.time

其次,从现代 Java 日期和时间 API 的 java.time 将您的值检索到适当的类型。避免使用java.util.Date,因为它设计不佳并且无法处理不同的时区。例如,如果您的数据库数据类型是datetime

    LocalDateTime dateTime = yourResultSet.getObject("your_col", LocalDateTime.class);

LocalDateTime 是一个没有时区的日期和时间,所以你不能得到错误的时区。提供您知道正确的偏移量:

    OffsetDateTime odt = dateTime.atOffset(ZoneOffset.UTC);

转换为客户端时区:

    ZoneId clientTimeZone = ZoneId.of("Indian/Reunion");
    ZonedDateTime clientDateTime = odt.atZoneSameInstant(clientTimeZone);

    System.out.println(clientDateTime);

2017-02-09T20:38:58+04:00[印度/留尼汪]

请自己使用 region/city 格式的实时时区,而不是像 +04:00 这样的偏移量。它更容易理解,也更面向未来。 Indian/Reunion 只是一个例子,当然,为您的客户使用正确的。

上面的ZonedDateTime 包含偏移量和时区。建议保持这种状态,我认为它不会造成任何伤害。客户始终可以选择不显示它。如果你还是坚持,再转LocalDateTime

    LocalDateTime clientDateTimeWithoutOffset = clientDateTime.toLocalDateTime();
    System.out.println(clientDateTimeWithoutOffset);

2017-02-09T20:38:58

如果数据库数据类型是timestamp

    OffsetDateTime odt = yourResultSet.getObject("your_col", OffsetDateTime.class);

这会保存上面的第一步。其余部分相同。

链接

Oracle tutorial: Date Time 解释如何使用 java.time。

【讨论】:

  • 首先感谢您提供最佳和准确的答案。我将来会使用这些信息。我接受其他答案,因为在我的情况下我无法更改数据库声明,但我投票赞成。
  • 感谢您的反馈!只是为了确保,您确实注意到我向您展示了如何在不更改数据库声明的情况下使用 java.time?由于您使用的是另一个答案,因此您当然可以通过接受它来做正确的事情(您也可以考虑对其进行投票)。
  • 抱歉耽搁了,我还有其他紧急事情要做。我有一个问题:我使用带有结果转换器的休眠查询,它说该值无法转换为 LocalDateTime(即使 mySql 中的值是日期时间类型)/任何解决方案?
  • 抱歉,我的 Hibernate 体验太少了,而且是多年前的事了。我的印象是它应该可以工作,但它到底需要什么——我相信其他人可以回答。
【解决方案2】:

java.util.Date 不存储任何时区。它只存储自“纪元”以来的毫秒数,即 1970 年 1 月 1 日 00:00:00 UTC。

因此,您所要做的就是知道您的服务器机器的时区,找到该时区与您要转换的时区之间的时间段,并添加或减去该时间段。

更新:

int clientGMT = 4; //GMT you want to convert to
int serverGMT = 2; //server's GMT
int delta = clientGMT - serverGMT; //delta between the dates

//assume this is the date in GMT + 2 received from the server
Date d1 = new SimpleDateFormat("dd.MM.yyyy hh:mm:ss").parse("12.03.2019 13:00:00");

//... and you want to convert it to GMT + 4 (client side's time zone)
Date resultDate = new Date(d1.getTime() + delta * 3600000);

P.S. 是的,您必须手动操作时区,正如我上面所说,java.util.Date 不存储此信息(假设每个日期都是 UTC)。

【讨论】:

  • 听起来很简单,但是你能添加如何做到这一点的代码吗?在我尝试编写代码之前,我还认为这很简单......
  • 看看这个。
  • 当然 Java.util.date 包含有关时区的信息!如果您询问它的值,您会得到它在其属性“cdate”中注册的内容
  • 请不要教年轻人使用早已过时且臭名昭著的SimpleDateFormat类。至少不是第一选择。而且不是没有任何保留。今天我们在java.time, the modern Java date and time API 和它的DateTimeFormatter 中做得更好。
  • 显示的方法是一种反模式。 1)它不考虑夏令时。 2) 它创建一个代表不同时间点的对象。 3) 它依赖于服务器的时区设置,应该是无关紧要的。
猜你喜欢
  • 1970-01-01
  • 2020-08-23
  • 2012-10-08
  • 2012-12-02
  • 2013-07-05
  • 2015-01-29
  • 1970-01-01
  • 1970-01-01
  • 2020-05-06
相关资源
最近更新 更多