在 Joda-Time 中使用两种时区格式解析日期的模式
Pattern for parsing date with two timezone format in Joda-Time
我有一个场景,我正在获取各种不同模式的日期字符串(来自第三方电子邮件服务器)(例如):
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)")
我在这里缺少什么?
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 project, now in maintenance mode, advises migration to the java.time 类。
two timezone format in Joda-Time
Mon, 13 Mar 2017 19:00:10 +0530 (IST)
不,那是 零 time zone 格式。
+0530
是一个 offset-from-UTC, a number of hours and minutes away from UTC。
指定 proper time zone name in the format of continent/region
, such as America/Montreal
, Africa/Casablanca
或 Pacific/Auckland
。切勿使用 3-4 个字母的缩写,例如 EST
或 IST
,因为它们 不是 真正的时区,不是标准化的,甚至不是唯一的(!)。
由于无法可靠地解析 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[Asia/Jerusalem]
所以我建议你去掉最后的假缩写块。将剩余的文本解析为 OffsetDateTime
,它至少可以在时间轴上为您提供准确的时刻。调整为 Instant
的 UTC,因为您的大部分工作通常应该在 UTC 中完成,包括您的日志记录。
使用 String::substring
删除缩写。请注意,我们在子字符串搜索中将左括号前的 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, 13 Mar 2017 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 类 在 generating/parsing 字符串时默认使用 ISO 8601。您可以在我上面的示例中看到它们生成的输出。请注意,ZonedDateTime
通过在方括号中附加时区名称来扩展标准。
顺便说一句,如果您有完全符合 RFC 1123 的类似输入,请知道 java.time 提供了一个预定义的格式化程序对象,DateTimeFormatter.RFC_1123_DATE_TIME
。
我有一个场景,我正在获取各种不同模式的日期字符串(来自第三方电子邮件服务器)(例如):
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)")
我在这里缺少什么?
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 project, now in maintenance mode, advises migration to the java.time 类。
two timezone format in Joda-Time
Mon, 13 Mar 2017 19:00:10 +0530 (IST)
不,那是 零 time zone 格式。
+0530
是一个 offset-from-UTC, a number of hours and minutes away from UTC。
指定 proper time zone name in the format of continent/region
, such as America/Montreal
, Africa/Casablanca
或 Pacific/Auckland
。切勿使用 3-4 个字母的缩写,例如 EST
或 IST
,因为它们 不是 真正的时区,不是标准化的,甚至不是唯一的(!)。
由于无法可靠地解析 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[Asia/Jerusalem]
所以我建议你去掉最后的假缩写块。将剩余的文本解析为 OffsetDateTime
,它至少可以在时间轴上为您提供准确的时刻。调整为 Instant
的 UTC,因为您的大部分工作通常应该在 UTC 中完成,包括您的日志记录。
使用 String::substring
删除缩写。请注意,我们在子字符串搜索中将左括号前的 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, 13 Mar 2017 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 类 在 generating/parsing 字符串时默认使用 ISO 8601。您可以在我上面的示例中看到它们生成的输出。请注意,ZonedDateTime
通过在方括号中附加时区名称来扩展标准。
顺便说一句,如果您有完全符合 RFC 1123 的类似输入,请知道 java.time 提供了一个预定义的格式化程序对象,DateTimeFormatter.RFC_1123_DATE_TIME
。