alpaca-time 0.24.9

New York time and US trading-calendar semantics for the alpaca-rust workspace
Documentation
use crate::calendar::{hhmm_to_total_minutes, market_hours_for_date};
use crate::clock::{now, parts};
use crate::error::TimeResult;
use crate::types::{MarketSession, TimestampParts};

fn timestamp_parts(timestamp: &str) -> TimeResult<TimestampParts> {
    parts(Some(timestamp))
}

pub fn market_session_at(timestamp: &str) -> TimeResult<MarketSession> {
    let parts = timestamp_parts(timestamp)?;
    let hours = market_hours_for_date(&parts.date)?;

    if !hours.is_trading_date {
        return Ok(MarketSession::Closed);
    }

    let minutes = hhmm_to_total_minutes(&parts.hhmm_string)?;
    let premarket_open = hhmm_to_total_minutes(hours.premarket_open.as_deref().unwrap())?;
    let regular_open = hhmm_to_total_minutes(hours.regular_open.as_deref().unwrap())?;
    let regular_close = hhmm_to_total_minutes(hours.regular_close.as_deref().unwrap())?;
    let after_hours_close = hhmm_to_total_minutes(hours.after_hours_close.as_deref().unwrap())?;

    if minutes >= premarket_open && minutes < regular_open {
        Ok(MarketSession::Premarket)
    } else if minutes >= regular_open && minutes < regular_close {
        Ok(MarketSession::Regular)
    } else if minutes >= regular_close && minutes < after_hours_close {
        Ok(MarketSession::AfterHours)
    } else {
        Ok(MarketSession::Closed)
    }
}

pub fn is_premarket_at(timestamp: &str) -> bool {
    matches!(market_session_at(timestamp), Ok(MarketSession::Premarket))
}

pub fn is_regular_session_at(timestamp: &str) -> bool {
    matches!(market_session_at(timestamp), Ok(MarketSession::Regular))
}

pub fn is_after_hours_at(timestamp: &str) -> bool {
    matches!(market_session_at(timestamp), Ok(MarketSession::AfterHours))
}

pub fn is_in_window(timestamp: &str, start: &str, end: &str) -> bool {
    let Ok(parts) = timestamp_parts(timestamp) else {
        return false;
    };
    let Ok(current) = hhmm_to_total_minutes(&parts.hhmm_string) else {
        return false;
    };
    let Ok(start) = hhmm_to_total_minutes(start) else {
        return false;
    };
    let Ok(end) = hhmm_to_total_minutes(end) else {
        return false;
    };
    if end < start {
        return false;
    }
    current >= start && current < end
}

pub fn is_overnight_window(timestamp: &str) -> bool {
    timestamp_parts(timestamp)
        .map(|parts| parts.hour >= 20 || parts.hour < 4)
        .unwrap_or(false)
}

pub fn is_regular_session_now() -> bool {
    is_regular_session_at(&now())
}

pub fn is_overnight_now() -> bool {
    is_overnight_window(&now())
}

pub fn is_in_window_now(start: &str, end: &str) -> bool {
    is_in_window(&now(), start, end)
}