在Joda-Time中使用两种时区格式解析日期的模式

时间:2017-03-30 15:33:24

标签: java date jodatime

我的情况是我以各种不同的模式(来自第三方电子邮件服务器)获取日期字符串(例如):

  • 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)")

我在这里缺少什么?

1 个答案:

答案 0 :(得分:2)

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-Timemaintenance mode项目建议迁移到java.time类。

  

Joda-Time中的两种时区格式

     

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

不,这是 time zone格式。

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

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

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

java.time类尝试猜测解析此类伪时区名称,但可能不是您的预期值。事实上,它不恰当地解释了你的第一个例子,将IST显然解释为以色列标准时间,其中包括印度标准时间爱尔兰标准时间,可能还有更多。

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,这至少可以为您提供时间轴上的确切时刻。调整为UTC为Instant,因为您的大部分工作通常应以UTC完成,包括您的日志记录。

使用String::substring删除缩写。请注意,我们在子字符串搜索中包含LEFT PARENTHESIS之前的SPACE,因为我们要删除字符和之后的所有内容。

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 );
  

inputModified:Mon,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