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.
This commit is contained in:
ryneeverett 2023-02-05 14:54:56 -05:00 committed by Dustin J. Mitchell
parent 5a4b981b6c
commit 1c4e103904
6 changed files with 34 additions and 36 deletions

View file

@ -1,7 +1,8 @@
//! Trait implementations for a few atomic types //! Trait implementations for a few atomic types
use crate::traits::*; use crate::traits::*;
use taskchampion::chrono::{offset::LocalResult, prelude::*}; use taskchampion::chrono::{DateTime, Utc};
use taskchampion::utc_timestamp;
impl PassByValue for usize { impl PassByValue for usize {
type RustType = usize; type RustType = usize;
@ -21,9 +22,7 @@ impl PassByValue for libc::time_t {
unsafe fn from_ctype(self) -> Option<DateTime<Utc>> { unsafe fn from_ctype(self) -> Option<DateTime<Utc>> {
if self != 0 { if self != 0 {
if let LocalResult::Single(ts) = Utc.timestamp_opt(self, 0) { return Some(utc_timestamp(self));
return Some(ts);
}
} }
None None
} }

View file

@ -5,8 +5,7 @@ use std::convert::TryFrom;
use std::ops::Deref; use std::ops::Deref;
use std::ptr::NonNull; use std::ptr::NonNull;
use std::str::FromStr; use std::str::FromStr;
use taskchampion::chrono::{offset::LocalResult, TimeZone, Utc}; use taskchampion::{utc_timestamp, Annotation, Tag, Task, TaskMut, Uuid};
use taskchampion::{Annotation, Tag, Task, TaskMut, Uuid};
/// A task, as publicly exposed by this library. /// 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( wrap_mut(
task, task,
|task| { |task| {
if let LocalResult::Single(ts) = Utc.timestamp_opt(entry, 0) { task.remove_annotation(utc_timestamp(entry))?;
task.remove_annotation(ts)?;
}
Ok(TCResult::Ok) Ok(TCResult::Ok)
}, },
TCResult::Error, TCResult::Error,

View file

@ -63,7 +63,7 @@ pub use errors::Error;
pub use replica::Replica; pub use replica::Replica;
pub use server::{Server, ServerConfig}; pub use server::{Server, ServerConfig};
pub use storage::StorageConfig; 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; pub use workingset::WorkingSet;
/// Re-exported type from the `uuid` crate, for ease of compatibility for consumers of this crate. /// Re-exported type from the `uuid` crate, for ease of compatibility for consumers of this crate.

View file

@ -1,14 +1,12 @@
#![allow(clippy::module_inception)] #![allow(clippy::module_inception)]
use chrono::prelude::*;
mod annotation; mod annotation;
mod status; mod status;
mod tag; mod tag;
mod task; mod task;
mod time;
pub use annotation::Annotation; pub use annotation::Annotation;
pub use status::Status; pub use status::Status;
pub use tag::Tag; pub use tag::Tag;
pub use task::{Task, TaskMut}; pub use task::{Task, TaskMut};
pub use time::{utc_timestamp, Timestamp};
pub type Timestamp = DateTime<Utc>;

View file

@ -1,10 +1,10 @@
use super::tag::{SyntheticTag, TagInner}; 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::depmap::DependencyMap;
use crate::errors::{Error, Result}; use crate::errors::{Error, Result};
use crate::replica::Replica; use crate::replica::Replica;
use crate::storage::TaskMap; use crate::storage::TaskMap;
use chrono::{offset::LocalResult, prelude::*}; use chrono::prelude::*;
use log::trace; use log::trace;
use std::convert::AsRef; use std::convert::AsRef;
use std::convert::TryInto; use std::convert::TryInto;
@ -136,7 +136,7 @@ impl Task {
.unwrap_or("") .unwrap_or("")
} }
pub fn get_entry(&self) -> Option<DateTime<Utc>> { pub fn get_entry(&self) -> Option<Timestamp> {
self.get_timestamp(Prop::Entry.as_ref()) 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 /// Get the wait time. If this value is set, it will be returned, even
/// if it is in the past. /// if it is in the past.
pub fn get_wait(&self) -> Option<DateTime<Utc>> { pub fn get_wait(&self) -> Option<Timestamp> {
self.get_timestamp(Prop::Wait.as_ref()) self.get_timestamp(Prop::Wait.as_ref())
} }
@ -227,15 +227,10 @@ impl Task {
self.taskmap.iter().filter_map(|(k, v)| { self.taskmap.iter().filter_map(|(k, v)| {
if let Some(ts) = k.strip_prefix("annotation_") { if let Some(ts) = k.strip_prefix("annotation_") {
if let Ok(ts) = ts.parse::<i64>() { if let Ok(ts) = ts.parse::<i64>() {
if let LocalResult::Single(entry) = Utc.timestamp_opt(ts, 0) { return Some(Annotation {
return Some(Annotation { entry: utc_timestamp(ts),
entry, description: v.to_owned(),
description: v.to_owned(), });
});
} else {
// ignore an invalid timestamp
return None;
}
} }
// note that invalid "annotation_*" are ignored // note that invalid "annotation_*" are ignored
} }
@ -278,7 +273,7 @@ impl Task {
} }
/// Get the modification time for this task. /// Get the modification time for this task.
pub fn get_modified(&self) -> Option<DateTime<Utc>> { pub fn get_modified(&self) -> Option<Timestamp> {
self.get_timestamp(Prop::Modified.as_ref()) self.get_timestamp(Prop::Modified.as_ref())
} }
@ -313,12 +308,10 @@ impl Task {
|| key.starts_with("dep_") || key.starts_with("dep_")
} }
fn get_timestamp(&self, property: &str) -> Option<DateTime<Utc>> { fn get_timestamp(&self, property: &str) -> Option<Timestamp> {
if let Some(ts) = self.taskmap.get(property) { if let Some(ts) = self.taskmap.get(property) {
if let Ok(ts) = ts.parse() { if let Ok(ts) = ts.parse() {
if let LocalResult::Single(entry) = Utc.timestamp_opt(ts, 0) { return Some(utc_timestamp(ts));
return Some(entry);
}
} }
// if the value does not parse as an integer, default to None // 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)) self.set_string(Prop::Priority.as_ref(), Some(priority))
} }
pub fn set_entry(&mut self, entry: Option<DateTime<Utc>>) -> Result<()> { pub fn set_entry(&mut self, entry: Option<Timestamp>) -> Result<()> {
self.set_timestamp(Prop::Entry.as_ref(), entry) self.set_timestamp(Prop::Entry.as_ref(), entry)
} }
pub fn set_wait(&mut self, wait: Option<DateTime<Utc>>) -> Result<()> { pub fn set_wait(&mut self, wait: Option<Timestamp>) -> Result<()> {
self.set_timestamp(Prop::Wait.as_ref(), wait) self.set_timestamp(Prop::Wait.as_ref(), wait)
} }
pub fn set_modified(&mut self, modified: DateTime<Utc>) -> Result<()> { pub fn set_modified(&mut self, modified: Timestamp) -> Result<()> {
self.set_timestamp(Prop::Modified.as_ref(), Some(modified)) self.set_timestamp(Prop::Modified.as_ref(), Some(modified))
} }
@ -548,7 +541,7 @@ impl<'r> TaskMut<'r> {
self.set_value(property, value) self.set_value(property, value)
} }
fn set_timestamp(&mut self, property: &str, value: Option<DateTime<Utc>>) -> Result<()> { fn set_timestamp(&mut self, property: &str, value: Option<Timestamp>) -> Result<()> {
self.set_string(property, value.map(|v| v.timestamp().to_string())) self.set_string(property, value.map(|v| v.timestamp().to_string()))
} }

View file

@ -0,0 +1,11 @@
use chrono::{offset::LocalResult, DateTime, TimeZone, Utc};
pub type Timestamp = DateTime<Utc>;
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."),
}
}