在 JAVA 中解析 RFC 2822 日期
Posted
技术标签:
【中文标题】在 JAVA 中解析 RFC 2822 日期【英文标题】:Parsing RFC 2822 date in JAVA 【发布时间】:2011-01-28 08:21:20 【问题描述】:我需要在 Java 中解析日期的 RFC 2822 字符串表示。示例字符串在这里:
2010 年 3 月 13 日星期六 11:29:05 -0800
它看起来很糟糕,所以我想确保我做的一切都是正确的,并且稍后会遇到奇怪的问题,因为通过 AM-PM/军事时间问题、UTC 时间问题、我不知道的问题来解释错误的日期预测等等……
谢谢!
【问题讨论】:
您是否尝试过使用java.text.SimpleDateFormat
?
这看起来很适合使用,我也担心正确的格式字符串是什么,看起来很容易搞砸。这似乎是 RFC 2822 的正确字符串:“EEE, d MMM yyyy HH:mm:ss Z”(根据下面的答案)
这太疯狂了,我从 SimpleDateFormat 官方页面上删除了它。他们的示例在技术上是正确的,但仍然是错误的:java.sun.com/j2se/1.4.2/docs/api/java/text/… 感谢您提醒我,这就是我的代码中的内容。
标准来源:Internet Message Format,向下滚动到 3.3 部分。日期和时间规范.
【参考方案1】:
DateTimeFormatter.RFC_1123_DATE_TIME
自从 Java 8 实现了新的日期时间类:java.time.ZonedDateTime
和 java.time.LocalDateTime
。 ZonedDateTime
支持解析几乎开箱即用的 RFC 字符串:
String rfcDate = "Tue, 4 Dec 2018 17:37:31 +0100 (CET)";
if (rfcDate.matches(".*[ ]\\(\\w\\w\\w\\)$"))
//Brackets with time zone are added sometimes, for example by JavaMail
//This must be removed before parsing
//from: "Tue, 4 Dec 2018 17:37:31 +0100 (CET)"
// to: "Tue, 4 Dec 2018 17:37:31 +0100"
rfcDate = rfcDate.substring(0, rfcDate.length() - 6);
//and now parsing...
DateTimeFormatter dateFormat = DateTimeFormatter.RFC_1123_DATE_TIME;
try
ZonedDateTime zoned = ZonedDateTime.parse(rfcDate, dateFormat);
LocalDateTime local = zoned.toLocalDateTime();
catch (DateTimeParseException e) ...
【讨论】:
解析的好答案。但我不建议将ZonedDateTime
转换为LocalDateTime
。这样一来,您就放弃了有价值的信息,即时区,而没有任何收获作为回报。就像没有特定货币上下文的金额一样,LocalDateTime
是模棱两可的,因此不能代表片刻。【参考方案2】:
有一个执行 RFC-2822 日期解析的 javax.mail 类:
javax.mail.internet.MailDateFormat
包括可选和过时的格式。
只要做:
new javax.mail.internet.MailDateFormat().parse("Sat, 13 Mar 2010 11:29:00 -0800")
new javax.mail.internet.MailDateFormat().parse("13 Mar 2010 11:29:00 -0800")
new javax.mail.internet.MailDateFormat().parse("13 Mar 2010 11:29 -0800")
它将正确解析这些有效的 RFC-2822 日期
至于其他旧的 DateFormatter,MailDateFormat
类不是线程安全的。
【讨论】:
【参考方案3】:这是执行您所要求的快速代码(使用 SimpleDateFormat)
String rfcDate = "Sat, 13 Mar 2010 11:29:05 -0800";
String pattern = "EEE, dd MMM yyyy HH:mm:ss Z";
SimpleDateFormat format = new SimpleDateFormat(pattern);
Date javaDate = format.parse(rfcDate);
//Done.
PS。我这里没有处理异常和并发(因为 SimpleDateFormat 在解析日期时不同步)。
【讨论】:
使用此链接docs.oracle.com/javase/8/docs/api/java/text/… 获取最新的可接受值 仅供参考,此答案现已过时。SimpleDateFormat
和 Date
类在几年前被 JSR 310 中定义的现代 java.time 类所取代。参见 the Answer by pathfinder78。【参考方案4】:
请记住,[day-of-week ","] 在 RFC-2822 中是可选的,因此建议的示例并未涵盖所有 RFC-2822 日期格式。此外,RFC-822 日期类型允许许多不同的时区表示法(obs-zone),“Z”格式说明符未涵盖这些符号。
我想没有简单的出路,除了寻找“,”和“-|+”来确定使用哪种模式。
【讨论】:
+1 指出星期几是可选的。我正在设置一个包含各种模式的 try/catch 瀑布,以不断尝试变化,直到找到可行的变化。 不仅day-of-week
是可选的,一天中的秒数也被声明为可选:time-of-day = hour ":" minute [ ":" second ]
【参考方案5】:
如果您的应用程序使用的是英语以外的其他语言,您可能希望通过使用备用 SimpleDateFormat 构造函数来强制日期解析/格式化的语言环境:
String pattern = "EEE, dd MMM yyyy HH:mm:ss Z";
SimpleDateFormat format = new SimpleDateFormat(pattern, Locale.ENGLISH);
【讨论】:
绝对需要设置区域设置。 Locale 的 android javadoc 建议使用 Locale.US 进行计算机到计算机的通信。 link +1 Locale.ROOT 也是一个不错的选择,但并非在所有系统上都可用。 @jasongilbert 你能解释一下为什么你说语言环境“绝对是必需的”吗?根据文档,它不是:docs.oracle.com/javase/7/docs/api/java/text/…——但也许你的意思是“如果……是必需的”,它是我感兴趣的“如果”的延续…… @AdamTuttle 如果您希望在默认语言环境不是美国英语时正确解析日期,则需要它。例如,如果用户在德国,则无法使用其默认区域设置正确解析日期。以上是关于在 JAVA 中解析 RFC 2822 日期的主要内容,如果未能解决你的问题,请参考以下文章
PHP 中的 RFC1123 和 RFC2822 日期时间格式有啥区别?
尝试在 moment.js 中转换 RFC2822 日期时出现“弃用警告:时刻构造回退到 js 日期”
如何在 Swift 中解析/创建格式为小数秒 UTC 时区(ISO 8601、RFC 3339)的日期时间戳?