mod date;
pub(crate) mod month;
#[macro_use]
mod macros;
#[cfg(feature = "serde")]
mod serde;
mod time;
mod utc;
pub mod zone;
use std::fmt;
use std::str::FromStr;
use crate::{
Point,
Span,
Frame,
real::Scale
};
use zone::Unsteady;
pub struct Utc;
pub struct Calendar<Z>(
pub Z
);
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct DateTime (pub Date, pub Time);
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Date {
pub y: i64,
pub m: u8,
pub d: u8
}
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Time {
pub h: u8,
pub m: u8,
pub s: u8
}
#[allow(missing_docs)]
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum Weekday {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
pub struct Frames<'c, Z> {
calendar: &'c Calendar<Z>,
scale: Scale,
previous: Point,
state: State
}
pub struct FramesRev<'c, Z>(pub Frames<'c, Z>);
enum State {
Date(Date),
Utc(Point),
Seconds
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseError {
Overflow,
Empty,
Format,
Invalid
}
impl fmt::Display for DateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self (date, time) = self;
write!(f, "{date} {time}")
}
}
impl fmt::Debug for DateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl DateTime {
pub const fn try_parse(from: &str) -> Result<Self, ParseError> {
let bytes = from.as_bytes();
let (date_bytes, time_len) = match bytes {
[d @ .., b'T' | b' ', _, _, b':', _, _, b':', _, _] => (d, 8),
[d @ .., b'T' | b' ', _, _, b':', _, _] => (d, 5),
[] => return Err(ParseError::Empty),
_ => return Err(ParseError::Format)
};
let time_bytes = bytes.split_at(bytes.len() - time_len).1;
let Ok(date_str) = std::str::from_utf8(date_bytes) else {
return Err(ParseError::Format);
};
let Ok(time_str) = std::str::from_utf8(time_bytes) else {
return Err(ParseError::Format);
};
let date_time = Self (
match Date::try_parse(date_str) {
Ok(date) => date,
Err(err) => return Err(err)
},
match Time::try_parse(time_str) {
Ok(time) => time,
Err(err) => return Err(err)
},
);
Ok(date_time)
}
#[must_use]
pub const fn parse(from: &str) -> Self {
match Self::try_parse(from) {
Ok(date_time) => date_time,
Err(err) => panic!("{}", err.as_str())
}
}
}
impl FromStr for DateTime {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_parse(s)
}
}
impl fmt::Display for Weekday {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let day = match self {
Weekday::Monday => "Monday",
Weekday::Tuesday => "Tuesday",
Weekday::Wednesday => "Wednesday",
Weekday::Thursday => "Thursday",
Weekday::Friday => "Friday",
Weekday::Saturday => "Saturday",
Weekday::Sunday => "Sunday",
};
f.write_str(day)
}
}
impl fmt::Debug for Weekday {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl State {
fn forward<Z: Unsteady>(
&mut self,
calendar: &Calendar<Z>,
scale: Scale,
previous: Point)
-> Point
{
match self {
Self::Date(ref mut date) if scale == Scale::Years => {
date.y += 1;
calendar.resolve_midnight_lossy(*date)
},
Self::Date(ref mut date) if scale == Scale::Months => {
match date.m == 12 {
true => {
date.y += 1;
date.m = 1;
},
false => date.m += 1
};
calendar.resolve_midnight_lossy(*date)
},
Self::Date(_) => unreachable!(),
Self::Utc(ref mut utc_point) => {
*utc_point = *utc_point + Span::from(scale);
calendar.0.revert_lossy(*utc_point)
},
Self::Seconds => previous + Span::SECOND
}
}
fn backward<Z: Unsteady>(
&mut self,
calendar: &Calendar<Z>,
scale: Scale,
previous: Point)
-> Point
{
match self {
Self::Date(ref mut date) if scale == Scale::Years => {
date.y -= 1;
calendar.resolve_midnight_lossy(*date)
},
Self::Date(ref mut date) if scale == Scale::Months => {
match date.m == 1 {
true => {
date.y -= 1;
date.m = 12;
},
false => date.m -= 1
};
calendar.resolve_midnight_lossy(*date)
},
Self::Date(_) => unreachable!(),
Self::Utc(ref mut utc_point) => {
*utc_point = *utc_point - Span::from(scale);
calendar.0.revert_lossy(*utc_point)
},
Self::Seconds => previous - Span::SECOND
}
}
}
impl<Z: Unsteady> Iterator for Frames<'_, Z> {
type Item = Frame;
fn next(&mut self) -> Option<Self::Item> {
loop {
let start = self.previous;
let stop = self.state.forward(
self.calendar,
self.scale,
self.previous
);
self.previous = stop;
if start != stop {
break Some(Frame {start, stop});
}
else {continue}
}
}
}
impl<'c, Z> Frames<'c, Z> {
pub fn rev(self) -> FramesRev<'c, Z> {FramesRev(self)}
}
impl<Z: Unsteady> Frames<'_, Z> {
pub fn prev(&mut self) -> Frame {
loop {
let stop = self.previous;
let start = self.state.backward(
self.calendar,
self.scale,
self.previous
);
self.previous = start;
if start != stop {
break Frame {start, stop};
}
else {continue}
}
}
}
impl<Z: Unsteady> Iterator for FramesRev<'_, Z> {
type Item = Frame;
fn next(&mut self) -> Option<Self::Item> {Some(self.0.prev())}
}
impl<Z: Unsteady> FramesRev<'_, Z> {
pub fn prev(&mut self) -> Frame {self.0.next().unwrap()}
}
impl ParseError {
const fn as_str(&self) -> &'static str {
match self {
Self::Overflow => "date/time overflowed integer",
Self::Empty => "invalid empty date/time",
Self::Format => "invalid date/time format",
Self::Invalid => "invalid date/time"
}
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_str().fmt(f)
}
}