use crate::{
Point,
real::Scale
};
use super::{
Utc,
Calendar,
DateTime,
Date,
Time,
Weekday,
Frames,
State
};
pub trait Shift {
fn apply(&self, point: Point) -> Point;
}
pub trait Steady: Shift {
fn revert(&self, point: Point) -> Point;
}
pub trait Unsteady: Shift {
type Ambiguity: std::fmt::Debug;
fn revert_lossy(&self, point: Point) -> Point;
fn try_revert(&self, point: Point) -> Result<Point, Self::Ambiguity>;
}
impl Shift for Utc {
fn apply(&self, point: Point) -> Point {point}
}
impl Steady for Utc {
fn revert(&self, point: Point) -> Point {point}
}
impl<Z: Shift> Calendar<Z> {
pub fn lookup(&self, point: Point) -> DateTime {
let shifted = self.0.apply(point);
Utc::lookup(shifted)
}
pub fn weekday(&self, point: Point) -> Weekday {
let in_utc = self.0.apply(point);
match (in_utc.timestamp / 86_400 - 4).rem_euclid(7) {
0 => Weekday::Monday,
1 => Weekday::Tuesday,
2 => Weekday::Wednesday,
3 => Weekday::Thursday,
4 => Weekday::Friday,
5 => Weekday::Saturday,
6 => Weekday::Sunday,
_ => unreachable!()
}
}
pub fn format(&self, point: Point) -> String {
self.lookup(point).to_string()
}
}
impl<Z: Steady> Calendar<Z> {
pub fn resolve(&self, datetime: DateTime) -> Point {
let utc_point = Utc::resolve(datetime);
self.0.revert(utc_point)
}
pub fn resolve_midnight(&self, date: Date) -> Point {
self.resolve(DateTime(date, Time::MIDNIGHT))
}
pub fn date_floor(&self, scale: Scale, point: Point) -> Point {
let point_utc = self.0.apply(point);
let utc_floor = match scale {
Scale::Years => Utc::start_of_year(point_utc),
Scale::Months => Utc::start_of_month(point_utc),
Scale::Weeks => {
let rest = (point_utc.timestamp - 4 * 86_400) % (7 * 86_400);
let timestamp = point_utc.timestamp - rest;
Point {timestamp}
},
Scale::Days |
Scale::Hours |
Scale::Minutes |
Scale::Seconds => {
let rest = point_utc.timestamp % scale.as_seconds() as i64;
let timestamp = point_utc.timestamp - rest;
Point {timestamp}
}
};
self.0.revert(utc_floor)
}
}
impl<Z: Unsteady> Calendar<Z> {
pub fn resolve_lossy(&self, datetime: DateTime) -> Point {
let utc_point = Utc::resolve(datetime);
self.0.revert_lossy(utc_point)
}
pub fn resolve_midnight_lossy(&self, date: Date) -> Point {
self.resolve_lossy(DateTime(date, Time::MIDNIGHT))
}
pub fn try_resolve(&self, dt: DateTime) -> Result<Point, Z::Ambiguity> {
let utc_point = Utc::resolve(dt);
self.0.try_revert(utc_point)
}
pub fn date_floor_lossy(&self, scale: Scale, point: Point) -> Point {
let point_utc = self.0.apply(point);
let utc_floor = match scale {
Scale::Years => Utc::start_of_year(point_utc),
Scale::Months => Utc::start_of_month(point_utc),
Scale::Weeks => {
let rest = (point_utc.timestamp - 4 * 86_400) % (7 * 86_400);
let timestamp = point_utc.timestamp - rest;
Point {timestamp}
},
Scale::Days |
Scale::Hours |
Scale::Minutes => {
let rest = point_utc.timestamp % scale.as_seconds() as i64;
let timestamp = point_utc.timestamp - rest;
Point {timestamp}
},
Scale::Seconds => return point
};
self.0.revert_lossy(utc_floor)
}
pub fn frames(&self, scale: Scale, start: Point) -> Frames<'_, Z> {
let first_start = self.date_floor_lossy(scale, start);
let state = match scale {
Scale::Years |
Scale::Months => State::Date (self.lookup(first_start).0),
Scale::Seconds => State::Seconds,
_ => State::Utc(self.0.apply(first_start))
};
Frames {
calendar: self,
scale,
previous: first_start,
state
}
}
}