解析具有不同时区的日期时间

4
即使在使用Java 15年的经验下,处理日期和时间的问题仍然会让人感到困扰......
这里是情况:我从某个外部系统得到一个时间戳,它以String的形式显示。时间戳的语义是它表示一个UTC日期。必须将该时间戳置入实体,然后置入PostgreSQL数据库中的TIMESTAMP字段中。此外,我需要将相同的时间戳作为本地时间(在我的情况下为CEST)置于实体中,然后置于带有时区的TIMESTAMP WITH TIME ZONE字段的数据库中。
如何确保无论执行代码的机器的设置如何,时间戳都能正确存储在实体中(以便与其他UTC时间戳进行验证),并在以后的报告中使用它们?
以下是代码,在我的本地计算机上正常工作:
SimpleDateFormat sdfUTC = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
sdfUTC.setTimeZone(TimeZone.getTimeZone("UTC"));
Date utcTimestamp = sdfUTC.parse(utcTimestampString);
// getMachinesTimezone is some internal util method giving the TimeZone object of the machines Location
Calendar localTimestamp = new GregorianCalendar(getMachinesTimezone());
localTimestamp.setTimeInMillis(utcTimestamp.getTime());

但是在服务器上执行相同的代码时,结果却不同,因此我认为这不是处理它的正确方法。 有什么建议吗?

PS:我在搜索这个论坛时读到了关于Joda Time的信息,但在给定的项目中,由于我只更改现有模块,因此我无法引入新库,所以我必须使用标准的JDK1.6。


嗨,Geziesfer,如果您没有得到您想要的答案,请考虑添加一些实际结果的示例,包括您本地计算机和服务器上的结果。这将为我们提供有关问题的更多上下文。对于一个写得很好、整洁、语法正确的问题,点赞! - jamesmortensen
在数据库中存储相同的数据似乎是很愚蠢的。 - stew
@stew:我不是要评判现有系统的愚蠢程度,但原因是后面生成报告的语句需要直接将UTC和本地时间作为输入参数,而不是在那个点上进行计算。 - Alexander Rühl
@jmort253:感谢您的好评。字符串中的时间戳例如为“2012-05-10T15:00:07.000”,在本地执行后,我发现它在数据库中是正确的。但是在服务器上,我看到UTC字段中是“2012-05-10 19:00:07”,而本地字段中是“2012-05-10 17:00:07+00”。由于我的时区是CEST,本地值应该是“2012-05-10 17:00:07+00”,而UTC字段应该与输入相同。 - Alexander Rühl
1
即使Java编程已有大约15年的经验,处理日期和时间仍然是一个常常让人困扰的话题。因为日期、时间和时区似乎是为了让程序员的生活变得更加困难而存在的,而且Java的日期/时间API使情况更糟。尽管在某些情况下采用JodaTime而不是标准的Java API可能会有所帮助,但在与JDBC等东西交互时,它并没有什么用处。 - Craig Ringer
显示剩余2条评论
3个回答

6

如果我理解正确,您需要在打印的数据/日历对象上设置时区。像这样:

private Locale locale = Locale.US;
private static final String[] tzStrings = {
    "America/New_York",
    "America/Chicago",
    "America/Denver",
    "America/Los_Angeles",
};

  Date now = new Date();
  for ( TimeZone z : zones) {
        DateFormat df = new SimpleDateFormat("K:mm a,z", locale);
        df.setTimeZone(z);
        String result = df.format(now);
        System.out.println(result); 
  }

如果我将时区设置为SimpleDateFormat,它会正常工作。

这是样例代码...

String date="05/19/2008 04:30 AM (EST)";
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy hh:mm aaa (z)");
TimeZone.setDefault(TimeZone.getTimeZone("PST"));
long millis = sdf.parse(date).getTime();
sdf.setTimeZone(TimeZone.getDefault());
System.out.println(sdf.format(new Date(millis)));

重点是,我不打印它,而是将其持久化。在上面的答案中,我陈述了我认为这很奇怪的原因。 - Alexander Rühl
我遇到了与主题发起者类似的问题:我有一个日期字符串,想将其解析为UTC时区的日期。使用SimpleDateFormat并通过sdf.setTimeZone(TimeZone.getTimeZone("UTC"))设置其时区为UTC并不够。解析器仍然使用我的本地时间。关键是使用TimeZone.setDefault(TimeZone.getTimeZone("UTC"))。所以感谢您的答案。 - Torsten

0

我认为您需要在日历对象中设置目标时区。我认为应该是这样的:

Calendar localTimestamp = new GregorianCalendar(TimeZone.getTimeZone("GMT+10"));
localTimestamp.setTimeInMillis(utcTimestamp.getTime());

在其他情况下,Java会为日历实例使用默认的系统时区。

好的,我在上面的代码中进行了调整 - 但是在服务器上它仍然不能按预期工作。 - Alexander Rühl
@Geziefer,你能说出问题是在什么时候出现的吗?是在数据库中还是在Java中已经出现了呢? - sebastian
似乎已经正确存储在数据库中,因为我在调试时已经看到了错误的时间戳。 - Alexander Rühl

-1

您可以通过以下示例代码来实现。

Date date = new Date();

DateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm:ss z");
formatter.setTimeZone(TimeZone.getTimeZone("CET"));

Date date1 = dateformat.parse(formatter.format(date));

// Set the formatter to use a different timezone
formatter.setTimeZone(TimeZone.getTimeZone("IST"));

Date date2 = dateformat.parse(formatter.format(date)); 
// Prints the date in the IST timezone
//    System.out.println(formatter.format(date));

问题在于,我的输入不是像你的例子那样的日期格式,而是一个字符串,例如“2012-01-01T12:00:00.000”。现在,当我在UTC上解析它并通过JPA实体将其保存在postgreSQL数据库中的TIMESTAMP字段中时,它会导致错误的时间戳:“2012-01-01 13:00:00”。执行此操作的机器位于GMT。如果我切换到本地时间(CET),它会产生不同的结果。这就是我不理解的地方,因为我有一个给定的字符串,并且说它应该格式化为UTC。 - Alexander Rühl
@BhavikAmbani 你尝试过像Geziefer提到的那样解析字符串吗?用你的方法对我也没用。 - Torsten
@BhavikAmbani 对不起,我当时是在讽刺,而且顺便说一下,我不是原帖的作者,所以无论如何我都不能接受你的答案。你有没有尝试解析字符串而不是使用日期对象,就像Geziefer所说的那样?(我很抱歉重复了一遍,但你似乎没有仔细阅读评论,或者至少没有充分回答它们。)如果我按照你的解决方案这样做,对我来说是行不通的。当我使用Anil Kumar C的答案时,它可以工作。区别在于在设置DateFormatter中的TimeZone之前使用TimeZone.setDefault(String)。 - Torsten

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