UTC时间戳 + Joda时间

5
我正在使用Joda编写一个简单的Java程序,尝试获取UTC时间戳:
public Timestamp getCurrentUTC(LocalDateTime date, DateTimeZone srcTZ, DateTimeZone dstTZ, Locale l) {
    DateTime srcDateTime = date.toDateTime(srcTZ);
    DateTime dstDateTime = srcDateTime.toDateTime(dstTZ);
    
    System.out.println("UTC Time:" + dstDateTime.getMillis());      
    System.out.println("UTC Time:" + new Timestamp(dstDateTime.getMillis()));
    
    return new Timestamp(dstDateTime.getMillis());
}

程序的输出如下所示:
UTC Time:1378265162047
UTC Time:2013-09-03 23:26:02.047

毫秒值是正确的UTC时间(即与GMT-4时区确认)。 秒值是EST时区。
我需要的是不改变UTC值的java.sql.Timestamp(即与时区无关的)用于数据库写入。是否可能?

Edit 1

DateTime srcDateTime = date.toDateTime(srcTZ);

DateTime dstDateTime = srcDateTime.toDateTime(dstTZ);

System.out.println("UTC Time:" + dstDateTime.getMillis());

我知道srcDateTime是本地时间(GMT-4),dstDateTime是世界标准时间(GMT-0)。日期的输出值如下:

Source Date:2013-09-04T09:10:43.683-04:00

Destination Date: 2013-09-04T13:10:43.683Z

我尝试了所有的组合来获取dstDateTime的UTC值作为java.sql.TimeStamp:

System.out.println("UTC Time:" + dstDateTime.getMillis());
    
System.out.println("UTC Time:" + new Timestamp(srcDateTime.toDateTime(DateTimeZone.UTC).getMillis()));

System.out.println("UTC Time:" + new Timestamp(dstDateTime.toDateTime(DateTimeZone.UTC).getMillis()));

测试的打印输出:
UTC Time:1378298760226 - Correct UTC

UTC Time:2013-09-04 08:46:00.226 - Incorrect Local Date Time instead of the Expected UTC

UTC Time:2013-09-04 08:46:00.226 - Incorrect Local Date Time instead of the Expected UTC

第一行打印正确的UTC时间戳。我所需要的就是相同类型的java.sql.TimeStamp值。无论我尝试什么,总是返回本地机器的日期时间。

编辑2

我尝试了以下方法:

System.out.println("UTC Timestamp:" + date.toDateTime(srcTZ).getMillis());
System.out.println("UTC Timestamp:" + new Timestamp(date.toDateTime(srcTZ).getMillis()));

输出结果如下:
UTC Time:1378342856315 - Correct UTC Time
UTC Timestap:2013-09-04 21:00:56.315 - Local Time other than the expected UTC Time

