public static void main(String[] args) {
Timestamp ts = new Timestamp(116, 02, 12, 20, 45, 0, 0);
Date d = new Date();
d.setTime(ts.getTime());
System.out.println(d);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
//System.out.println(ts.getTime());
System.out.println(simpleDateFormat.format(ts));
System.out.println(Timestamp.valueOf(simpleDateFormat.format(ts)));
}
在上面的代码中,最后两行打印不同的值。当前时区是CST,我想将其转换为UTC。当我转换它时,最后两行打印不同的值一小时,即最后但一个打印 13 3 月 2:45 上午和最后打印 13 三月 3:45 上午。为什么它们不同,我该如何纠正它。
Java 8
Instant inst = LocalDateTime.of(2016, Month.MARCH, 12, 20, 45)
.atZone(ZoneId.of("America/Chicago"))
.toInstant();
System.out.println(inst);
这打印
2016-03-13T02:45:00Z
今天,您(通常)不需要Timestamp
对象。java.sql.Timestamp
类早已过时。一旦我们使用它以纳秒精度在 SQL 数据库之间传输时间戳值。今天,我们改用Instant
类来实现此目的。Instant
类之一java.time
,现代 Java 日期和时间 API(有时我们使用来自同一 API 的LocalDateTime
,这取决于您的确切要求和数据库列的数据类型)。
Timestamp
和Instant
都没有时区。与Timestamp
不同,Instant
始终以 UTC 打印(由上述输出末尾的Z
表示)。如您所见,上面的代码片段已将您的时间 20:45 CST 正确转换为第二天 UTC 的 02:45。
如果您确实需要时间戳,通常是对于您无法更改或现在不想更改的旧版 API,则转换很容易:
Timestamp ts = Timestamp.from(inst);
System.out.println(ts);
2016-03-12 20:45:00.0
Timestamp.toString
使用 JVM 的时区设置来生成字符串,因此您可以识别我们开始的时间。因此,Timestamp
包含正确的时间点。无需以任何方式转换它。如果它被错误地插入到您的数据库中,则问题出在您的 JDBC 驱动程序、数据库或其他地方,如果可以的话,您应该更愿意在那里更正它。
爪哇 6 和 7
与上述非常相似的代码将在Java 7中工作,如果你将ThreeTen Backport添加到你的项目中。这是java.time
类到 Java 6 和 7 的向后移植,我在底部包含一个链接(它是 JSR-310 的 ThreeTen,其中首次描述了现代 API)。
Instant inst = LocalDateTime.of(2016, Month.MARCH, 12, 20, 45)
.atZone(ZoneId.of("America/Chicago"))
.toInstant();
Timestamp ts = DateTimeUtils.toSqlTimestamp(inst);
您注意到与Java 8的唯一区别是我们将Instant
转换为Timestamp
的方式。当然,结果是一样的。
我不想依赖ThreeTen Backport,当然还有办法获得Timestamp
。我不会像在代码中那样使用已弃用的构造函数,即使只要没有人篡改 JVM 的时区设置,它就可以工作。如果您知道您想要等于 02:45 UTC 的Timestamp
,一个选项是
Timestamp ts = Timestamp.valueOf("2016-03-12 20:45:00");
不过,它仍然取决于 JVM 的时区设置。
您的代码中出了什么问题?
如前所述,Timestamp
没有时区,因此将Timestamp
转换为 UTC 没有意义。
代码中发生的情况:
- 已弃用的
Timestamp
构造函数使用 JVM 的时区设置(我认为是美国/芝加哥)来构造一个对应的Timestamp
2016 年 3 月 12 日晚上 8:45 在您的时区(与 UTC 时间 3 月 13 日凌晨 2:45 的时间点相同)。 - 您的
SimpleDateFormat
正确地将其格式化为2016-03-13 02:45:00
(UTC)。 Timestamp.valueOf()
也使用美国/芝加哥时区。但是,在 3 月 12 日至 13 日之间的夜晚,夏令时(夏令时)从这个时区开始。凌晨 2 点,时钟向前移动到 3。所以今天晚上没有2:45。Timestamp
选择3:45。
链接
- Oracle 教程:解释如何使用
java.time
的日期时间。 - Java 规范请求 (JSR) 310,其中首次描述了
java.time
。 - ThreeTen Backport 项目,将
java.time
向 Java 6 和 7 的反向移植。 - ThreeTenABP,ThreeTen Backport 的安卓版
- 问:如何在Android项目中使用ThreeTenABP,并进行了非常详尽的解释。