【问题标题】:Pattern for parsing date with two timezone format in Joda-Time在 Joda-Time 中使用两种时区格式解析日期的模式
【发布时间】:2017-03-31 11:45:51
【问题描述】:

我有一个场景,我以各种不同的模式(来自第三方电子邮件服务器)获取日期字符串(例如):

  • Mon, 13 Mar 2017 19:00:10 +0530 (IST)
  • Tue, 21 Mar 2017 09:23:00 -0700 (PDT)
  • Sun, 12 Mar 2017 14:31:13 +0000 (UTC)

这意味着,只有时区被更改。我可以使用 Java 的 SimpleDateFormat 轻松解析这个,例如:

String pattern = "EEE, dd MMM yyyy HH:mm:ss Z '('z')'"
SimpleDateFormat df = new SimpleDateFormat(pattern);
df.parse("Fri, 31 Mar 2017 13:31:14 +0530 (IST)");

但是当使用 Joda-Time 库中的 DateTimeFormat 时,我无法使用相同的模式。

String pattern = "EEE, dd MMM yyyy HH:mm:ss Z '('z')'"
DateTimeFormat parser = DateTimeFormat.forPattern(pattern)
parser.parseDateTime("Fri, 31 Mar 2017 13:31:14 +0530 (IST)")

我在这里缺少什么?

【问题讨论】:

  • 根据 javadoc (joda-time.sourceforge.net/apidocs/org/joda/time/format/…) - 区域名称: 时区名称 ('z') 无法解析。所以这个字段只用于format/toString()
  • -0700 的偏移量如何描述为 UTC?我所知道的唯一五个半小时的偏移量是印度,但这是一个负数的偏移量,而你的是一个正数的偏移量。这些是实际值还是错误地组合了这些示例?
  • @BasilBourque 抱歉,示例错误。我修好了。
  • 第一个是真的,IST 的偏移量是正数吗?

标签: java date jodatime


【解决方案1】:

tl;博士

String input = "Mon, 13 Mar 2017 19:00:10 +0530 (IST)";
int index = input.indexOf ( " (" ); // Searching for SPACE + LEFT PARENTHESIS.
String inputModified = input.substring ( 0 , index ); // "Mon, 13 Mar 2017 19:00:10 +0530"

Instant instant = 
    OffsetDateTime.parse ( 
        inputModified , 
        DateTimeFormatter.ofPattern( "EEE, d MMM uuuu HH:mm:ss Z" ) 
    ).toInstant() 
;

查看类似的code run live at IdeOne.com

使用 java.time

仅供参考:Joda-Time 项目现在位于 maintenance mode,建议迁移到 java.time 类。

Joda-Time 中的两个时区格式

2017 年 3 月 13 日星期一 19:00:10 +0530 (IST)

不,那是 time zone 格式。

+0530 是一个offset-from-UTC,与UTC 相距数小时和数分钟。

continent/region 的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 3-4 个字母的缩写,例如 ESTIST,因为它们不是真正的时区,没有标准化,甚至不是唯一的(!)。

由于无法可靠地解析 3-4 个字母的缩写,Joda-Time 有拒绝尝试的政策(如上面 Hugo 的评论中所述)。鉴于我们接下来看到的情况,我怀疑这是一个明智的政策。

java.time 类尝试猜测解析此类伪时区名称,但可能不是您的预期值。实际上,它不恰当地解释了您的第一个示例,显然将 IST 解释为 Israel Standard Time 选项包括 India Standard TimeIreland Standard Time em>,可能还有更多。

String input = "Mon, 13 Mar 2017 19:00:10 +0530 (IST)";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "EEE, d MMM uuuu HH:mm:ss Z '('z')'") ;
ZonedDateTime zdt = ZonedDateTime.parse ( input , f );

zdt.toString(): 2017-03-13T19:00:10+02:00[亚洲/耶路撒冷]

所以我建议你去掉最后的虚假缩写块。将剩余的文本解析为OffsetDateTime,这至少可以为您提供时间轴上的准确时刻。将 Instant 调整为 UTC,因为您的大部分工作通常应在 UTC 中完成,包括您的日志记录。