每当我尝试转换为时间戳时,我会失去我想要的有效UTC值。
就方法参数而言:
srcTZ = DateTimeZone.forTimeZone(TimeZone.getTimeZone("America/Montreal")
dstTZ = DateTimeZone.forTimeZone(TimeZone.getTimeZone("Etc/UTC"))
Local l = new Locale("en", "CA")

非常感谢你的帮助。

Nick。

第三次编辑

你好,Matt,

非常感谢您的回复。我们得到了与您相同的结果。不知道关于打印等的事情...更具体地说:

System.out.println("UTC Timestamp:" + srcDateTime.toDateTime(dstTZ).getMillis());
System.out.println("UTC Timestamp:" + srcDateTime.toDateTime(dstTZ));
System.out.println("UTC Timestamp:" + new Timestamp(srcDateTime.toDateTime(dstTZ).getMillis()));

输出结果如下:

UTC Timestamp:1378389098468 - Correct UTC Timestap (Thu, 05 Sep 2013 13:51:38 GMT)
UTC Timestamp:2013-09-05T13:51:38.468Z - Correct UTC Time
UTC Timestamp:2013-09-05 09:51:38.468 - Local time is printed, UTC is expected

当我们意识到数据库存储的是本地时间而非UTC时间时,这个问题被带到了我的视线中。
+---------------------+
| effectivedate       |
+---------------------+
| 2013-09-05 09:34:11 |
+---------------------+

MySQL时区设置为'-00:00'

mysql> SELECT CURRENT_TIMESTAMP;
+---------------------+
| CURRENT_TIMESTAMP   |
+---------------------+
| 2013-09-05 13:48:09 |
+---------------------+

使用Eclipse调试器调试应用程序时,我们发现本地日期时间(2013-09-05 09:51:38.468)被传递到了数据库中(无法发布图片,积分不足...)。数据类型为TimeStamp,没有字符串操作。也许Eclipse调试器同样使用了String.println()函数,但不确定...非常感谢您帮助我们调试应用程序。不想浪费太多时间和精力(开个小玩笑)...顺祝商祺,Nick.

1
毫秒值是正确的UTC时间(即GMT-4)。这个声明对我来说没有意义。UTC时间不总是GMT-0吗? - Daniel Kaplan
可能这个答案可以帮到你:http://stackoverflow.com/a/13539088/180100 - user180100
嗨,抱歉,我修复了工作。我的意思是根据本地时区GMT-4进行计算。我想要得到GMT-0的时间戳。RC,toString方法不适用。我们需要它以时间戳的形式。我知道toString方法返回正确的值。 - Nick Cameo
5个回答

24

希望这篇文章能够为某些人节省3天的麻烦。在代码中设置默认时区,比设置环境变量等更便携。您可以通过在代码构造函数等适当位置添加以下内容来实现:

DateTimeZone.setDefault(DateTimeZone.UTC);

您可以打印UTC时间,连接UTC时间等等...


6
如果您在代码中没有使用Joda时间,则还需要设置TimeZone.setDefault(TimeZone)。您也可以仅设置user.timezone系统属性,以便一次性获取两者。如果在触及任何Joda代码之前设置TimeZone(使用系统属性或调用代码),则它将选择您的设置。请参见我几天前回答中的注释以设置TimeZone。 - pickypg

6

尝试运行下面的简短程序,这可以帮助您理解正在发生的事情:

LocalDateTime date = LocalDateTime.now();
DateTimeZone tz = DateTimeZone.getDefault();

System.out.println(date);
System.out.println(tz);
System.out.println("-----");
System.out.println(date.toDateTime(tz));
System.out.println(date.toDateTime(tz).toInstant());
System.out.println(date.toDateTime(tz).toDateTime(DateTimeZone.UTC));
System.out.println("-----");
System.out.println(date.toDateTime(tz).getMillis());
System.out.println(date.toDateTime(tz).toInstant().getMillis());
System.out.println(date.toDateTime(tz).toDateTime(DateTimeZone.UTC).getMillis());
System.out.println("-----");
System.out.println(new Timestamp(date.toDateTime(tz).getMillis()));
System.out.println(new Timestamp(date.toDateTime(tz).toInstant().getMillis()));
System.out.println(new Timestamp(date.toDateTime(tz).toDateTime(DateTimeZone.UTC).getMillis()));

在我的电脑上,输出结果为:
2013-09-04T19:08:35.111
America/Phoenix
-----
2013-09-04T19:08:35.111-07:00
2013-09-05T02:08:35.111Z
2013-09-05T02:08:35.111Z
-----
1378346915111
1378346915111
1378346915111
-----
2013-09-04 19:08:35.111
2013-09-04 19:08:35.111
2013-09-04 19:08:35.111

从下面的代码可以看出,您可以使用.toInstant().toDateTime(DateTimeZone.UTC)来获取UTC时间。但是即使您不调用这两个方法中的任何一个,当您调用getMillis()时仍将获得UTC值。

因此,问题不在于JodaTime库。问题在于如何评估结果。

当您创建一个java.sql.Timestamp对象时,您传递的是距离1970年1月1日UTC的毫秒数。只有在显示它时,才将本地时区应用于结果。

假设您将其作为Timestamp对象传递给数据库而不进行一些中间字符串表示,则应该没有问题。仅仅因为当您调用System.out.println时它看起来像本地时间,并不意味着它在内部也是本地时间。

java.sql.Timestamp类扩展了java.util.Date类 - 这就是它继承这种行为的原因。


你好,马特,请查看上面的第三次编辑。 - Nick Cameo
如果您的“Timestamp”以本地时间保存,那么您可能是通过字符串拼接构建SQL查询,而不是传递正确的参数?只是猜测。 - Matt Johnson-Pint

1

我为此苦苦挣扎了几个小时,最终找到了解决方案。

我正在使用PlayFramework - 不确定这是否对任何人有帮助,但对于我的数据库连接设置,我必须使用:

db.default.url="jdbc:mysql://localhost/continuum?characterEncoding=UTF-8&serverTimezone=UTC"

(添加'&serverTimezone=UTC')



0

LocalDateTime 已经排除了时区,这意味着它要么是指用户输入“他们”的时间的本地时间(例如,对于一个葬礼邀请,它将始终在当地时间),要么如果您提前计划,则已经在 UTC 中。

如果您的 date 已经在 srcTZDateTimeZone 中,但是以 LocalDateTime 的形式加载,则应返回

new Timestamp(date.toDateTime(srcTZ).toDateTime(DateTimeZone.UTC).getMillis());

或者,如果您确定 date 已经是 UTC 时间,也就是 GMT+0,那么可以返回

new Timestamp(date.toDateTime(DateTimeZone.UTC).getMillis());

最后,大多数数据库接受时间戳作为ISO格式的字符串,这是默认的toString输出:

date/*.toDateTime(srcTZ)*/.toDateTime(DateTimeZone.UTC).toString()

你好 Picky,请查看上面的 edit。我尝试了你建议的所有组合,但它们都返回本地时间,而不是需要的 UTC 时间以写入数据库。 - Nick Cameo
1
我们将LocalDateTime排除时区的本地时间传递给函数,但在函数内部使用DateTime进行正确的偏移: DateTime srcDateTime = date.toDateTime(srcTZ); DateTime dstDateTime = srcDateTime.toDateTime(dstTZ); - Nick Cameo
@NickCameo 时间特别难处理,因为存在时区问题。这就是为什么我倾向于始终使用UTC,并尽可能晚地将其转换为时区(在表示层)。如果“date”处于EST/GMT-4等时区,则需要将其转换为具有该时区的“DateTime”对象,这应该是“toDateTime(srcTZ)”的第一步。此时,您应该拥有实际日期/时间及其实际时区。从那里开始,您实际上不需要执行“toDateTime(DateTimeZone.UTC)”,但您应该能够使用“getMillis()”,例如“date.toDateTime(srcTZ).getMillis()”。 - pickypg
除此之外,您能否提供您的“日期”、“srcTZ”甚至“dstTZ”的示例值? - pickypg
@pickypg 请查看"编辑2"。 - Nick Cameo
将默认的 TimeZone 设置为 UTC/GMT:TimeZone.setDefault(TimeZone.getTimeZone("GMT"))。这只需要在启动时完成一次即可。为了避免编写代码,您还可以设置 "user.timezone" 系统属性,它会自动设置(使用 -Duser.timezone=GMT 来设置UTC)。 - pickypg

0
你需要UTC值还是可以直接使用Timestamp.toString()方法?这将为您提供一个格式化的时间字符串,可用于SQL语句。

我知道toString方法可以工作,但我们需要它在java.sql.TimeStamp中。 - Nick Cameo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接