如何解析RSS源带时区缩写的非标准日期字符串为Java Instant?
非标RSS日期格式解析方案
核心原因
解析失败问题主要来自两个点:
- 存在可选的星期前缀,固定格式串无法同时兼容带/不带星期头的两种输入
- 时区为区域缩写(如EDT、HKT),内置的格式化器没有配置对应的缩写映射,且默认Locale不是英文时会无法识别英文的月份/星期缩写
原生Java.time实现方案(无额外依赖)
通过DateTimeFormatterBuilder构造支持可选规则的格式化器,同时补充自定义时区缩写映射即可:
import java.time.Instant; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; public class RssDateParser { // 自定义时区缩写映射,可根据实际遇到的缩写扩展 private static final Map<Long, String> CUSTOM_ZONE_MAPPING = new HashMap<>(); static { CUSTOM_ZONE_MAPPING.put((long) 'E' << 16 | 'D' << 8 | 'T', "America/New_York"); CUSTOM_ZONE_MAPPING.put((long) 'H' << 16 | 'K' << 8 | 'T', "Asia/Hong_Kong"); CUSTOM_ZONE_MAPPING.put((long) 'C' << 16 | 'S' << 8 | 'T', "Asia/Shanghai"); CUSTOM_ZONE_MAPPING.put((long) 'P' << 16 | 'S' << 8 | 'T', "America/Los_Angeles"); } private static final Set<String> ZONE_ID_SET = CUSTOM_ZONE_MAPPING.values().stream().collect(Collectors.toSet()); private static final DateTimeFormatter RSS_DATE_FORMATTER = new DateTimeFormatterBuilder() // 匹配可选的星期前缀:如 "Tue, ",无此前缀也不会报错 .optionalStart() .appendPattern("EEE, ") .optionalEnd() // 核心日期时间规则 .appendPattern("dd MMM yyyy HH:mm:ss ") // 绑定自定义时区缩写映射 .appendZoneText(DateTimeFormatter.TextStyle.SHORT, ZONE_ID_SET) // 强制使用英文Locale,避免中文等非英文系统环境下无法识别英文月份/星期缩写 .toFormatter(Locale.US); public static Instant parse(String dateStr) { return ZonedDateTime.parse(dateStr.trim(), RSS_DATE_FORMATTER).toInstant(); } }
测试效果
- 输入
Tue, 25 May 2021 00:00:00 EDT→ 输出Instant:2021-05-25T04:00:00Z - 输入
03 Jun 2021 18:35:00 HKT→ 输出Instant:2021-06-03T10:35:00Z
后续如果遇到新的格式变体,在Builder中新增optionalStart/optionalEnd包裹的对应规则即可;遇到新的时区缩写,往CUSTOM_ZONE_MAPPING中补充映射即可。
第三方依赖兜底方案
如果不介意引入轻量第三方依赖,可使用Apache Commons Lang的DateUtils,传入多套候选格式串自动尝试匹配,代码更简洁:
<!-- 引入依赖 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.14.0</version> </dependency>
import org.apache.commons.lang3.time.DateUtils; import java.time.Instant; import java.util.Locale; public class RssDateParser { private static final String[] DATE_PATTERNS = { "EEE, dd MMM yyyy HH:mm:ss zzz", "dd MMM yyyy HH:mm:ss zzz", "yyyy-MM-dd'T'HH:mm:ssX", // 兼容标准ISO格式 "EEE, dd MMM yyyy HH:mm:ss Z" // 兼容标准RFC1123格式 }; public static Instant parse(String dateStr) throws Exception { return DateUtils.parseDate(dateStr, Locale.US, DATE_PATTERNS).toInstant(); } }
该方案后续新增格式只需往DATE_PATTERNS数组中追加对应格式串即可,无需调整复杂的Builder逻辑。
内容的提问来源于stack exchange,提问作者Niklas
相关产品推荐
相关产品推荐

