deep-time 0.1.0-alpha.6

High-precision, no-std, no-alloc date-time library, leap-seconds, time scales, relativistic time, and a powerful date & duration parser
Documentation
use crate::{DateClassification, OffsetType, TimeType};
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;

pub(crate) fn get_compatible_time_suffixes(class: &DateClassification) -> Vec<String> {
    if class.time.is_none() {
        return get_offset_suffix(class).map_or_else(Vec::new, |s| vec![s]);
    }

    let mut time_bases = build_time_bases(class);
    if let Some(suff) = get_offset_suffix(class) {
        for base in &mut time_bases {
            base.push_str(&suff);
        }
    }
    time_bases
}

fn get_offset_suffix(class: &DateClassification) -> Option<String> {
    let offset = class.offset;
    match offset {
        OffsetType::None => None,
        OffsetType::Zulu => Some(String::from("Z")),

        _ => {
            let prefix = if offset.is_bracketed() {
                if class.space_before_bracket { " " } else { "" }
            } else {
                if class.space_before_offset { " " } else { "" }
            };

            let mut s = String::with_capacity(20);
            s.push_str(prefix);

            match offset {
                OffsetType::Iana => s.push_str("%Q"),
                OffsetType::InBracketIana => s.push_str("[%Q]"),
                OffsetType::Hm { colon } => s.push_str(offset_spec(colon, false)),
                OffsetType::HmS { colon } => s.push_str(offset_spec(colon, true)),
                OffsetType::InBracketHm { colon } => {
                    s.push('[');
                    s.push_str(offset_spec(colon, false));
                    s.push(']');
                }
                OffsetType::InBracketHmS { colon } => {
                    s.push('[');
                    s.push_str(offset_spec(colon, true));
                    s.push(']');
                }
                OffsetType::HmAndIana { colon } => {
                    s.push_str(offset_spec(colon, false));
                    s.push_str(" %Q");
                }
                OffsetType::HmSAndIana { colon } => {
                    s.push_str(offset_spec(colon, true));
                    s.push_str(" %Q");
                }
                OffsetType::HmAndInbracketIana { colon } => {
                    s.push_str(offset_spec(colon, false));
                    s.push_str("[%Q]");
                }
                OffsetType::HmSAndInbracketIana { colon } => {
                    s.push_str(offset_spec(colon, true));
                    s.push_str("[%Q]");
                }
                _ => return None,
            }
            Some(s)
        }
    }
}

#[inline]
fn build_time_bases(class: &DateClassification) -> Vec<String> {
    let mut suffixes: Vec<String> = Vec::with_capacity(4);
    let connector_str = class.connector.as_str();
    let use_fractional = class.has_fractional;
    let (preferred_has_seconds, time_colons) = match class.time {
        TimeType::Hm { colons } => (false, colons),
        TimeType::HmS { colons } => (true, colons),
        TimeType::None => return suffixes,
    };
    let time_sep = if time_colons { ":" } else { "" };
    let is_12h = class.has_ampm;

    let include_extra = !use_fractional && class.is_pure_numeric;

    let has_seconds_options: Vec<bool> = if use_fractional {
        vec![true]
    } else if include_extra {
        if preferred_has_seconds {
            vec![true, false]
        } else {
            vec![false, true]
        }
    } else {
        vec![preferred_has_seconds]
    };

    if is_12h {
        for &has_seconds in &has_seconds_options {
            let mut base = String::with_capacity(32);
            base.push_str(connector_str);
            base.push_str("%I");
            base.push_str(time_sep);
            base.push_str("%M");

            if use_fractional {
                base.push_str(time_sep);
                base.push_str("%S%.f %p");
                suffixes.push(base);
            } else if has_seconds {
                base.push_str(time_sep);
                base.push_str("%S %p");
                suffixes.push(base);
            } else {
                base.push_str(" %p");
                suffixes.push(base);
            }
        }
    } else {
        for hour in ["%H", "%k"] {
            for &has_seconds in &has_seconds_options {
                let mut base = String::with_capacity(32);
                base.push_str(connector_str);
                base.push_str(hour);
                base.push_str(time_sep);
                base.push_str("%M");

                if use_fractional {
                    base.push_str(time_sep);
                    base.push_str("%S%.f");
                    suffixes.push(base);
                } else if has_seconds {
                    base.push_str(time_sep);
                    base.push_str("%S");
                    suffixes.push(base);
                } else {
                    suffixes.push(base);
                }
            }
        }
    }
    suffixes
}

#[inline]
fn offset_spec(colon: bool, has_seconds: bool) -> &'static str {
    match (has_seconds, colon) {
        (true, true) => "%::z",
        (true, false) => "%z",
        (false, true) => "%:z",
        (false, false) => "%z",
    }
}