【问题标题】:Wrong value while converting UTC date to a given timezone将 UTC 日期转换为给定时区时的值错误
【发布时间】:2020-09-29 14:04:43
【问题描述】:

我正在尝试将格式为 2020-06-09T06:30:00Z 的 UTC 日期字符串转换为像 Asia/Calcutta 这样的时区。

UTCAsia/Calcutta 之间的时间偏移是 +5:30,因此将 2020-06-09T06:30:00Z 转换为 Asia/Calcutta 的预期结果是 2020-06-09T12:00:00Z,但我得到的是 2020-06-09T01:00:00Z

    String utcDate = "2020-06-09T06:30:00Z";
    String timezone = "Asia/Calcutta";

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
    LocalDateTime date = LocalDateTime.parse(utcDate, formatter);          //2020-06-09T06:30
    ZonedDateTime zonedDate = ZonedDateTime.of(date, ZoneId.of(timezone)); //2020-06-09T06:30+05:30[Asia/Calcutta]
    Date dateConverted = Date.from( zonedDate.toInstant());                //Tue Jun 09 01:00:00 UTC 2020

我认为zonedDate.toInstant() 正在转换减去5:30 而不是将5:30 添加到2020-06-09T06:30。我的机器在 UTC 时区。可能是它转换为当地时间不知道是什么问题。也试过下面的方法,还是一样的问题。

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
sdf.setTimeZone(TimeZone.getTimeZone(timezone));
sdf.parse(dateString);

【问题讨论】:

  • 我认为你做错了。当您解析日期并设置时区时。它将理解 06:30 是 GMT+5:30 时区。然后当你打印出来的时候。它将打印 GMT+0 值。根据需要进行转换。先按 zonedDateTime 解析
  • @user3562932 是的。刚刚使用 SimpleDateFormat 让它工作。如果您可以使用 ZonedDateTime 发布更正,那也会有帮助
  • 欢迎接受您自己的答案。将问题清楚地标记为已回答将有助于其他读者。

标签: java datetime timezone


【解决方案1】:

java.time

我同意你的观点,你应该更喜欢使用 java.time,现代 Java 日期和时间 API。当您知道如何操作时,这并不复杂。

    String utcDate = "2020-06-09T06:30:00Z";
    String timezone = "Asia/Calcutta";

    ZoneId zone = ZoneId.of(timezone);
    ZonedDateTime zdt = Instant.parse(utcDate).atZone(zone);

    System.out.println("zdt: " + zdt);

输出是:

zdt: 2020-06-09T12:00+05:30[亚洲/加尔各答]

您注意到ZonedDateTime 中的时间是您预期的 12:00。不过,您还说您期望的尾随 Z 是错误的。 Z 表示与 UTC 的偏移量为 0,因此由于偏移量是 +05:30,我们很高兴没有在那里有 Z

如果您需要的是老式的 Date 对象,大概是您现在不想升级的旧版 API,则没有理由先转换为您的时区。这有效:

    Date oldfashionedDate = Date.from(Instant.parse(utcDate));
    System.out.println("oldfashionedDate: " + oldfashionedDate);

我的时区的输出是:

oldfashionedDate: Tue Jun 09 08:30:00 CEST 2020

您可能会怀疑Date 是否正确。它是。由于我在欧洲/哥本哈根时区,目前在偏移 +02:00,Date 在这个时区打印,所以一天中的时间是 8:30(Date 没有时区或抵消)。相反,如果我在亚洲/加尔各答时区跑步:

oldfashionedDate: Tue Jun 09 12:00:00 IST 2020

您注意到印度标准时间的 IST 而不是中欧夏令时间的 CEST。时间和预期一样是12:00。

积分供您随身携带

  • 切勿将Z 硬编码为格式模式字符串中的文字。正如我所说,它是一个偏移量,因此需要将其解析为偏移量。
  • 出于同样的原因,不要解析为LocalDateTime。一个LocalDateTime。没有任何偏移量,因此您正在丢失信息。
  • 在这种情况下,根本不使用格式化程序。您的格式是 ISO 8601,Instant 将其解析为默认格式,也就是说,没有任何明确的格式化程序(OffsetDateTimeZonedDateTime 也是如此)。
  • 进行 UTC 亚洲/加尔各答的转换。在我看来,您正在进行相反的转换。

链接

Wikipedia article: ISO 8601

【讨论】:

  • utcDate 是从第 3 方 API 接收的字符串,“Z”不是我添加的,时区可以是任何时区,也来自 API 而不是我的系统时区。我刚刚发布了例如
  • 好的,@Thanthu,这有什么问题吗? UTC 日期时间是否总是带有尾随 Z?如果不是,它还能是什么样子?我的答案和代码适用于任何以 Z 结尾的 ISO 8601 日期时间字符串和任何 IANA time zone
  • 是的,它总是带有尾随 Z。你的回答很好解释。我会投票。一旦我尝试,将接受答案。然而,这个问题之前已经解决了,正如你所提到的,在我的情况下发生了相反的情况
【解决方案2】:

使用SimpleDateFormat 可以正常工作,如下所示。但是遇到这个问题的任何人都应该使用ZonedDateTime

String utcDate = "2020-06-09T06:30:00Z";
String timezone = "Asia/Calcutta";

SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
sdf1.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = sdf1.parse(utcDate);       // Tue Jun 09 06:30:00 UTC 2020

SimpleDateFormat sdf2 = new SimpleDateFormat("dd-M-yyyy hh:mm:ss a");
sdf2.setTimeZone(TimeZone.getTimeZone(timezone));

String sDateInZone = sdf2.format(date);      // 09-6-2020 12:00:00 PM
SimpleDateFormat formatter = new SimpleDateFormat("dd-M-yyyy hh:mm:ss a");
Date dateInZone = formatter.parse(sDateInZone);    // Tue Jun 09 12:00:00 UTC 2020

编辑: 使用ZonedDateTime 使其工作,如下所示:

String utcDate = "2020-06-09T06:30:00Z";
String timezone = "Asia/Calcutta";

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
LocalDateTime dateUtc = LocalDateTime.parse(utcDate, formatter);
ZonedDateTime utcZonedDateTime = dateUtc.atZone(ZoneId.systemDefault());

ZoneId zoneId = ZoneId.of(timezone);
ZonedDateTime zonedDateTime = utcZonedDateTime.withZoneSameInstant(zoneId);
String zoneDateString = formatter.format(zonedDateTime);

Date date = Date.from(LocalDateTime.parse(zoneDateString, formatter).atZone(ZoneId.systemDefault()).toInstant());

【讨论】:

  • 从您的ZonedDateTime 代码我得到Tue Jun 09 06:30:00 IST 2020,这是不正确的。我再说一遍,不要将 Z 硬编码为格式模式字符串中的文字,这会导致错误的结果。
猜你喜欢
  • 1970-01-01
  • 2020-06-11
  • 2019-09-07
  • 2019-06-15
  • 2019-05-21
  • 2011-08-30
  • 2021-03-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多