使用String::substring 去掉缩写。请注意,我们在子字符串搜索中包含左括号前的空格,因为我们要删除字符和之后的所有内容。

String input = "Mon, 13 Mar 2017 19:00:10 +0530 (IST)";
int index = input.indexOf ( " (" ); // Searching for SPACE + LEFT PARENTHESIS.
String inputModified = input.substring ( 0 , index );

输入修改时间:2017 年 3 月 13 日星期一 19:00:10 +0530

使用末尾的数字偏移量解析为OffsetDateTime 对象,以指导我们了解该值的确切时刻。

DateTimeFormatter f = DateTimeFormatter.ofPattern( "EEE, d MMM uuuu HH:mm:ss Z" );
OffsetDateTime odt = OffsetDateTime.parse ( inputModified , f );

odt.toString(): 2017-03-13T19:00:10+05:30

提取一个 Instant 对象,以在 UTC 中为我们提供相同的时刻。

Instant instant = odt.toInstant ();

instant.toString(): 2017-03-13T13:30:10Z

如果您坚持,您可以调整到自己的特定时区。但我建议在佩戴 Programmer hat 时学习以 UTC 进行思考。将 UTC 视为“真正的时间”,所有其他时区都只是该主题的变体。

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone( z );

ISO 8601

您的示例中显示的那种模式在过去的协议中很常见,例如 RFC 1123 / RFC 822。

如今,方法是始终使用ISO 8601。在这个现代标准中,格式易于在各种人类文化中阅读,对英语的依赖较少,易于机器解析,并且设计明确。

java.time 类在生成/解析字符串时默认使用 ISO 8601。您可以在我上面的示例中看到它们生成的输出。请注意,ZonedDateTime 通过在方括号中附加时区名称来扩展标准。

顺便说一句,如果您有完全符合 RFC 1123 的类似输入,请知道 java.time 提供了一个预定义的格式化程序对象 DateTimeFormatter.RFC_1123_DATE_TIME

【讨论】:

  • 很好的答案!非常有帮助。虽然,我将所有日期/时间保存在数据库和 UTC 日志中,因为正如你所说,它是 真正的时间 :) 我想第一时间砍掉最后一部分,但是好奇我是否遗漏了一些东西,所以提出了一个问题。由于我也以各种其他模式获取此日期字符串(来自 Mailgun,所以我坚持使用多种模式),我将调整斩波代码以仅删除此特定模式中的最后一部分。再次感谢!
  • @ShashankAgrawal 您可以使用偏移量和缩写来推断+0530 (IST) 表示印度时间(Asia/Kolkata)而不是爱尔兰等时间。但是时区改变了它们的偏移量,而且它们变化得惊人地频繁。所以最终你只是在猜测,这样的猜测代码将来可能会被破坏。无需猜测预期的时区,因为仅偏移量就可以使您到达 UTC,这是您进行记录和比较所需的全部。另外,请注意我在最后添加的最后一句话——你说你有类似的输入,如果 RFC 1123 使用预定义的格式化程序。
  • 我没有明白你的意思最终你只是在猜测。您能否详细说明一下,以便我可以在我错的地方相应地修复我的代码。
  • @ShashankAgrawal 至于“猜测”,我根本不是指您的代码。我指的是我的评论,关于将+0530 (IST) 映射到印度时间。虽然我们现在所知道的情况是可能的,但在未来,区域定义和偏移量可以并且将会改变。当这些变化在未来发生时,我们今天所做的任何硬编码映射都会中断。所以我们只是猜测预期的时区。这就是为什么当有人想要传达时区时,他们应该指定true time zone in continent/region format
  • 哦,太好了!感谢您的澄清。
猜你喜欢
  • 1970-01-01
  • 2011-03-19
  • 1970-01-01
  • 2013-08-17
  • 2013-05-23
  • 2010-11-22
  • 1970-01-01
  • 2014-04-08
  • 2017-01-11
相关资源
最近更新 更多