我的情况是我以各种不同的模式(来自第三方电子邮件服务器)获取日期字符串(例如):
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)")
我在这里缺少什么?
答案 0 :(得分:2)
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。
仅供参考:现在位于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/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用诸如EST
或IST
之类的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 );
示例中显示的模式在过去的协议中很常见,例如RFC 1123 / RFC 822.
如今,方法是始终使用ISO 8601。在这个现代标准中,这些格式易于在各种人类文化中阅读,对英语的依赖性较小,机器易于解析,并且设计得毫不含糊。
生成/解析字符串时,java.time类在默认情况下使用ISO 8601。您可以在上面的示例中看到他们生成的输出。请注意ZonedDateTime
通过在方括号中附加时区名称来扩展标准。
顺便说一句,如果您有类似的输入完全符合RFC 1123,请知道java.time提供了一个预定义的格式化程序对象DateTimeFormatter.RFC_1123_DATE_TIME
。