From 1c4e103904555e703e4cc3ddb4fbafc89b7d9f04 Mon Sep 17 00:00:00 2001 From: ryneeverett Date: Sun, 5 Feb 2023 14:54:56 -0500 Subject: [PATCH] taskchampion: Add time.utc_timestamp function. Add a function that returns a Timestamp from an i64. One advantage is improved readability since this function is guaranteed to return a `LocalResult::Single`. Anther advantage is that it will panic if something other than a LocalResult::Single is returned by chrono, which shouldn't be possible for UTC timestamps which can't have DST ambiguity. --- taskchampion/lib/src/atomic.rs | 7 ++-- taskchampion/lib/src/task.rs | 7 ++-- taskchampion/taskchampion/src/lib.rs | 2 +- taskchampion/taskchampion/src/task/mod.rs | 6 ++-- taskchampion/taskchampion/src/task/task.rs | 37 +++++++++------------- taskchampion/taskchampion/src/task/time.rs | 11 +++++++ 6 files changed, 34 insertions(+), 36 deletions(-) create mode 100644 taskchampion/taskchampion/src/task/time.rs diff --git a/taskchampion/lib/src/atomic.rs b/taskchampion/lib/src/atomic.rs index 048fc4dd6..01c72059e 100644 --- a/taskchampion/lib/src/atomic.rs +++ b/taskchampion/lib/src/atomic.rs @@ -1,7 +1,8 @@ //! Trait implementations for a few atomic types use crate::traits::*; -use taskchampion::chrono::{offset::LocalResult, prelude::*}; +use taskchampion::chrono::{DateTime, Utc}; +use taskchampion::utc_timestamp; impl PassByValue for usize { type RustType = usize; @@ -21,9 +22,7 @@ impl PassByValue for libc::time_t { unsafe fn from_ctype(self) -> Option> { if self != 0 { - if let LocalResult::Single(ts) = Utc.timestamp_opt(self, 0) { - return Some(ts); - } + return Some(utc_timestamp(self)); } None } diff --git a/taskchampion/lib/src/task.rs b/taskchampion/lib/src/task.rs index 495ca909f..f734c05ac 100644 --- a/taskchampion/lib/src/task.rs +++ b/taskchampion/lib/src/task.rs @@ -5,8 +5,7 @@ use std::convert::TryFrom; use std::ops::Deref; use std::ptr::NonNull; use std::str::FromStr; -use taskchampion::chrono::{offset::LocalResult, TimeZone, Utc}; -use taskchampion::{Annotation, Tag, Task, TaskMut, Uuid}; +use taskchampion::{utc_timestamp, Annotation, Tag, Task, TaskMut, Uuid}; /// A task, as publicly exposed by this library. /// @@ -760,9 +759,7 @@ pub unsafe extern "C" fn tc_task_remove_annotation(task: *mut TCTask, entry: i64 wrap_mut( task, |task| { - if let LocalResult::Single(ts) = Utc.timestamp_opt(entry, 0) { - task.remove_annotation(ts)?; - } + task.remove_annotation(utc_timestamp(entry))?; Ok(TCResult::Ok) }, TCResult::Error, diff --git a/taskchampion/taskchampion/src/lib.rs b/taskchampion/taskchampion/src/lib.rs index 4b29126b9..b9d793646 100644 --- a/taskchampion/taskchampion/src/lib.rs +++ b/taskchampion/taskchampion/src/lib.rs @@ -63,7 +63,7 @@ pub use errors::Error; pub use replica::Replica; pub use server::{Server, ServerConfig}; pub use storage::StorageConfig; -pub use task::{Annotation, Status, Tag, Task, TaskMut}; +pub use task::{utc_timestamp, Annotation, Status, Tag, Task, TaskMut}; pub use workingset::WorkingSet; /// Re-exported type from the `uuid` crate, for ease of compatibility for consumers of this crate. diff --git a/taskchampion/taskchampion/src/task/mod.rs b/taskchampion/taskchampion/src/task/mod.rs index 2e95b2a09..259ed6ab0 100644 --- a/taskchampion/taskchampion/src/task/mod.rs +++ b/taskchampion/taskchampion/src/task/mod.rs @@ -1,14 +1,12 @@ #![allow(clippy::module_inception)] -use chrono::prelude::*; - mod annotation; mod status; mod tag; mod task; +mod time; pub use annotation::Annotation; pub use status::Status; pub use tag::Tag; pub use task::{Task, TaskMut}; - -pub type Timestamp = DateTime; +pub use time::{utc_timestamp, Timestamp}; diff --git a/taskchampion/taskchampion/src/task/task.rs b/taskchampion/taskchampion/src/task/task.rs index 47d3d4889..b0927615e 100644 --- a/taskchampion/taskchampion/src/task/task.rs +++ b/taskchampion/taskchampion/src/task/task.rs @@ -1,10 +1,10 @@ use super::tag::{SyntheticTag, TagInner}; -use super::{Annotation, Status, Tag, Timestamp}; +use super::{utc_timestamp, Annotation, Status, Tag, Timestamp}; use crate::depmap::DependencyMap; use crate::errors::{Error, Result}; use crate::replica::Replica; use crate::storage::TaskMap; -use chrono::{offset::LocalResult, prelude::*}; +use chrono::prelude::*; use log::trace; use std::convert::AsRef; use std::convert::TryInto; @@ -136,7 +136,7 @@ impl Task { .unwrap_or("") } - pub fn get_entry(&self) -> Option> { + pub fn get_entry(&self) -> Option { self.get_timestamp(Prop::Entry.as_ref()) } @@ -149,7 +149,7 @@ impl Task { /// Get the wait time. If this value is set, it will be returned, even /// if it is in the past. - pub fn get_wait(&self) -> Option> { + pub fn get_wait(&self) -> Option { self.get_timestamp(Prop::Wait.as_ref()) } @@ -227,15 +227,10 @@ impl Task { self.taskmap.iter().filter_map(|(k, v)| { if let Some(ts) = k.strip_prefix("annotation_") { if let Ok(ts) = ts.parse::() { - if let LocalResult::Single(entry) = Utc.timestamp_opt(ts, 0) { - return Some(Annotation { - entry, - description: v.to_owned(), - }); - } else { - // ignore an invalid timestamp - return None; - } + return Some(Annotation { + entry: utc_timestamp(ts), + description: v.to_owned(), + }); } // note that invalid "annotation_*" are ignored } @@ -278,7 +273,7 @@ impl Task { } /// Get the modification time for this task. - pub fn get_modified(&self) -> Option> { + pub fn get_modified(&self) -> Option { self.get_timestamp(Prop::Modified.as_ref()) } @@ -313,12 +308,10 @@ impl Task { || key.starts_with("dep_") } - fn get_timestamp(&self, property: &str) -> Option> { + fn get_timestamp(&self, property: &str) -> Option { if let Some(ts) = self.taskmap.get(property) { if let Ok(ts) = ts.parse() { - if let LocalResult::Single(entry) = Utc.timestamp_opt(ts, 0) { - return Some(entry); - } + return Some(utc_timestamp(ts)); } // if the value does not parse as an integer, default to None } @@ -366,15 +359,15 @@ impl<'r> TaskMut<'r> { self.set_string(Prop::Priority.as_ref(), Some(priority)) } - pub fn set_entry(&mut self, entry: Option>) -> Result<()> { + pub fn set_entry(&mut self, entry: Option) -> Result<()> { self.set_timestamp(Prop::Entry.as_ref(), entry) } - pub fn set_wait(&mut self, wait: Option>) -> Result<()> { + pub fn set_wait(&mut self, wait: Option) -> Result<()> { self.set_timestamp(Prop::Wait.as_ref(), wait) } - pub fn set_modified(&mut self, modified: DateTime) -> Result<()> { + pub fn set_modified(&mut self, modified: Timestamp) -> Result<()> { self.set_timestamp(Prop::Modified.as_ref(), Some(modified)) } @@ -548,7 +541,7 @@ impl<'r> TaskMut<'r> { self.set_value(property, value) } - fn set_timestamp(&mut self, property: &str, value: Option>) -> Result<()> { + fn set_timestamp(&mut self, property: &str, value: Option) -> Result<()> { self.set_string(property, value.map(|v| v.timestamp().to_string())) } diff --git a/taskchampion/taskchampion/src/task/time.rs b/taskchampion/taskchampion/src/task/time.rs new file mode 100644 index 000000000..928da2a6a --- /dev/null +++ b/taskchampion/taskchampion/src/task/time.rs @@ -0,0 +1,11 @@ +use chrono::{offset::LocalResult, DateTime, TimeZone, Utc}; + +pub type Timestamp = DateTime; + +pub fn utc_timestamp(secs: i64) -> Timestamp { + match Utc.timestamp_opt(secs, 0) { + LocalResult::Single(tz) => tz, + // The other two variants are None and Ambiguous, which both are caused by DST. + _ => unreachable!("We're requesting UTC so daylight saving time isn't a factor."), + } +}