temporal_core 0.0.1

An Intl-first date time library
Documentation
use std::time::{SystemTime, Duration, UNIX_EPOCH};

use crate::iso;

#[derive(Default)]
pub struct NominalDuration {
    is_negative: bool,
    years: u32,
    months: u32,
    weeks: u32,
    days: u32,
    hours: u32,
    minutes: u32,
    seconds: u32,
    milli_seconds: u32,
    micro_seconds: u32,
    nano_seconds: u32,
}

impl NominalDuration {
    fn signum(&self) -> i32 {
        if self.is_negative {
            -1
        } else {
            1
        }
    }

    pub fn from_years(years: i32) -> Self {
        Self {
            years: years.abs().try_into().unwrap(),
            ..Self::default()
        }
    }

    pub fn years(&self) -> i32 {
        self.years as i32 * self.signum()
    }
    pub fn months(&self) -> i32 {
        self.months as i32 * self.signum()
    }
    pub fn weeks(&self) -> i32 {
        self.weeks as i32 * self.signum()
    }
    pub fn days(&self) -> i32 {
        self.days as i32 * self.signum()
    }
}

pub struct SignedDuration {
    secs: i64,
    nanos: u32, // Always 0 <= nanos < NANOS_PER_SEC
}

impl TryFrom<SignedDuration> for Duration {
    type Error = <i64 as TryFrom<u64>>::Error;

    fn try_from(value: SignedDuration) -> Result<Self, Self::Error> {
        Ok(Self::new(value.as_secs().try_into()?, value.subsec_nanos()))
    }
}

impl TryFrom<Duration> for SignedDuration {
    type Error = <i64 as TryFrom<u64>>::Error;

    fn try_from(value: Duration) -> Result<Self, Self::Error> {
        Ok(Self::new_unchecked(value.as_secs().try_into()?, value.subsec_nanos()))
    }
}

impl SignedDuration {
    pub fn now() -> Self {
        let now = SystemTime::now();
        Self::from_system_time_since_unix(now)
    }

    pub(crate) fn new_unchecked(secs: i64, nanos: u32) -> Self {
        Self { secs, nanos }
    }

    pub fn new(mut secs: i64, nanos: i32) -> Self {
        secs += nanos.div_euclid(1000_000_000) as i64;
        Self {
            secs,
            nanos: nanos.rem_euclid(1000_000_000) as u32,
        }
    }

    pub fn from_iso_string(s: &str) -> Option<Self> {
        let i = iso::parse(s)?;
        let offset_secs = match i.timezone_offset? {
            iso::IsoOffset::Z => 0,
            iso::IsoOffset::Numeric(x) => x.to_seconds() as i64,
        };
        let time_secs = if let Some(x) = i.time {
            x.to_second().into()
        } else {
            0
        };
        let secs = i.date.to_epoch_second() + time_secs + offset_secs;
        Some(Self::new(secs, 0))
    }

    pub(crate) fn from_system_time_since_unix(s: SystemTime) -> Self {
        match s.duration_since(UNIX_EPOCH) {
            Ok(d) => SignedDuration {
                secs: d.as_secs() as i64,
                nanos: d.subsec_nanos(),
            },
            Err(e) => {
                let d = e.duration();
                SignedDuration {
                    secs: -(d.as_secs() as i64),
                    nanos: d.subsec_nanos(),
                }
            }
        }
    }

    pub fn from_secs(secs: i64) -> Self {
        Self { secs, nanos: 0 }
    }

    pub fn as_secs(&self) -> i64 {
        self.secs
    }

    pub fn subsec_nanos(&self) -> u32 {
        self.nanos
    }
}