use crate::date::Date;
use crate::datetime::DateTime;
use crate::error::{RstimeError, RstimeResult};
use crate::time::Time;
pub fn parse_datetime(s: &str, fmt: &str) -> RstimeResult<DateTime> {
let tokens = tokenize_format(fmt);
let mut year = 0i32;
let mut month = 1u8;
let mut day = 1u8;
let mut hour = 0u8;
let mut minute = 0u8;
let mut second = 0u8;
let mut millisecond = 0u16;
let mut pos = 0usize;
let chars: Vec<char> = s.chars().collect();
for token in &tokens {
match token {
Token::Literal(lit) => {
let lit_chars: Vec<char> = lit.chars().collect();
if pos + lit_chars.len() > chars.len() {
return Err(RstimeError::new(format!(
"期望文本 '{}' 但输入已结束",
lit
)));
}
let actual: String = chars[pos..pos + lit_chars.len()].iter().collect();
if actual != *lit {
return Err(RstimeError::new(format!(
"位置 {} 期望 '{}',实际得到 '{}'",
pos, lit, actual
)));
}
pos += lit_chars.len();
}
Token::Specifier(spec) => {
let value = read_number(&chars, &mut pos, spec.len);
match spec.kind {
SpecKind::Year4 => year = value as i32,
SpecKind::Year2 => year = 2000 + value as i32,
SpecKind::Month => month = value as u8,
SpecKind::Day => day = value as u8,
SpecKind::Hour24 => hour = value as u8,
SpecKind::Hour12 => hour = value as u8,
SpecKind::Minute => minute = value as u8,
SpecKind::Second => second = value as u8,
SpecKind::Millisecond => millisecond = value as u16,
}
}
}
}
let date = Date::new(year, month, day);
let time = Time::new(hour, minute, second, millisecond);
Ok(DateTime::new(date, time))
}
pub fn parse_date(s: &str, fmt: &str) -> RstimeResult<Date> {
let dt = parse_datetime(s, fmt)?;
Ok(dt.date)
}
pub fn parse_time(s: &str, fmt: &str) -> RstimeResult<Time> {
let dt = parse_datetime(&format!("2000-01-01 {}", s), &format!("{{YYYY}}-{{MM}}-{{DD}} {}", fmt))?;
Ok(dt.time)
}
pub fn parse_iso8601(s: &str) -> RstimeResult<DateTime> {
let s = s.trim();
if s.len() >= 23
&& s.as_bytes()[4] == b'-'
&& s.as_bytes()[7] == b'-'
&& (s.as_bytes()[10] == b'T' || s.as_bytes()[10] == b' ')
&& s.as_bytes()[13] == b':'
&& s.as_bytes()[16] == b':'
&& s.as_bytes()[19] == b'.'
{
return parse_datetime(s, "{YYYY}-{MM}-{DD}T{HH}:{mm}:{ss}.{SSS}");
}
if s.len() >= 19
&& s.as_bytes()[4] == b'-'
&& s.as_bytes()[7] == b'-'
&& (s.as_bytes()[10] == b'T' || s.as_bytes()[10] == b' ')
{
return parse_datetime(s, "{YYYY}-{MM}-{DD}T{HH}:{mm}:{ss}");
}
if s.len() >= 10 && s.as_bytes()[4] == b'-' && s.as_bytes()[7] == b'-' {
let dt = parse_datetime(s, "{YYYY}-{MM}-{DD}")?;
return Ok(DateTime::new(dt.date, Time::MIDNIGHT));
}
Err(RstimeError::new(format!("无法识别的 ISO 8601 格式: '{}'", s)))
}
enum SpecKind {
Year4,
Year2,
Month,
Day,
Hour24,
Hour12,
Minute,
Second,
Millisecond,
}
struct FormatSpec {
kind: SpecKind,
len: usize,
}
enum Token {
Literal(String),
Specifier(FormatSpec),
}
fn tokenize_format(fmt: &str) -> Vec<Token> {
let mut tokens = Vec::new();
let chars: Vec<char> = fmt.chars().collect();
let mut i = 0;
while i < chars.len() {
if chars[i] == '{' {
let mut end = i + 1;
while end < chars.len() && chars[end] != '}' {
end += 1;
}
if end < chars.len() && end > i + 1 {
let token: String = chars[i + 1..end].iter().collect();
if let Some(spec) = classify_token(&token) {
tokens.push(Token::Specifier(spec));
i = end + 1;
continue;
}
}
}
let start = i;
while i < chars.len() && (chars[i] != '{' || i == start) {
if chars[i] == '{' {
let mut j = i + 1;
while j < chars.len() && chars[j] != '}' {
j += 1;
}
if j < chars.len() {
let t: String = chars[i + 1..j].iter().collect();
if classify_token(&t).is_some() {
break;
}
}
}
i += 1;
}
if i > start {
tokens.push(Token::Literal(chars[start..i].iter().collect()));
}
}
tokens
}
fn classify_token(token: &str) -> Option<FormatSpec> {
match token {
"YYYY" => Some(FormatSpec {
kind: SpecKind::Year4,
len: 4,
}),
"YY" => Some(FormatSpec {
kind: SpecKind::Year2,
len: 2,
}),
"MM" => Some(FormatSpec {
kind: SpecKind::Month,
len: 2,
}),
"M" => Some(FormatSpec {
kind: SpecKind::Month,
len: 1,
}),
"DD" => Some(FormatSpec {
kind: SpecKind::Day,
len: 2,
}),
"D" => Some(FormatSpec {
kind: SpecKind::Day,
len: 1,
}),
"HH" => Some(FormatSpec {
kind: SpecKind::Hour24,
len: 2,
}),
"H" => Some(FormatSpec {
kind: SpecKind::Hour24,
len: 1,
}),
"hh" => Some(FormatSpec {
kind: SpecKind::Hour12,
len: 2,
}),
"h" => Some(FormatSpec {
kind: SpecKind::Hour12,
len: 1,
}),
"mm" => Some(FormatSpec {
kind: SpecKind::Minute,
len: 2,
}),
"m" => Some(FormatSpec {
kind: SpecKind::Minute,
len: 1,
}),
"ss" => Some(FormatSpec {
kind: SpecKind::Second,
len: 2,
}),
"s" => Some(FormatSpec {
kind: SpecKind::Second,
len: 1,
}),
"SSS" => Some(FormatSpec {
kind: SpecKind::Millisecond,
len: 3,
}),
_ => None,
}
}
fn read_number(chars: &[char], pos: &mut usize, len: usize) -> u32 {
let end = (*pos + len).min(chars.len());
let num_str: String = chars[*pos..end].iter().collect();
*pos = end;
num_str.parse::<u32>().unwrap_or(0)
}