From 4769c1233617af145dc7985852814c0a48113240 Mon Sep 17 00:00:00 2001 From: xeruf <27jf@pm.me> Date: Mon, 23 Dec 2024 02:48:31 +0100 Subject: [PATCH] feat: relative hourstamp parsing --- src/helpers.rs | 22 +++++++++++++++++----- src/main.rs | 5 +++-- src/tasks.rs | 25 ++++++++++++++----------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index a35b367..712a837 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -58,11 +58,15 @@ pub fn parse_hour_after(str: &str, after: DateTime) -> Option Option> { + parse_date_with_ref(str, Local::now()) +} + +pub fn parse_date_with_ref(str: &str, reference: DateTime) -> Option> { // Using two libraries for better exhaustiveness, see https://github.com/uutils/parse_datetime/issues/84 - match interim::parse_date_string(str, Local::now(), interim::Dialect::Us) { + match interim::parse_date_string(str, reference, interim::Dialect::Us) { Ok(date) => Some(date.to_utc()), Err(e) => { - match parse_datetime::parse_datetime_at_date(Local::now(), str) { + match parse_datetime::parse_datetime_at_date(reference, str) { Ok(date) => Some(date.to_utc()), Err(_) => { warn!("Could not parse date from \"{str}\": {e}"); @@ -85,8 +89,8 @@ pub fn parse_date(str: &str) -> Option> { /// - Plain number as hour, 18 hours back or 6 hours forward /// - Number with prefix as minute offset /// - Otherwise try to parse a relative date -pub fn parse_tracking_stamp(str: &str) -> Option { - if let Some(num) = parse_hour(str, 6) { +pub fn parse_tracking_stamp(str: &str, after: Option>) -> Option { + if let Some(num) = parse_hour_after(str, after.unwrap_or(Local::now() - TimeDelta::hours(18))) { return Some(num.to_timestamp()); } let stripped = str.trim().trim_start_matches('+').trim_start_matches("in "); @@ -169,7 +173,7 @@ pub fn format_timestamp_relative_to(stamp: &Timestamp, reference: &Timestamp) -> mod test { use super::*; - use chrono::{NaiveDate, NaiveDateTime, Timelike}; + use chrono::{FixedOffset, NaiveDate, Timelike}; use interim::datetime::DateTime; #[test] @@ -203,4 +207,12 @@ mod test { // TODO test timezone offset issues } + + #[test] + fn test_timezone() { + assert_eq!( + FixedOffset::east_opt(7200).unwrap().timestamp_millis_opt(1000).unwrap().time(), + NaiveTime::from_hms_opt(2, 0, 1).unwrap() + ); + } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 15ade0f..5953294 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ use crate::helpers::*; use crate::kinds::{format_tag_basic, match_event_tag, Prio, BASIC_KINDS, PROPERTY_COLUMNS, PROP_KINDS}; use crate::task::{State, StateChange, Task, MARKER_PROPERTY}; use crate::tasks::{referenced_event, PropertyCollection, StateFilter, TasksRelay}; -use chrono::Local; +use chrono::{DateTime, Local, TimeZone, Utc}; use colored::Colorize; use directories::ProjectDirs; use env_logger::{Builder, Target, WriteStyle}; @@ -650,7 +650,8 @@ async fn main() -> Result<()> { match arg { None => tasks.move_to(None), Some(arg) => { - if parse_tracking_stamp(arg).and_then(|stamp| tasks.track_at(stamp, None)).is_some() { + if parse_tracking_stamp(arg, Local.timestamp_millis_opt(tasks.get_position_timestamped().0.as_u64() as i64 * 1000).earliest()) + .and_then(|stamp| tasks.track_at(stamp, None)).is_some() { println!("{}", tasks.times_tracked(15)); } // So the error message is not covered up diff --git a/src/tasks.rs b/src/tasks.rs index 6c8c893..eafae69 100644 --- a/src/tasks.rs +++ b/src/tasks.rs @@ -4,13 +4,6 @@ mod tests; mod children_traversal; mod durations; -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque}; -use std::fmt::{Display, Formatter}; -use std::iter::{empty, once, FusedIterator}; -use std::ops::{Deref, Div, Rem}; -use std::str::FromStr; -use std::time::Duration; - use crate::event_sender::{EventSender, MostrMessage}; use crate::hashtag::Hashtag; use crate::helpers::{ @@ -22,6 +15,8 @@ use crate::task::{State, StateChange, Task, MARKER_DEPENDS, MARKER_PARENT, MARKE use crate::tasks::children_traversal::ChildrenTraversal; use crate::tasks::durations::{referenced_events, timestamps, Durations}; pub use crate::tasks::nostr_users::NostrUsers; + +use chrono::{Local, TimeDelta}; use colored::Colorize; use itertools::Itertools; use log::{debug, error, info, trace, warn}; @@ -30,6 +25,12 @@ use nostr_sdk::{ SingleLetterTag, Tag, TagKind, Timestamp, Url, }; use regex::bytes::Regex; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque}; +use std::fmt::{Display, Formatter}; +use std::iter::{empty, once, FusedIterator}; +use std::ops::{Deref, Div, Rem}; +use std::str::FromStr; +use std::time::Duration; use tokio::sync::mpsc::Sender; const DEFAULT_PRIO: Prio = 25; @@ -240,6 +241,10 @@ impl TasksRelay { self.get_position_at(now()).1 } + pub(crate) fn get_position_timestamped(&self) -> (Timestamp, Option) { + self.get_position_at(now()) + } + fn sorting_key(&self, task: &Task) -> impl Ord { self.sorting .iter() @@ -277,9 +282,7 @@ impl TasksRelay { label } else { format!("{}{}", - if limit > times.len() || limit == usize::MAX { "All ".to_string() } - else if limit < 20 { "Recent ".to_string() } - else { format!("Latest {limit} Entries of ") }, + if limit > times.len() || limit == usize::MAX { "All ".to_string() } else if limit < 20 { "Recent ".to_string() } else { format!("Latest {limit} Entries of ") }, label) }.italic(), ×[times.len().saturating_sub(limit)..].join("\n")) @@ -1160,7 +1163,7 @@ impl TasksRelay { /// /// Returns false and prints a message if parsing failed pub(crate) fn track_from(&mut self, str: &str) -> bool { - parse_tracking_stamp(str) + parse_tracking_stamp(str, None) .and_then(|stamp| self.track_at(stamp, self.get_position())) .is_some() }