use crate::{Dt, JD_2000_2_451_545, SEC_PER_DAYI64, SEC_PER_HALF_DAYI64, Scale, TimeParts};
#[inline]
pub(crate) fn parse_yymmdd(input: &str) -> Option<Dt> {
let parsed = TimeParts::from_str("%y%m%d", input, true, true, false).ok()?;
parsed.to_time_point().ok()
}
pub(crate) fn parse_yyyy_mm(bytes: &[u8]) -> Option<Dt> {
let len = bytes.len();
let (sign, mut pos) = match bytes.get(0) {
Some(b'+') => (1i32, 1),
Some(b'-') => (-1i32, 1),
_ => (1i32, 0),
};
let mut year = 0i32;
let year_start = pos;
while pos < len && bytes[pos].is_ascii_digit() {
year = year * 10 + (bytes[pos] - b'0') as i32;
pos += 1;
}
if pos == year_start || pos == len {
return None;
}
if !matches!(bytes.get(pos), Some(b'-' | b'/' | b'.')) {
return None;
}
pos += 1;
let month = match len - pos {
1 => {
let b = bytes[pos];
if !b.is_ascii_digit() {
return None;
}
(b - b'0') as u32
}
2 => {
let b1 = bytes[pos];
let b2 = bytes[pos + 1];
if !b1.is_ascii_digit() || !b2.is_ascii_digit() {
return None;
}
10 * (b1 - b'0') as u32 + (b2 - b'0') as u32
}
_ => return None,
};
if month == 0 || month > 12 {
return None;
}
year *= sign;
if year < crate::MIN_YEAR || year > crate::MAX_YEAR {
return None;
}
let jdn = Dt::ymd_to_jdn(year as i64, month as u8, 1);
let days_since_j2000 = jdn - JD_2000_2_451_545;
let sec = days_since_j2000 * SEC_PER_DAYI64 - SEC_PER_HALF_DAYI64;
Some(Dt::from(sec, 0, Scale::UTC))
}
pub(crate) fn parse_yyyymm(s: &str) -> Option<Dt> {
let (y_str, m_str) = if let Some(rest) = s.strip_prefix('-') {
if rest.len() != 6 {
return None;
}
(&rest[0..4], &rest[4..6])
} else {
if s.len() != 6 {
return None;
}
(&s[0..4], &s[4..6])
};
if let (Ok(mut y), Ok(m)) = (y_str.parse::<i32>(), m_str.parse::<u32>()) {
if s.starts_with('-') {
y = -y;
}
if (1..=12).contains(&m) && (crate::MIN_YEAR..=crate::MAX_YEAR).contains(&y) {
let parsed =
TimeParts::from_str("%Y%m", s.trim_start_matches('-'), true, true, true).ok()?;
return parsed.to_time_point().ok();
}
}
None
}