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, AsMoment, AsTime, Calendar, Date, Moment, Month, Offset, PlainTime, TimeZone, Year,
};

pub struct DateTime<C: Calendar = Iso> {
    calendar: C,
    moment: Moment,
    tz: TimeZone,
}

// Conversion From
impl<C: Calendar> DateTime<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 {
        Self {
            calendar,
            moment,
            tz: TimeZone::UTC,
        }
    }

    /// Returns the date and time of the given `moment`, projected on to the given `calendar`,
    /// at the given offset from UTC.
    pub const fn from_moment_at_offset(calendar: C, moment: Moment, offset: Offset) -> Self {
        Self {
            calendar,
            moment,
            tz: TimeZone::fixed(offset),
        }
    }
}

// Components
impl<C: Calendar> DateTime<C> {
    pub(crate) fn components(&self) -> (Date<C>, PlainTime, Offset) {
        let offset = self.tz.offset_at_moment(self.moment);
        let (mut secs, nsec) = self.moment.to_unix();

        secs += offset.as_total_seconds() as i64;

        let date = Date::from_unix_timestamp(self.calendar, secs);
        let time = PlainTime::from_unix_timestamp(secs, nsec);

        (date, time, offset)
    }

    /// Gets the calendar associated with this date and time.
    #[must_use]
    pub const fn calendar(&self) -> C {
        self.calendar
    }

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

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

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

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

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

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

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

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

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

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

// Conversion To
impl<C: Calendar> DateTime<C> {
    /// Returns a new date and time, representing the same moment, but at the new time zone.
    #[must_use]
    pub fn at(&self, tz: &TimeZone) -> Self {
        Self {
            tz: tz.clone(),
            moment: self.moment,
            calendar: self.calendar,
        }
    }
}

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

impl<C: Calendar> From<DateTime<C>> for Moment {
    fn from(dt: DateTime<C>) -> Self {
        dt.as_moment()
    }
}

impl<C: Calendar> AsMoment for DateTime<C> {
    fn as_moment(&self) -> Moment {
        self.moment
    }
}

impl<C: Calendar> AsDate<C> for DateTime<C> {
    fn as_date(&self) -> Date<C> {
        let (secs, _) = self.as_timestamp();

        Date::from_unix_timestamp(self.calendar, secs)
    }
}

impl<C: Calendar> AsTime for DateTime<C> {
    fn as_time(&self) -> PlainTime {
        let (secs, nsec) = self.as_timestamp();

        PlainTime::from_unix_timestamp(secs, nsec)
    }

    fn as_timestamp(&self) -> (i64, u32) {
        let offset = self.tz.offset_at_moment(self.moment);
        let (mut secs, nsec) = self.moment.to_unix();

        secs += offset.as_total_seconds() as i64;

        (secs, nsec)
    }
}