forked from janek/mostr
feat: localize nostr Timestamps consistently
This commit is contained in:
parent
3eefbad6d5
commit
957422f767
|
@ -1,7 +1,10 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::io::{stdin, stdout, Write};
|
use std::io::{stdin, stdout, Write};
|
||||||
|
|
||||||
|
use chrono::{Local, NaiveDateTime, TimeZone};
|
||||||
|
use chrono::LocalResult::Single;
|
||||||
use log::{debug, error, info, trace, warn};
|
use log::{debug, error, info, trace, warn};
|
||||||
|
use nostr_sdk::Timestamp;
|
||||||
|
|
||||||
pub fn some_non_empty(str: &str) -> Option<String> {
|
pub fn some_non_empty(str: &str) -> Option<String> {
|
||||||
if str.is_empty() { None } else { Some(str.to_string()) }
|
if str.is_empty() { None } else { Some(str.to_string()) }
|
||||||
|
@ -27,3 +30,39 @@ pub fn prompt(prompt: &str) -> Option<String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For use in format strings but not possible, so need global find-replace
|
||||||
|
pub const MAX_TIMESTAMP_WIDTH: u8 = 15;
|
||||||
|
/// Format nostr Timestamp relative to local time with optional day specifier or full date if needed
|
||||||
|
pub fn relative_datetimestamp(stamp: &Timestamp) -> String {
|
||||||
|
match Local.timestamp_opt(stamp.as_u64() as i64, 0) {
|
||||||
|
Single(time) => {
|
||||||
|
let date = time.date_naive();
|
||||||
|
let prefix = match Local::now()
|
||||||
|
.date_naive()
|
||||||
|
.signed_duration_since(date)
|
||||||
|
.num_days()
|
||||||
|
{
|
||||||
|
-1 => "tomorrow ".into(),
|
||||||
|
0 => "".into(),
|
||||||
|
1 => "yesterday ".into(),
|
||||||
|
2..=6 => date.format("last %a ").to_string(),
|
||||||
|
_ => date.format("%y-%m-%d ").to_string(),
|
||||||
|
};
|
||||||
|
format!("{}{}", prefix, time.format("%H:%M"))
|
||||||
|
}
|
||||||
|
_ => stamp.to_human_datetime(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn local_datetimestamp(stamp: &Timestamp) -> String {
|
||||||
|
format_stamp(stamp, "%y-%m-%d %a %H:%M")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn format_stamp(stamp: &Timestamp, format: &str) -> String {
|
||||||
|
match Local.timestamp_opt(stamp.as_u64() as i64, 0) {
|
||||||
|
Single(time) => time.format(format).to_string(),
|
||||||
|
_ => stamp.to_human_datetime(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -384,7 +384,7 @@ async fn main() {
|
||||||
None => {
|
None => {
|
||||||
tasks.get_current_task().map_or_else(
|
tasks.get_current_task().map_or_else(
|
||||||
|| info!("With a task selected, use ,NOTE to attach NOTE and , to list all its notes"),
|
|| info!("With a task selected, use ,NOTE to attach NOTE and , to list all its notes"),
|
||||||
|task| println!("{}", task.description_events().map(|e| format!("{} {}", e.created_at.to_human_datetime(), e.content)).join("\n")),
|
|task| println!("{}", task.description_events().map(|e| format!("{} {}", local_datetimestamp(&e.created_at), e.content)).join("\n")),
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
41
src/tasks.rs
41
src/tasks.rs
|
@ -17,7 +17,7 @@ use nostr_sdk::prelude::Marker;
|
||||||
use TagStandard::Hashtag;
|
use TagStandard::Hashtag;
|
||||||
|
|
||||||
use crate::{Events, EventSender, MostrMessage};
|
use crate::{Events, EventSender, MostrMessage};
|
||||||
use crate::helpers::some_non_empty;
|
use crate::helpers::{format_stamp, local_datetimestamp, relative_datetimestamp, some_non_empty};
|
||||||
use crate::kinds::*;
|
use crate::kinds::*;
|
||||||
use crate::task::{MARKER_DEPENDS, MARKER_PARENT, State, Task, TaskState};
|
use crate::task::{MARKER_DEPENDS, MARKER_PARENT, State, Task, TaskState};
|
||||||
|
|
||||||
|
@ -183,7 +183,7 @@ impl Tasks {
|
||||||
.map(|str| EventId::from_str(str).ok().map_or(str.to_string(), |id| self.get_task_title(&id)))
|
.map(|str| EventId::from_str(str).ok().map_or(str.to_string(), |id| self.get_task_title(&id)))
|
||||||
.join(" "));
|
.join(" "));
|
||||||
if new != last {
|
if new != last {
|
||||||
full.push_str(&format!("{} {}\n", event.created_at.to_human_datetime(), new.as_ref().unwrap_or(&"---".to_string())));
|
full.push_str(&format!("{:>15} {}\n", relative_datetimestamp(&event.created_at), new.as_ref().unwrap_or(&"---".to_string())));
|
||||||
last = new;
|
last = new;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,12 +199,21 @@ impl Tasks {
|
||||||
let mut vec = Vec::with_capacity(set.len() / 2);
|
let mut vec = Vec::with_capacity(set.len() / 2);
|
||||||
let mut iter = timestamps(set.iter(), &ids).tuples();
|
let mut iter = timestamps(set.iter(), &ids).tuples();
|
||||||
while let Some(((start, _), (end, _))) = iter.next() {
|
while let Some(((start, _), (end, _))) = iter.next() {
|
||||||
vec.push(format!("{} - {} by {}", start.to_human_datetime(), end.to_human_datetime(), key))
|
vec.push(format!("{} - {} by {}",
|
||||||
|
local_datetimestamp(start),
|
||||||
|
// Only use full stamp when ambiguous (>1day)
|
||||||
|
if end.as_u64() - start.as_u64() > 86400 {
|
||||||
|
local_datetimestamp(end)
|
||||||
|
} else {
|
||||||
|
format_stamp(end, "%H:%M")
|
||||||
|
},
|
||||||
|
key))
|
||||||
}
|
}
|
||||||
iter.into_buffer().for_each(|(stamp, _)|
|
iter.into_buffer()
|
||||||
vec.push(format!("{} started by {}", stamp.to_human_datetime(), key)));
|
.for_each(|(stamp, _)|
|
||||||
|
vec.push(format!("{} started by {}", local_datetimestamp(stamp), key)));
|
||||||
vec
|
vec
|
||||||
}).sorted_unstable()
|
}).sorted_unstable() // TODO sorting depends on timestamp format - needed to interleave different people
|
||||||
).join("\n")
|
).join("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,23 +389,7 @@ impl Tasks {
|
||||||
"{} since {} (total tracked time {}m)",
|
"{} since {} (total tracked time {}m)",
|
||||||
// TODO tracking since, scheduled/planned for
|
// TODO tracking since, scheduled/planned for
|
||||||
state.get_label(),
|
state.get_label(),
|
||||||
match Local.timestamp_opt(state.time.as_u64() as i64, 0) {
|
relative_datetimestamp(&state.time),
|
||||||
Single(time) => {
|
|
||||||
let date = time.date_naive();
|
|
||||||
let prefix = match Local::now()
|
|
||||||
.date_naive()
|
|
||||||
.signed_duration_since(date)
|
|
||||||
.num_days()
|
|
||||||
{
|
|
||||||
0 => "".into(),
|
|
||||||
1 => "yesterday ".into(),
|
|
||||||
2..=6 => date.format("%a ").to_string(),
|
|
||||||
_ => date.format("%y-%m-%d ").to_string(),
|
|
||||||
};
|
|
||||||
format!("{}{}", prefix, time.format("%H:%M"))
|
|
||||||
}
|
|
||||||
_ => state.time.to_human_datetime(),
|
|
||||||
},
|
|
||||||
self.time_tracked(*t.get_id()) / 60
|
self.time_tracked(*t.get_id()) / 60
|
||||||
)?;
|
)?;
|
||||||
writeln!(lock, "{}", t.descriptions().join("\n"))?;
|
writeln!(lock, "{}", t.descriptions().join("\n"))?;
|
||||||
|
@ -708,7 +701,7 @@ impl Tasks {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn track_at(&mut self, time: Timestamp) -> EventId {
|
pub(crate) fn track_at(&mut self, time: Timestamp) -> EventId {
|
||||||
info!("{} from {}", self.position.map_or(String::from("Stopping time-tracking"), |id| format!("Tracking \"{}\"", self.get_task_title(&id))), time.to_human_datetime());
|
info!("{} from {}", self.position.map_or(String::from("Stopping time-tracking"), |id| format!("Tracking \"{}\"", self.get_task_title(&id))), relative_datetimestamp(&time));
|
||||||
let pos = self.get_position();
|
let pos = self.get_position();
|
||||||
let tracking = build_tracking(pos);
|
let tracking = build_tracking(pos);
|
||||||
// TODO this can lead to funny deletions
|
// TODO this can lead to funny deletions
|
||||||
|
|
Loading…
Reference in New Issue