hayson-engine 0.14.0

Storage engine for hayson document databases — the embeddable core behind hayson-server, hayson-cli and hayson-browser
Documentation
use chrono::{NaiveDate, NaiveTime};

/// Parses the ISO 8601 subset accepted by the TS engine's `ISO_DATE` regex into epoch milliseconds.
///
/// NOTE: a date-time with no zone is treated as UTC (JS treats it as local time); local-timezone
/// parity isn't reproducible in a deterministic test, so this is a documented simplification.
pub fn parse_iso_timestamp(s: &str) -> Option<i64> {
    if s.len() < 10 {
        return None;
    }
    let date = NaiveDate::parse_from_str(&s[0..10], "%Y-%m-%d").ok()?;
    if s.len() == 10 {
        return Some(date.and_hms_opt(0, 0, 0)?.and_utc().timestamp_millis());
    }
    let sep = s.as_bytes().get(10).copied()?;
    if sep != b'T' && sep != b' ' {
        return None;
    }
    let rest = &s[11..];

    let mut zone_start = None;
    for (i, c) in rest.char_indices() {
        if i < 5 {
            continue;
        }
        if c == 'Z' || c == '+' || c == '-' {
            zone_start = Some(i);
            break;
        }
    }
    let (time_str, zone_str) = match zone_start {
        Some(i) => (&rest[..i], Some(&rest[i..])),
        None => (rest, None),
    };

    let time = if time_str.len() == 5 {
        NaiveTime::parse_from_str(time_str, "%H:%M").ok()?
    } else if time_str.contains('.') {
        NaiveTime::parse_from_str(time_str, "%H:%M:%S%.f").ok()?
    } else {
        NaiveTime::parse_from_str(time_str, "%H:%M:%S").ok()?
    };
    let naive = date.and_time(time);

    match zone_str {
        None | Some("Z") => Some(naive.and_utc().timestamp_millis()),
        Some(zone) => {
            let sign: i64 = if zone.starts_with('-') { -1 } else { 1 };
            let digits: String = zone.chars().filter(|c| c.is_ascii_digit()).collect();
            if digits.len() < 4 {
                return None;
            }
            let hh: i64 = digits[0..2].parse().ok()?;
            let mm: i64 = digits[2..4].parse().ok()?;
            let offset_secs = sign * (hh * 3600 + mm * 60);
            Some(naive.and_utc().timestamp_millis() - offset_secs * 1000)
        }
    }
}