parse go times with tz

master
Bel LaPointe 2024-05-22 15:21:11 -04:00
parent c7b0fc8dba
commit 74b02118bc
2 changed files with 175 additions and 5 deletions

View File

@ -18,15 +18,63 @@ impl Tasks {
}
pub fn from_reader(r: impl std::io::Read) -> Result<Tasks, String> {
let result = Tasks::_from_reader(r)?;
if !result.is_legacy() {
return Ok(result);
}
let mut v2 = Tasks::new();
for k in vec!["todo", "scheduled", "done"] {
v2.0.extend(
result.0[0]
.get_value(k.to_string())
.or(Some(serde_yaml::Value::Null))
.unwrap()
.as_sequence()
.or(Some(&vec![]))
.unwrap()
.iter()
.map(|v| {
Task::from_value(match k {
"done" => {
let mut t = Task::new();
t.set_value(
"_done".to_string(),
v.clone(),
);
serde_yaml::Value::from(t.0)
},
_ => v.clone(),
})
})
);
}
Ok(v2)
}
fn _from_reader(mut r: impl std::io::Read) -> Result<Tasks, String> {
let mut buff = String::new();
match r.read_to_string(&mut buff) {
Err(msg) => {
return Err(format!("failed to read body: {}", msg));
},
_ => {}
};
let mut result = Tasks::new();
match serde_yaml::from_reader::<_, Vec<serde_yaml::Value>>(r) {
match serde_yaml::from_str::<Vec<serde_yaml::Value>>(&buff) {
Ok(v) => {
result.0.extend(v.iter().map(|x| {
Task::from_value(x.clone())
}));
Ok(result)
},
Err(msg) => Err(format!("failed to read: {}", msg)),
Err(msg) => match Task::from_str(buff) {
Ok(t) => {
result.0.push(t);
Ok(result)
},
Err(_) => Err(format!("failed to parse yaml: {}", msg)),
},
}
}
@ -41,6 +89,13 @@ impl Tasks {
.collect()
)
}
fn is_legacy(&self) -> bool {
self.len() == 1
&& self.0[0].get_value("done".to_string()).is_some()
&& self.0[0].get_value("scheduled".to_string()).is_some()
&& self.0[0].get_value("todo".to_string()).is_some()
}
}
#[cfg(test)]
@ -56,6 +111,20 @@ mod test_tasks {
assert_eq!(2, tasks.due().len());
}
#[test]
fn from_reader_legacy() {
let tasks = Tasks::from_reader(
std::fs::File::open("./src/testdata/legacy.yaml").expect("failed to open file")
).expect("failed to read file");
assert_eq!(8, tasks.len());
assert_eq!(5, tasks.due().len());
assert_eq!("a".to_string(), tasks.due().0[0].get("is".to_string()).expect("missing 0th is"));
assert_eq!("b".to_string(), tasks.due().0[1].get("todo".to_string()).expect("missing 1st todo"));
assert_eq!("c".to_string(), tasks.due().0[2].get("is".to_string()).expect("missing 2nd is"));
assert_eq!("d".to_string(), tasks.due().0[3].get("todo".to_string()).expect("missing 3rd todo"));
assert_eq!("e".to_string(), tasks.due().0[4].get("todo".to_string()).expect("missing 4th todo"));
}
#[test]
fn from_reader() {
let tasks = Tasks::from_reader(
@ -79,8 +148,16 @@ impl Task {
Task(serde_yaml::Mapping::new())
}
pub fn from_reader(r: impl std::io::Read) -> Result<Task, String> {
match serde_yaml::from_reader::<_, serde_yaml::Value>(r) {
pub fn from_reader(mut r: impl std::io::Read) -> Result<Task, String> {
let mut buff = String::new();
match r.read_to_string(&mut buff) {
Err(msg) => Err(format!("failed to read body: {}", msg)),
_ => Task::from_str(buff),
}
}
pub fn from_str(s: String) -> Result<Task, String> {
match serde_yaml::from_str::<serde_yaml::Value>(&s) {
Ok(v) => Ok(Task::from_value(v)),
Err(msg) => Err(format!("failed to read value: {}", msg)),
}
@ -169,6 +246,17 @@ impl Task {
mod test_task {
use super::*;
#[test]
fn from_str() {
assert!(Task::from_str("{ invalid".to_string()).is_err());
assert!(Task::from_str("1".to_string()).is_ok());
assert!(Task::from_str("'1'".to_string()).is_ok());
assert!(Task::from_str("null".to_string()).is_ok());
assert!(Task::from_str("true".to_string()).is_ok());
assert!(Task::from_str("[]".to_string()).is_ok());
assert!(Task::from_str("{}".to_string()).is_ok());
}
#[test]
fn from_reader() {
let task = Task::from_reader(
@ -483,6 +571,7 @@ impl TS {
}
fn new(src: String) -> Result<TS, String> {
// %Y-%m-%dT%H:%MZ
match DateTime::parse_from_str(
&format!("{} +0000", src),
"%Y-%m-%dT%H:%MZ %z",
@ -490,6 +579,8 @@ impl TS {
Ok(v) => { return Ok(TS(v.timestamp() as u64)) },
_ => {},
};
// %Y-%m-%dT%H
match NaiveDateTime::parse_from_str(
&format!("{}:00", src),
"%Y-%m-%dT%H:%M",
@ -497,6 +588,8 @@ impl TS {
Ok(v) => { return Ok(TS(v.and_utc().timestamp() as u64)) },
_ => {},
};
// %Y-%m-%d
match NaiveDateTime::parse_from_str(
&format!("{}T00:00", src),
"%Y-%m-%dT%H:%M",
@ -504,7 +597,31 @@ impl TS {
Ok(v) => { return Ok(TS(v.and_utc().timestamp() as u64)) },
_ => {},
};
Err(format!("cannot parse date format from {}", src))
// Sun Dec 3 23:29:27 EST 2023
match DateTime::parse_from_str(
&format!("{}", src)
.replace("PDT", "-0800")
.replace("MDT", "-0700")
.replace("EDT", "-0400")
.replace("PST", "-0700")
.replace("MST", "-0600")
.replace("EST", "-0500")
.replace(" 1 ", " 01 ")
.replace(" 2 ", " 02 ")
.replace(" 3 ", " 03 ")
.replace(" 4 ", " 04 ")
.replace(" 5 ", " 05 ")
.replace(" 6 ", " 06 ")
.replace(" 7 ", " 07 ")
.replace(" 8 ", " 08 ")
.replace(" 9 ", " 09 ")
.replace(" ", " "),
"%a %b %d %H:%M:%S %z %Y",
) {
Ok(v) => Ok(TS(v.timestamp() as u64)),
Err(msg) => Err(format!("failed to parse legacy golang time: {}", msg)),
}
}
fn unix(&self) -> u64 {
@ -525,6 +642,42 @@ mod test_ts {
#[test]
fn parse() {
match TS::new("Tue Nov 7 07:33:11 PST 2023".to_string()) {
Ok(ts) => {
assert_eq!("2023-11-07T14:33Z", ts.to_string());
},
Err(err) => assert!(false, "failed to parse ts: {}", err),
};
match TS::new("Sun Jun 4 05:24:32 PDT 2023".to_string()) {
Ok(ts) => {
assert_eq!("2023-06-04T13:24Z", ts.to_string());
},
Err(err) => assert!(false, "failed to parse ts: {}", err),
};
match TS::new("Sat Nov 4 08:36:01 MDT 2023".to_string()) {
Ok(ts) => {
assert_eq!("2023-11-04T15:36Z", ts.to_string());
},
Err(err) => assert!(false, "failed to parse ts: {}", err),
};
match TS::new("Sun Nov 5 22:27:17 MST 2023".to_string()) {
Ok(ts) => {
assert_eq!("2023-11-06T04:27Z", ts.to_string());
},
Err(err) => assert!(false, "failed to parse ts: {}", err),
};
match TS::new("Thu Apr 18 16:17:41 EDT 2024".to_string()) {
Ok(ts) => {
assert_eq!("2024-04-18T20:17Z", ts.to_string());
},
Err(err) => assert!(false, "failed to parse ts: {}", err),
};
match TS::new("Sun Dec 3 23:29:27 EST 2023".to_string()) {
Ok(ts) => {
assert_eq!("2023-12-04T04:29Z", ts.to_string());
},
Err(err) => assert!(false, "failed to parse ts: {}", err),
};
match TS::new("2024-05-01T00:00Z".to_string()) {
Ok(ts) => {
assert_eq!(1714521600, ts.unix());

17
pttodoer/src/testdata/legacy.yaml vendored Normal file
View File

@ -0,0 +1,17 @@
todo:
- a
- todo: b
schedule: 2000-01-01
scheduled:
- c
- todo: d
schedule: 2000-02-01
- todo: e
ts: Sun Dec 3 23:29:27 EST 2023
schedule: '0 0 1 1 *'
- todo: f
schedule: 2099-02-02
done:
- g
- todo: h
ts: Sun Dec 3 23:29:27 EST 2023