aboutsummaryrefslogtreecommitdiff
path: root/apps/koffice/libko/src/cass/time.rs
diff options
context:
space:
mode:
Diffstat (limited to 'apps/koffice/libko/src/cass/time.rs')
-rw-r--r--apps/koffice/libko/src/cass/time.rs141
1 files changed, 141 insertions, 0 deletions
diff --git a/apps/koffice/libko/src/cass/time.rs b/apps/koffice/libko/src/cass/time.rs
new file mode 100644
index 000000000000..48d4d5f9be2e
--- /dev/null
+++ b/apps/koffice/libko/src/cass/time.rs
@@ -0,0 +1,141 @@
+use crate::cass::Date;
+use chrono::{
+ DateTime, Duration, FixedOffset as Offset, Local, NaiveDateTime, NaiveTime, TimeZone, Timelike,
+ Utc,
+};
+use std::{cmp::Ordering, ops::Sub};
+
+/// A convenience wrapper around [DateTime][t] with fixed timezone
+///
+/// [t]: chrono::DateTime
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Time {
+ inner: DateTime<Offset>,
+}
+
+impl From<DateTime<Offset>> for Time {
+ fn from(inner: DateTime<Offset>) -> Self {
+ Self { inner }
+ }
+}
+
+impl<'t> Sub for &'t Time {
+ type Output = Duration;
+
+ fn sub(self, o: &'t Time) -> Self::Output {
+ self.inner - o.inner
+ }
+}
+
+impl ToString for Time {
+ fn to_string(&self) -> String {
+ format!("{}", self.inner.format("%Y-%m-%d %H:%M:%S%:z"))
+ }
+}
+
+impl Time {
+ /// Get the current local time and pin it to a fixed Tz offset
+ pub fn now() -> Self {
+ let now = Local::now();
+ Self {
+ inner: build_datetime(
+ now.time()
+ .with_second(0)
+ .and_then(|t| t.with_nanosecond(0))
+ .unwrap(),
+ ),
+ }
+ }
+
+ /// Get the time that might be rounded to the next 15 minutes
+ pub(crate) fn rounded(r: bool) -> Self {
+ if r {
+ Time::now().round()
+ } else {
+ Time::now()
+ }
+ }
+
+ pub(crate) fn date(&self) -> chrono::Date<Offset> {
+ self.inner.date()
+ }
+
+ /// Check if a time stamp happened _after_ a date
+ pub fn after(&self, date: &Date) -> bool {
+ &Date::from(self.date()) > date
+ }
+
+ #[cfg(test)]
+ pub(crate) fn fixed(hour: u32, min: u32, sec: u32) -> Self {
+ Self {
+ inner: build_datetime(NaiveTime::from_hms(hour, min, sec)),
+ }
+ }
+
+ /// Return a new instance that is rounded to nearest 15 minutes
+ ///
+ /// It uses the internally provided offset to do rounding, meaning
+ /// that the timezone information will not change, even when
+ /// rounding values that were created in a different timezone.
+ pub fn round(&self) -> Self {
+ let naive = self.inner.time();
+ let (new_min, incr_hour) = match naive.minute() {
+ // 0-7 => (0, false)
+ m if m < 7 => (0, false),
+ // 7-22 => (15, false)
+ m if m >= 7 && m < 22 => (15, false),
+ // 22-37 => (30, false)
+ m if m >= 22 && m < 37 => (30, false),
+ // 37-52 => (45, false)
+ m if m >= 37 && m < 52 => (45, false),
+ // 52-59 => (0, true)
+ m if m >= 52 && m <= 59 => (0, true),
+ _ => unreachable!(),
+ };
+
+ let hour = naive.hour();
+ let new = NaiveTime::from_hms(if incr_hour { hour + 1 } else { hour }, new_min, 0);
+ let offset = self.inner.offset();
+ let date = self.inner.date();
+
+ Self {
+ inner: DateTime::from_utc(NaiveDateTime::new(date.naive_local(), new), *offset),
+ }
+ }
+
+ pub fn hour(&self) -> u32 {
+ self.inner.hour()
+ }
+
+ pub fn minute(&self) -> u32 {
+ self.inner.minute()
+ }
+
+ pub fn second(&self) -> u32 {
+ self.inner.second()
+ }
+}
+
+/// Build a DateTime with the current local fixed offset
+fn build_datetime(nt: NaiveTime) -> DateTime<Offset> {
+ let date = Utc::now().date().naive_local();
+ let offset = Local.offset_from_utc_date(&date);
+
+ DateTime::from_utc(NaiveDateTime::new(date, nt), offset)
+}
+
+#[test]
+fn simple() {
+ let t = Time::fixed(10, 44, 0);
+ let round = t.round();
+ assert_eq!(round.minute(), 45);
+
+ let t = Time::fixed(6, 8, 0);
+ let round = t.round();
+ assert_eq!(round.minute(), 15);
+
+ let t = Time::fixed(6, 55, 0);
+ let round = t.round();
+ assert_eq!(round.minute(), 0);
+ assert_eq!(round.hour(), 7);
+}