在 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.ZonedDateTimejava.time.LocalDateTimeZonedDateTime 支持解析几乎开箱即用的 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/… 获取最新的可接受值 仅供参考,此答案现已过时。 SimpleDateFormatDate 类在几年前被 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)的日期时间戳?

如何在 Swift 中解析/创建格式为小数秒 UTC 时区(ISO 8601、RFC 3339)的日期时间戳?

将 Python 日期时间转换为 rfc 2822

golang解析git log时间