greg 0.2.5

Simple Unobtrusive Date & Time library
Documentation
//! *Real Time* -- [`Point`], [`Span`], [`Frame`] & [`Scale`]

#![cfg_attr(
	feature = "rusqlite",
	doc = "\n",
	doc = "The `rusqlite` feature implements the [`rusqlite`] traits [`FromSql`](rusqlite::types::FromSql) and [`ToSql`](rusqlite::types::ToSql) for [`Point`] and [`Span`]."
)]

mod point;
mod span;
mod frame;

#[cfg(feature = "serde")]
mod serde;

#[cfg(feature = "rusqlite")]
mod sql;

pub use span::ParseSpanError;

/// *Point* in Time
///
/// This is simply a timestamp according to UNIX convention, that is, the number of seconds since 1970-01-01 00:00:00.
/// Negative values represent time before that epoch, obviously.
///
/// Because it is an [`i64`], the date range that can be represented is very large, roughly from the *year* `-292_277_022_657` to `+292_277_026_596`.
/// However, working with [`Point`]s close to end of this range is a bad idea, as date math operations might overflow.
///
/// Note that the [calendar time](crate::calendar) counterpart to this, [`DateTime`](crate::calendar::DateTime), can represent an even larger range, since it has an entire [`i64`] for just the year.
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Point {
	/// Count of seconds since 1970-01-01 00:00:00
	pub timestamp: i64
}

/// Timespan (Duration)
///
/// Simply an unsigned count of seconds.
/// [`Debug`], [`Display`](std::fmt::Display) and [`FromStr`](std::str::FromStr) implementations break this number, which is often incomprehensibly large, down into understandable units.
/// So instead of `Span { seconds: 108030 }` you get `1d6h30s`.
///
/// These are the units used:
///
/// | [`Scale`] | unit |    seconds |
/// |-----------|------|-----------:|
/// | Years     | `y`  | `31556952` |
/// | Months    | `mo` |  `2629746` |
/// | Weeks     | `w`  |   `604800` |
/// | Days      | `d`  |    `86400` |
/// | Hours     | `h`  |     `3600` |
/// | Minutes   | `m`  |       `60` |
/// | Seconds   | `s`  |        `1` |
///
/// As explained in the documentation for [`Scale`], the amount of seconds for `y` and `mo` is the average for each of these scales.
///
/// The Span can be parsed from the terse duration format using [`parse`](Self::parse) or [`try_parse`](Self::try_parse).
/// These methods are also `const`, so they can be used to define constants.
///
/// ```
/// use greg::Span;
///
/// const TIMEOUT: Span = Span::parse("30s");
///
/// assert_eq!(Span::parse("0s"), Span::from_seconds(0));
/// assert_eq!(Span::parse("1m30s"), Span::MINUTE + Span::SECOND * 30);
/// assert_eq!(Span::parse("1d10h"), Span::DAY + Span::HOUR * 10);
/// assert_eq!(Span::try_parse("48h"), Ok(Span::DAY * 2));
/// assert_eq!(
///     Span::parse("3w2h10m30s"),
///     Span::WEEK * 3 + Span::HOUR * 2 + Span::MINUTE * 10 + Span::SECOND * 30
/// );
/// ```
/// The alternate form for [`Display`](std::fmt::Display) (with `{:#}`) produces a string where each number/unit pair is separated by a space.
/// Additionally, if a precision is specified (with `{:.2}`), at most the requested number of number/unit pairs are printed, truncating smaller units.
/// ```
/// use greg::Span;
/// let span = Span::parse("1w2d8h33m12s");
///
/// assert_eq!(span.to_string(), "1w2d8h33m12s");
/// assert_eq!(format!("{span:#}"), "1w 2d 8h 33m 12s");
/// assert_eq!(format!("{span:#.2}"), "1w 2d");
/// ```
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Span {
	/// Duration in seconds
	pub seconds: u64
}

/// Timeframe: half-open range of [`Point`]s `start..stop` (`stop` is excluded)
///
/// This is essentially a [`Range`](std::ops::Range)`<`[`Point`]`>`, but the `end` field is called `stop`.
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Frame {
	/// The first included [`Point`]
	pub start: Point,
	/// The first [`Point`] no longer included
	pub stop: Point
}

/// Timescale
///
/// This enum represents various "scales" of time, and with the exception of [`Scale::Years`] and [`Scale::Months`] they are precisely defined as a number of seconds.
/// For [`Scale::Years`] and [`Scale::Months`] such a precise definition is obviously not available.
/// Nonetheless, it is often useful to treat them as a concrete number of seconds, such as when formatting a time [`Span`] not anchored to a particular [`Point`] in time.
/// For a human to comprehend a long length of time, it's better to express it in terms of years and months, instead of just weeks, which is the highest precisely defined [`Scale`], even if that is somewhat ill-defined.
/// To do this, the [`Scale`] must be expressed as a fixed number of seconds (a [`Span`]).  
/// There are a few different values that could reasonably be chosen for the length of [`Scale::Years`] and [`Scale::Months`] respectively.
/// This library simply copies [helper types](https://en.cppreference.com/w/cpp/chrono/duration#Helper_types) of [`std::chrono::duration`](https://en.cppreference.com/w/cpp/chrono/duration) provided in C++ since C++20.  
/// Accordingly, "years is equal to 365.2425 days (the average length of a Gregorian year)" and "months is equal to 30.436875 days (exactly 1/12 of years)".
/// Conveniently, both of these work out to an integral number of seconds.
///
/// | Scale   | unit |    seconds |
/// |---------|------|-----------:|
/// | Years   | `y`  | `31556952` |
/// | Months  | `mo` |  `2629746` |
/// | Weeks   | `w`  |   `604800` |
/// | Days    | `d`  |    `86400` |
/// | Hours   | `h`  |     `3600` |
/// | Minutes | `m`  |       `60` |
/// | Seconds | `s`  |        `1` |
///
/// All this is not to say that a [`Scale`] represents just a specific number of seconds.
/// For instance, the [`Calendar::date_floor`](crate::Calendar::date_floor) and [`Calendar::frames`](crate::Calendar::frames) functions use it with "calendar semantics".
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum Scale {
	Seconds,
	Minutes,
	Hours,
	Days,
	Weeks,
	Months,
	Years
}

impl Scale {
	/// The (average) amount of seconds, see [`Scale`] for details
	pub const fn as_seconds(self) -> u64 {
		match self {
			Self::Years =>   31556952,
			Self::Months =>   2629746,
			Self::Weeks =>     604800,
			Self::Days =>       86400,
			Self::Hours =>       3600,
			Self::Minutes =>       60,
			Self::Seconds =>        1
		}
	}
}