ako 0.0.3

Ako is a Rust crate that offers a practical and human-friendly approach to creating, manipulating, formatting and converting dates, times and timestamps.
Documentation
use core::fmt::{self, Debug, Formatter};

use crate::calendar::Iso;
use crate::{AsDate, AsTime, Calendar, Date, DateTime, Moment, Month, Offset, PlainTime, Year};

/// A **date and time** on a calendar, independent of any time zone.
#[derive(Clone, Copy)]
pub struct PlainDateTime<C: Calendar = Iso> {
    pub(crate) date: Date<C>,
    pub(crate) time: PlainTime,
}

// Conversion From
impl<C: Calendar> PlainDateTime<C> {
    /// Returns the date and time of the given `moment`, projected on to the given `calendar`.
    pub const fn from_moment(calendar: C, moment: Moment) -> Self {
        let (secs, nsec) = moment.to_unix();
        let date = Date::from_unix_timestamp(calendar, secs);
        let time = PlainTime::from_unix_timestamp(secs, nsec);

        Self { date, time }
    }
}

// Components
impl<C: Calendar> PlainDateTime<C> {
    /// Gets the calendar that this date is interpreted in.
    #[must_use]
    #[inline]
    pub const fn calendar(self) -> C {
        self.date.calendar()
    }

    /// Gets the year of this date.
    #[must_use]
    #[inline]
    pub fn year(self) -> Year<C> {
        self.date.year()
    }

    /// Gets the month within the year.
    #[must_use]
    #[inline]
    pub fn month(self) -> Month<C> {
        self.date.month()
    }

    /// Gets the day within the month.
    #[must_use]
    #[inline]
    pub fn day(self) -> u8 {
        self.date.day()
    }

    /// Gets the day within the year.
    #[must_use]
    #[inline]
    pub fn day_of_year(self) -> u16 {
        self.date.day_of_year()
    }

    /// Gets the hour within the day.
    #[must_use]
    #[inline]
    pub const fn hour(self) -> u8 {
        self.time.hour()
    }

    /// Gets the minute within the hour.
    #[must_use]
    #[inline]
    pub const fn minute(self) -> u8 {
        self.time.minute()
    }

    /// Gets the second within the minute.
    #[must_use]
    #[inline]
    pub const fn second(self) -> u8 {
        self.time.second()
    }

    /// Gets the millisecond within the second.
    #[must_use]
    #[inline]
    pub const fn millisecond(self) -> u16 {
        self.time.millisecond()
    }

    /// Gets the microsecond within the second.
    #[must_use]
    #[inline]
    pub const fn microsecond(self) -> u32 {
        self.time.microsecond()
    }

    /// Gets the nanosecond within the second.
    #[must_use]
    #[inline]
    pub const fn nanosecond(self) -> u32 {
        self.time.nanosecond()
    }
}

// Composition
impl<C: Calendar> PlainDateTime<C> {
    /// Converts this [`PlainDateTime`] to a [`DateTime`] with the assumption that
    /// this date and time represents a moment at UTC.
    ///
    pub const fn assume_utc(self) -> DateTime<C> {
        let (secs, nsec) = self.as_timestamp();
        let moment = Moment::from_unix(secs, nsec);

        DateTime::from_moment(self.calendar(), moment)
    }

    /// Converts this [`PlainDateTime`] to a [`DateTime`] with the assumption that
    /// this date and time represents a moment at the given offset from UTC.
    ///
    pub const fn assume_offset(self, offset: Offset) -> DateTime<C> {
        let (mut secs, nsec) = self.as_timestamp();

        // adjust the timestamp, in reverse, by the offset
        // this is because we need to get to the UTC timestamp
        secs -= offset.as_total_seconds() as i64;

        let moment = Moment::from_unix(secs, nsec);

        DateTime::from_moment_at_offset(self.calendar(), moment, offset)
    }

    /// Returns a new date-time representing the same physical date, projected into `calendar`.
    #[must_use]
    pub const fn on<C2: Calendar>(self, calendar: C2) -> PlainDateTime<C2> {
        self.with_date(self.date.on(calendar))
    }

    /// Returns a new date-time with the date replaced.
    #[must_use]
    pub const fn with_date<C2: Calendar>(self, date: Date<C2>) -> PlainDateTime<C2> {
        PlainDateTime {
            date,
            time: self.time,
        }
    }

    /// Returns a new date-time with the time replaced.
    #[must_use]
    pub fn with_time<T: Into<PlainTime>>(self, time: T) -> Self {
        Self {
            date: self.date,
            time: time.into(),
        }
    }
}

// Conversion To
impl<C: Calendar> PlainDateTime<C> {
    /// Returns this date and time as a **local** timestamp in seconds and nanoseconds since
    /// the Unix epoch.
    pub(crate) const fn as_timestamp(self) -> (i64, u32) {
        let (mut secs, nsec) = self.time.as_timestamp();

        secs += self.date.as_days_since_unix_epoch() as i64 * 86_400;

        (secs, nsec)
    }
}

impl<C: Calendar> Debug for PlainDateTime<C> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.pad(&self.format_rfc3339())
    }
}

impl<C: Calendar> AsDate<C> for PlainDateTime<C> {
    fn as_date(&self) -> Date<C> {
        self.date
    }
}

impl<C: Calendar> AsTime for PlainDateTime<C> {
    fn as_time(&self) -> PlainTime {
        self.time
    }

    fn as_timestamp(&self) -> (i64, u32) {
        (*self).as_timestamp()
    }
}