1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
use std::time;
use std::ops::{
Add,
Sub
};
use super::{
Point,
Span
};
// Constructors
impl Point {
/// The earliest [`Point`] that can be represented by the timestamp
///
/// `-292277022657-01-27 08:29:52` (a Monday)
pub const MIN: Self = Self::from_epoch(i64::MIN);
/// The last [`Point`] that can be represented by the timestamp
///
/// `292277026596-12-04 15:30:07` (a Sunday)
pub const MAX: Self = Self::from_epoch(i64::MAX);
/// Same as constructing `Point {timestamp}` directly
pub const fn from_epoch(timestamp: i64) -> Self {
Self {timestamp}
}
/// Get the current [`Point`] in time from the system clock
pub fn now() -> Self {
let sys = time::SystemTime::now();
let timestamp = match sys.duration_since(time::UNIX_EPOCH) {
Ok(duration) => duration.as_secs() as i64,
Err(err) => -(err.duration().as_secs() as i64)
};
Self {timestamp}
}
/// Checked [`Span`] addition
///
/// Computes `self + span`, returning `None` if overflow occurred.
///
/// ```
/// use greg::{Point, Span};
///
/// assert_eq!(
/// Point::now().checked_add(Span::parse("300000000000y")),
/// None
/// );
/// assert_eq!(
/// Point::MIN.checked_add(Span::MAX),
/// Some(Point::MAX)
/// );
/// ```
pub const fn checked_add(self, span: Span) -> Option<Self> {
match self.shift_to_u64().checked_add(span.seconds) {
Some(unsigned) => Some(Self::shift_from_u64(unsigned)),
None => None
}
}
/// Checked [`Span`] subtraction
///
/// Computes `self - span`, returning `None` if overflow occurred.
///
/// ```
/// use greg::{Point, Span};
///
/// assert_eq!(
/// Point::now().checked_sub(Span::parse("300000000000y")),
/// None
/// );
///
/// assert_eq!(
/// Point::MAX.checked_sub(Span::MAX),
/// Some(Point::MIN)
/// );
/// ```
pub const fn checked_sub(self, span: Span) -> Option<Self> {
match self.shift_to_u64().checked_sub(span.seconds) {
Some(unsigned) => Some(Self::shift_from_u64(unsigned)),
None => None
}
}
/// Saturating [`Span`] addition
///
/// Computes `self + span`, saturating at the numeric bounds instead of overflowing.
///
/// ```
/// use greg::{Point, Span};
///
/// assert_eq!(
/// Point::now().saturating_add(Span::parse("300000000000y")),
/// Point::MAX
/// );
/// assert_eq!(
/// Point::MIN.saturating_add(Span::MAX - Span::SECOND),
/// Point::MAX - Span::SECOND
/// );
/// ```
pub const fn saturating_add(self, span: Span) -> Self {
Self::shift_from_u64(
self.shift_to_u64().saturating_add(span.seconds)
)
}
/// Saturating [`Span`] subtraction
///
/// Computes `self - span`, saturating at the numeric bounds instead of overflowing.
///
/// ```
/// use greg::{Point, Span};
///
/// assert_eq!(
/// Point::now().saturating_sub(Span::parse("300000000000y")),
/// Point::MIN
/// );
///
/// assert_eq!(
/// Point::MAX.saturating_sub(Span::MAX - Span::SECOND),
/// Point::MIN + Span::SECOND
/// );
/// ```
pub const fn saturating_sub(self, span: Span) -> Self {
Self::shift_from_u64(
self.shift_to_u64().saturating_sub(span.seconds)
)
}
/// Shift the timestamp into a `u64`
///
/// We need to shift back & forth because the [`Span`] is a `u64`.
/// If we did the addition/subtraction in `i64`, we couldn't do `Point(i64::MAX) - Span(u64::MAX) = Point(i64::MIN)` in one go, for instance.
/// By doing it this way, we can add/subtract more than `i64::MAX` and only check for overflows at the end.
///
/// Credit: https://stackoverflow.com/a/74491572
const fn shift_to_u64(self) -> u64 {
(self.timestamp as u64).wrapping_add(u64::MAX / 2 + 1)
}
/// Shift the `u64` back into a timestamp
///
/// Credit: https://stackoverflow.com/a/74491572
const fn shift_from_u64(u: u64) -> Self {
Self { timestamp: u.wrapping_sub(u64::MAX / 2 + 1) as i64 }
}
}
// Math
impl Add<Span> for Point {
type Output = Self;
fn add(self, rhs: Span) -> Self::Output {
let timestamp = self.timestamp + rhs.seconds as i64;
Self {timestamp}
}
}
impl Sub<Span> for Point {
type Output = Self;
fn sub(self, rhs: Span) -> Self::Output {
let timestamp = self.timestamp - rhs.seconds as i64;
Self {timestamp}
}
}
impl Sub<Point> for Point {
type Output = Span;
/// Note that [`Span`]s cannot be negative, so this is the **absolute** difference between the [`Point`]s
fn sub(self, rhs: Point) -> Self::Output {
let seconds = self.timestamp.abs_diff(rhs.timestamp);
Span {seconds}
}
}
impl From<time::SystemTime> for Point {
fn from(sys: time::SystemTime) -> Self {
let timestamp = match sys.duration_since(time::UNIX_EPOCH) {
Ok(duration) => duration.as_secs() as i64,
Err(err) => -(err.duration().as_secs() as i64)
};
Self {timestamp}
}
}