【iso8601库】ISO 8601 低层解析器详解(parsers.rs)
parsers.rs是一个基于 nom 解析器组合库实现的 ISO 8601 格式解析器,支持 no_std 环境。
整体架构
设计理念
- 低层解析器: 提供基础的解析组件,可以组合使用
- 保留剩余输入: 允许继续解析其他内容或错误恢复
- 模块化设计: 每个解析器负责特定的格式部分
核心工具函数
数字解析函数
fn take_digits(i: &[u8]) -> IResult<&[u8], u32> {let (i, digits) = take_while(AsChar::is_dec_digit).parse(i)?;// 解析数字字符串为 u32
}
功能: 提取连续数字并转换为整数
fn take_n_digits(i: &[u8], n: usize) -> IResult<&[u8], u32> {let (i, digits) = take_while_m_n(n, n, AsChar::is_dec_digit)(i)?;// 精确提取 n 位数字
}
功能: 精确提取指定长度的数字
fn n_digit_in_range(i: &[u8], n: usize, range: impl core::ops::RangeBounds<u32>) -> IResult<&[u8], u32> {// 提取数字并验证范围
}
功能: 提取数字并验证其有效性(如月份 1-12)
符号解析
fn sign(i: &[u8]) -> IResult<&[u8], i32> {alt((tag("-"), tag("+"))).map(|s: &[u8]| match s {b"-" => -1,_ => 1,}).parse(i)
}
功能: 解析 + 或 - 符号,返回乘数因子
日期解析器
年份解析
fn date_year(i: &[u8]) -> IResult<&[u8], i32> {(opt(sign), |i| take_n_digits(i, 4)).map(|(s, year)| s.unwrap_or(1) * year as i32).parse(i)
}
支持格式: +2023, -2023, 2023
三种日期格式支持
1. 日历日期 (YMD)
fn date_ymd(i: &[u8]) -> IResult<&[u8], Date> {(date_year, opt(tag("-")), date_month, opt(tag("-")), date_day).map(|(year, _, month, _, day)| Date::YMD { year, month, day }).parse(i)
}
格式: YYYY-MM-DD 或 YYYYMMDD
2. 序数日期 (Ordinal)
fn date_ordinal(i: &[u8]) -> IResult<&[u8], Date> {separated_pair(date_year, opt(tag("-")), date_ord_day).map(|(year, ddd)| Date::Ordinal { year, ddd }).parse(i)
}
格式: YYYY-DDD(年-年内第几天)
3. 周日期 (Week)
fn date_iso_week(i: &[u8]) -> IResult<&[u8], Date> {(date_year, (opt(tag("-")), tag("W")), date_week, opt(tag("-")), date_week_day).map(|(year, _, ww, _, d)| Date::Week { year, ww, d }).parse(i)
}
格式: YYYY-Www-D(年-周数-周内天数)
时间解析器
时间组件解析
fn time_hour(i: &[u8]) -> IResult<&[u8], u32> // 0-24
fn time_minute(i: &[u8]) -> IResult<&[u8], u32> // 0-59
fn time_second(i: &[u8]) -> IResult<&[u8], u32> // 0-60(支持闰秒)
毫秒解析
fn fraction_millisecond(i: &[u8]) -> IResult<&[u8], u32> {let (i, mut digits) = take_while(AsChar::is_dec_digit).parse(i)?;// 处理 1-3 位小数,转换为毫秒
}
转换规则:
""→ 0"1"→ 100"12"→ 120"123"→ 123"1234"→ 123(截断)
完整时间解析
pub fn parse_time(i: &[u8]) -> IResult<&[u8], Time> {(time_hour, // HHopt(tag(":")), // :time_minute, // MMopt(preceded(opt(tag(":")), time_second)), // [SS]opt(preceded(one_of(",."), fraction_millisecond)), // [.(m*)]opt(alt((timezone_hour, timezone_utc))), // [(Z|+...|-...)]).map(|(h, _, m, s, ms, z)| {// 构建 Time 结构体})
}
支持格式: HH:MM:SS.sss+HH:MM
时区解析
fn timezone_hour(i: &[u8]) -> IResult<&[u8], (i32, i32)> {(sign, time_hour, opt(preceded(opt(tag(":")), time_minute))).map(|(s, h, m)| (s * (h as i32), s * (m.unwrap_or(0) as i32))).parse(i)
}fn timezone_utc(input: &[u8]) -> IResult<&[u8], (i32, i32)> {tag("Z").map(|_| (0, 0)).parse(input)
}
支持格式: Z, +08:00, -05:30
日期时间组合解析
pub fn parse_datetime(i: &[u8]) -> IResult<&[u8], DateTime> {separated_pair(parse_date, tag("T"), parse_time).map(|(d, t)| DateTime { date: d, time: t }).parse(i)
}
格式: Date + "T" + Time
持续时间解析
ISO 8601 持续时间格式
1. 组件格式 (YMDHMS)
fn duration_ymdhms(i: &[u8]) -> IResult<&[u8], Duration> {preceded(tag("P"), ...).map(|(y, mo, d, time)| {Duration::YMDHMS {year: y.unwrap_or(0),month: mo.unwrap_or(0),day: d.unwrap_or(0),hour: h,minute: mi,second: s,millisecond: ms,}})
}
格式: P[nY][nM][nD][T[nH][nM][nS]]
示例:
P1Y2M3DT4H5M6S= 1年2月3天4小时5分钟6秒PT1H30M= 1小时30分钟
2. 周格式
fn duration_weeks(i: &[u8]) -> IResult<&[u8], Duration> {preceded(tag("P"), duration_week).map(Duration::Weeks).parse(i)
}
格式: PnW
示例: P4W = 4周
3. 日期时间格式
fn duration_datetime(i: &[u8]) -> IResult<&[u8], Duration> {// 解析 PYYYY-MM-DDTHH:MM:SS 格式
}
格式: PYYYY-MM-DDTHH:MM:SS
设计优势
- 组合性: 使用
nom组合器,易于扩展和维护 - 错误恢复: 保留剩余输入,支持更好的错误处理
- 性能: 直接操作字节,避免字符串分配
- no_std 支持: 适用于嵌入式系统
- 完整性: 支持 ISO 8601 主要格式变体
使用示例
// 低层使用 - 保留剩余输入
let (remaining, date) = parse_date(b"2023-02-18 extra data")?;
// remaining = b" extra data"// 组合解析器
let (remaining, datetime) = parse_datetime(b"2023-02-18T17:08:08Z end")?;// 错误处理
match parse_time(b"25:61:00") {Ok((remaining, time)) => { /* 成功 */ }Err(nom::Err::Error(e)) => { /* 解析错误 */ }Err(nom::Err::Incomplete(_)) => { /* 需要更多输入 */ }
}
这个解析器库提供了灵活、高效的 ISO 8601 格式解析能力,特别适合需要精细控制解析过程的场景。
