Skip to content

Commit bb82ff7

Browse files
committed
WIP: Swap chrono::NaiveDateTime::parse_from_str for AI SLOP
1 parent 758ef47 commit bb82ff7

1 file changed

Lines changed: 102 additions & 43 deletions

File tree

src/datetime.rs

Lines changed: 102 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -71,52 +71,111 @@ impl DateTime {
7171
Self::from_epoch_msec_tz(epoch_msec, 0)
7272
}
7373
pub fn from_iso_str(iso_str: &str) -> Result<DateTime, String> {
74-
const PATTERN: &str = "2020-02-03T11:59:43";
75-
if iso_str.len() >= PATTERN.len() {
76-
let s = iso_str;
77-
let naive_str = &s[..PATTERN.len()];
78-
if let Ok(ndt) = chrono::NaiveDateTime::parse_from_str(naive_str, "%Y-%m-%dT%H:%M:%S") {
79-
let mut msec = 0;
80-
let mut offset = 0;
81-
let mut rest = &s[PATTERN.len()..];
82-
if matches!(rest.as_bytes().first(), Some(b'.')) {
83-
rest = &rest[1..];
84-
if rest.len() >= 3 {
85-
match rest[..3].parse::<i32>() {
86-
Ok(ms) => {
87-
msec = ms;
88-
rest = &rest[3..];
89-
}
90-
Err(err) => {
91-
return Err(format!("Parsing DateTime msec part error: {err}, in '{iso_str}"))
92-
}
93-
}
94-
}
95-
}
96-
if !rest.is_empty() {
97-
if rest.len() == 1 && *rest.as_bytes().first().expect("len() is 1") == b'Z' {
98-
} else if rest.len() == 3 {
99-
if let Ok(hrs) = rest.parse::<i32>() {
100-
offset = 60 * 60 * hrs;
101-
} else {
102-
return Err(format!("Invalid DateTime TZ(3) part: '{rest}, date time: {iso_str}"))
103-
}
104-
} else if rest.len() == 5 {
105-
if let Ok(hrs) = rest.parse::<i32>() {
106-
offset = 60 * (60 * (hrs / 100) + (hrs % 100));
107-
} else {
108-
return Err(format!("Invalid DateTime TZ(5) part: '{rest}, date time: {iso_str}"))
109-
}
110-
} else {
111-
return Err(format!("Invalid DateTime TZ part: '{rest}, date time: {iso_str}"))
112-
}
74+
const BASE_LEN: usize = 19;
75+
let b = iso_str.as_bytes();
76+
if b.len() < BASE_LEN {
77+
return Err(format!("Invalid DateTime: '{iso_str:?}"));
78+
}
79+
if b[4] != b'-' || b[7] != b'-' || b[10] != b'T' || b[13] != b':' || b[16] != b':' {
80+
return Err(format!("Invalid DateTime: '{iso_str:?}"));
81+
}
82+
fn parse_digits(slice: &[u8]) -> Option<i32> {
83+
let mut v = 0i32;
84+
for &c in slice {
85+
if c < b'0' || c > b'9' {
86+
return None;
87+
}
88+
v = v * 10 + ((c - b'0') as i32);
89+
}
90+
Some(v)
91+
}
92+
let year = parse_digits(&b[0..4]).ok_or_else(|| format!("Invalid DateTime: '{iso_str:?}"))?;
93+
let month = parse_digits(&b[5..7]).ok_or_else(|| format!("Invalid DateTime: '{iso_str:?}"))?;
94+
let day = parse_digits(&b[8..10]).ok_or_else(|| format!("Invalid DateTime: '{iso_str:?}"))?;
95+
let hour = parse_digits(&b[11..13]).ok_or_else(|| format!("Invalid DateTime: '{iso_str:?}"))?;
96+
let minute = parse_digits(&b[14..16]).ok_or_else(|| format!("Invalid DateTime: '{iso_str:?}"))?;
97+
let second = parse_digits(&b[17..19]).ok_or_else(|| format!("Invalid DateTime: '{iso_str:?}"))?;
98+
99+
let mut idx = BASE_LEN;
100+
let mut msec: i32 = 0;
101+
102+
if idx < b.len() && b[idx] == b'.' {
103+
idx += 1;
104+
let mut digits = 0usize;
105+
let mut val = 0i32;
106+
while idx < b.len() && digits < 3 {
107+
let c = b[idx];
108+
if c < b'0' || c > b'9' {
109+
break;
110+
}
111+
val = val * 10 + ((c - b'0') as i32);
112+
idx += 1;
113+
digits += 1;
114+
}
115+
if digits == 0 {
116+
return Err(format!("Parsing DateTime msec part error, in '{iso_str}"));
117+
}
118+
if digits == 1 {
119+
msec = val * 100;
120+
} else if digits == 2 {
121+
msec = val * 10;
122+
} else {
123+
msec = val;
124+
}
125+
while idx < b.len() {
126+
let c = b[idx];
127+
if c < b'0' || c > b'9' {
128+
break;
129+
}
130+
idx += 1;
131+
}
132+
}
133+
134+
let mut offset_seconds: i32 = 0;
135+
if idx < b.len() {
136+
let c = b[idx];
137+
if c == b'Z' {
138+
if idx + 1 != b.len() {
139+
return Err(format!("Invalid DateTime TZ part: '{}', date time: {iso_str}", &iso_str[idx..]));
140+
}
141+
} else if c == b'+' || c == b'-' {
142+
let sign = if c == b'-' { -1 } else { 1 };
143+
idx += 1;
144+
if idx >= b.len() {
145+
return Err(format!("Invalid DateTime TZ part: '{}', date time: {iso_str}", &iso_str[idx.saturating_sub(1)..]));
146+
}
147+
let rem = &b[idx..];
148+
if rem.len() >= 5 && rem[2] == b':' {
149+
let hh = parse_digits(&rem[0..2]).ok_or_else(|| format!("Invalid DateTime TZ(3) part: '{}', date time: {iso_str}", &iso_str[idx - 1..]))?;
150+
let mm = parse_digits(&rem[3..5]).ok_or_else(|| format!("Invalid DateTime TZ(3) part: '{}', date time: {iso_str}", &iso_str[idx - 1..]))?;
151+
if rem.len() != 5 {
152+
return Err(format!("Invalid DateTime TZ part: '{}', date time: {iso_str}", &iso_str[idx - 1..]));
113153
}
114-
let epoch_msec = (ndt.and_utc().timestamp() - i64::from(offset)) * 1000 + i64::from(msec);
115-
let dt = DateTime::from_epoch_msec_tz(epoch_msec, offset);
116-
return Ok(dt)
154+
offset_seconds = sign * (hh * 3600 + mm * 60);
155+
} else if rem.len() == 2 {
156+
let hh = parse_digits(&rem[0..2]).ok_or_else(|| format!("Invalid DateTime TZ(3) part: '{}', date time: {iso_str}", &iso_str[idx - 1..]))?;
157+
offset_seconds = sign * (hh * 3600);
158+
} else if rem.len() == 4 {
159+
let hh = parse_digits(&rem[0..2]).ok_or_else(|| format!("Invalid DateTime TZ(5) part: '{}', date time: {iso_str}", &iso_str[idx - 1..]))?;
160+
let mm = parse_digits(&rem[2..4]).ok_or_else(|| format!("Invalid DateTime TZ(5) part: '{}', date time: {iso_str}", &iso_str[idx - 1..]))?;
161+
offset_seconds = sign * (hh * 3600 + mm * 60);
162+
} else {
163+
return Err(format!("Invalid DateTime TZ part: '{}', date time: {iso_str}", &iso_str[idx - 1..]));
117164
}
165+
} else {
166+
return Err(format!("Invalid DateTime TZ part: '{}', date time: {iso_str}", &iso_str[idx..]));
118167
}
119-
Err(format!("Invalid DateTime: '{iso_str:?}"))
168+
}
169+
170+
let naive = match chrono::NaiveDate::from_ymd_opt(year, month as u32, day as u32)
171+
.and_then(|d| chrono::NaiveTime::from_hms_milli_opt(hour as u32, minute as u32, second as u32, msec as u32).map(|t| d.and_time(t))) {
172+
Some(ndt) => ndt,
173+
None => return Err(format!("Invalid DateTime: '{iso_str:?}")),
174+
};
175+
176+
let epoch_msec = (naive.and_utc().timestamp() - i64::from(offset_seconds)) * 1000 + i64::from(msec);
177+
let dt = DateTime::from_epoch_msec_tz(epoch_msec, offset_seconds);
178+
Ok(dt)
120179
}
121180
pub fn epoc_msec_utc_offset(self) -> (i64, i32) {
122181
let msec= self.0 / (TZ_MASK + 1);

0 commit comments

Comments
 (0)