【问题标题】:For a given Date object, capture it's value relevant to GMT timezone对于给定的 Date 对象,捕获它与 GMT 时区相关的值
【发布时间】:2019-09-05 15:11:42
【问题描述】:

在我在 Java 8 上运行的应用程序中,我有一个 Date 对象。此对象的时区取决于客户端的位置。

在某个特定点,我需要将此日期转换为 GMT,以便它可以与我从数据库访问的值进行比较。

我尝试了 SimpleDateFormat 和 ZonedDateTime,但有一个痛点。这些 API 为我提供了字符串值的 GMT 时间,这非常好。但是,一旦我解析它并将其分配给 Date 对象,它就会回到我的本地时区!

例如:

public class TimeZoneDemo {
    public static void main(String[] args) throws ParseException {
        Date istDate = Calendar.getInstance().getTime();

        ZonedDateTime gmtTime = istDate.toInstant().atZone(ZoneId.of("GMT"));
        System.out.println(gmtTime);

        Date gmtDate = Date.from(gmtTime.toInstant());
        System.out.println(gmtDate);
    }
}

在上面的代码中,gmtTime 显示与 GMT 时区相关的正确值,但 gmtDate ( Date 对象)在本地时区打印值。

P.S.:我的最终目标是在 java.sql.TimeStamp 对象中包含 GMT 值。

如何做到这一点?

更新 1: 在浏览完 cmets 和回复后,我了解到 Date 对象只包含一个包含毫秒的长值。 但是,我的期望是当这条线被执行时:

Date gmtDate = Date.from(gmtTime.toInstant());

无论对象 gmtTime 包含什么时间,我都需要在 TimeStamp 或 Date 对象中捕获该时间。这样做的目的是为了能够与数据库中保存的值进行比较。我将日期作为参数传递给我的 SQL 查询。

有人可以帮助我了解如何实现吗?

【问题讨论】:

  • “但是,一旦我解析它并将其分配给 Date 对象,它就会回到我的本地时区!”并不真地。日期对象不在任何时区“内”。它们只是代表时间的瞬间。见codeblog.jonskeet.uk/2017/04/23/all-about-java-util-date
  • 总是,甚至在使用 Java 8(或更高版本)时更是如此,避免使用 DateTimestamp 类。它们设计不佳且早已过时。使用java.time, the modern Java date and time API
  • 与数据库中持久化的值进行比较 — 如 in 相等还是之前或之后?您更喜欢用 SQL 还是 Java 进行比较?我相信我们可以提供帮助。
  • 无需再使用java.sql.TimeStamp。在 JDBC 4.2 及更高版本中替换为 OffsetDateTimemyPreparedStatement.setObject( … , OffsetDateTime.now( ZoneOffset.UTC ) ) ;OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;。同样,永远不要再使用java.util.Datejava.util.Calendar。多年前采用 JSR 310 时被 java.time 取代。
  • @OleV.V.:我将日期作为参数传递给我的 SQL 查询

标签: java java-8 zoneddatetime sql-timestamp


【解决方案1】:

java.time 和 JDBC 4.2

答案在@BasilBourque 的评论中:使用OffsetDateTime

    PreparedStatement yourPreparedStatement = yourDatabaseConnection.prepareStatement(
            "select smth from your_table where your_time_stamp_col < ?;");
    OffsetDateTime gmtTime = OffsetDateTime.now(ZoneOffset.UTC);
    yourPreparedStatement.setObject(1, gmtTime);

这需要一个兼容 JDBC 4.2 的 JDBC 驱动程序,我认为现在我们所有人都在使用它。这很好,因为它允许我们绕过java.sql.Timestampjava.sql 中的其他日期时间类型。它们都设计得很糟糕,而且已经过时了。

正如其他人所说,过时的课程 DateTimestamp 都没有任何时区或与 UTC/GMT 的偏移量。

一些相关问题

【讨论】:

  • 这可能只在 a) 您的驱动程序支持 OffsetDateTime 并且 b) 您使用 TIMESTAMP WITH TIME ZONE 数据类型时才有效
  • @PhilippeMarschall 谢谢你,你是对的。根据定义,符合 JDBC 4.2 的驱动程序确实支持OffsetDateTime。数据库数据类型应该是timestamp with time zone。如果只是timestamp(没有时区),则需要在Java 端使用LocalDateTime。其他一切都还是一样。
【解决方案2】:

首先Date 类是旧的过时(没有双关语)基础设施的一部分。如果有可能摆脱它并使用java.time 包。但是,如果您必须使用Date,那么您的时区问题就不是问题了。您的行 System.out.println(gmtDate); 仅使用您的本地时区打印它,因为系统假定它是最佳选择。但不管怎样,Date 自 1970 年 1 月 1 日格林威治标准时间 00:00:00 起以毫秒为单位保持特定的时间。 Date 类具有方法 compareTo()after()before(),允许您比较 2 个日期。此外,Date 具有方法 getTime(),它返回自 1970 年 1 月 1 日 00:00:00 GMT 以来由该日期表示的毫秒数。因此,您可以比较 long 的值。但同样,最好的选择是切换到 java.time 包和类 InstantZonedDateTime(以及其他)具有方法 compareTo()、isAfter() 和 isBefore()。

【讨论】:

  • 感谢您的解释,您能否参考上述问题中的更新标题和“更新1”并帮助我完成它?
  • 我只是让办公室,今晚无法回答。
【解决方案3】:

您误解了日期时间类的语义。 java.util.Date 是一个特定的时间点,一个瞬间,它没有与之关联的时区。但是,如果您有时区,则可以向时区询问 java.util.Date 的时间。

java.sql.TimeStamp 等同于java.time.LocalDateTime。它不是瞬间,也没有与之关联的时区。

【讨论】:

  • 感谢您的解释,您能否参考上述问题中的更新标题和“更新1”并帮助我完成它?
  • Date 不正确java.util.Date 类表示 UTC 中的时刻,定义为自 1970-01-01T00:00Z 以来的毫秒数,其中 Z 表示 UTC(零时分秒的偏移量)。如果没有 UTC 的上下文或某些此类参考,您无法代表片刻。
  • java.sql.Timestamp 不正确。 该类实际上是java.util.Date 的子类,笨拙地添加了以纳秒为单位的小数秒,同时仍带有Date 的毫秒数。由于Timestamp Date,它也代表了UTC 中的一个时刻。它肯定等同于LocalDateTime,后者故意缺少时区或与UTC 偏移的概念。见:What's the difference between Instant and LocalDateTime?
  • java.sql.Timestamp 在 JDBC 4.2 及更高版本中被 OffsetDateTime 替换(而不是 LocalDateTime 在此答案中的错误说明)。有关示例代码,请参阅问题上的 my comment
  • @BasilBourque 不是真的不,java.sql.Timestamp 是子类但不是子类型,因此不是java.util.Datejava.sql.Timestamp,不像 java.util.Date 是在 JVM 时区。您可以自己检查一下, Timestamp.valueOf("2019-09-06 17:47:11").getTime() 的值取决于JVM时区。还要注意java.sql.Timestamp 上的java.time.LocalDateTime 转换方法,因为它们是等效的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多