Skip to content

Commit 611ef1f

Browse files
committed
Flesh out the eq/ord impls for dates more intuitively
Relates to #934 We resolve dates or times relative to the missing complement in the fields OffsetDateTime value, essentially allowing one to easily filter on naive dates and times. We also enable the LIKE operator for Dates, such that `published~2021` would work, for example. Signed-off-by: Jim Crossley <[email protected]>
1 parent 494acd5 commit 611ef1f

File tree

1 file changed

+54
-7
lines changed

1 file changed

+54
-7
lines changed

common/src/db/query.rs

+54-7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use chrono::{Local, NaiveDateTime};
12
use human_date_parser::{from_human_time, ParseResult};
23
use regex::Regex;
34
use sea_orm::entity::ColumnDef;
@@ -105,6 +106,7 @@ impl Query {
105106
..
106107
} => context
107108
.values()
109+
.filter(|v| matches!(v, Value::String(_)))
108110
.any(|field| vs.iter().any(|v| field.contains(v))),
109111
_ => false,
110112
}
@@ -245,6 +247,7 @@ impl Value<'_> {
245247
pub fn contains(&self, pat: &str) -> bool {
246248
match self {
247249
Self::String(s) => s.contains(pat),
250+
Self::Date(d) => d.to_string().contains(pat),
248251
_ => false,
249252
}
250253
}
@@ -262,7 +265,25 @@ impl PartialEq<String> for Value<'_> {
262265
Ok(i) => v.eq(&i),
263266
_ => false,
264267
},
265-
Self::Date(_) => false, // impractical, given the granularity
268+
Self::Date(v) => match from_human_time(&v.to_string()) {
269+
Ok(ParseResult::DateTime(field)) => match from_human_time(rhs) {
270+
Ok(ParseResult::DateTime(other)) => field.eq(&other),
271+
Ok(ParseResult::Date(d)) => {
272+
let other = NaiveDateTime::new(d, field.time())
273+
.and_local_timezone(Local)
274+
.unwrap();
275+
field.eq(&other)
276+
}
277+
Ok(ParseResult::Time(t)) => {
278+
let other = NaiveDateTime::new(field.date_naive(), t)
279+
.and_local_timezone(Local)
280+
.unwrap();
281+
field.eq(&other)
282+
}
283+
_ => false,
284+
},
285+
_ => false,
286+
},
266287
}
267288
}
268289
}
@@ -280,13 +301,22 @@ impl PartialOrd<String> for Value<'_> {
280301
_ => None,
281302
},
282303
Self::Date(v) => match from_human_time(&v.to_string()) {
283-
Ok(ParseResult::DateTime(field)) => {
284-
if let Ok(ParseResult::DateTime(other)) = from_human_time(rhs) {
304+
Ok(ParseResult::DateTime(field)) => match from_human_time(rhs) {
305+
Ok(ParseResult::DateTime(other)) => field.partial_cmp(&other),
306+
Ok(ParseResult::Date(d)) => {
307+
let other = NaiveDateTime::new(d, field.time())
308+
.and_local_timezone(Local)
309+
.unwrap();
285310
field.partial_cmp(&other)
286-
} else {
287-
None
288311
}
289-
}
312+
Ok(ParseResult::Time(t)) => {
313+
let other = NaiveDateTime::new(field.date_naive(), t)
314+
.and_local_timezone(Local)
315+
.unwrap();
316+
field.partial_cmp(&other)
317+
}
318+
_ => None,
319+
},
290320
_ => None,
291321
},
292322
}
@@ -1132,20 +1162,37 @@ mod tests {
11321162

11331163
#[test(tokio::test)]
11341164
async fn apply_to_context() -> Result<(), anyhow::Error> {
1165+
use time::format_description::well_known::Rfc2822;
11351166
let now = time::OffsetDateTime::now_utc();
1167+
let then = OffsetDateTime::parse("Sat, 12 Jun 1993 13:25:19 GMT", &Rfc2822)?;
11361168
let context = HashMap::from([
11371169
("id", Value::String("foo")),
11381170
("count", Value::Int(42)),
11391171
("score", Value::Float(6.66)),
1172+
("detected", Value::Date(&then)),
11401173
("published", Value::Date(&now)),
11411174
]);
11421175
assert!(q("oo|aa|bb&count<100&count>10&id=foo").apply(&context));
11431176
assert!(q("score=6.66").apply(&context));
11441177
assert!(q("count>=42&count<=42").apply(&context));
1145-
assert!(q("published>2 days ago&published<in 1 week").apply(&context));
1178+
assert!(q("published>2 days ago&published<next week").apply(&context));
1179+
1180+
assert!(q("detected=1993-06-12").apply(&context));
1181+
assert!(q("detected>13:20:00").apply(&context));
1182+
assert!(q("detected~1993").apply(&context));
1183+
assert!(!q("1993").apply(&context));
1184+
1185+
assert!(q(&format!("published={}", now)).apply(&context));
1186+
assert!(q(&format!("published={}", now.date())).apply(&context));
1187+
assert!(q(&format!("published={}", now.time())).apply(&context));
1188+
assert!(q(&format!("published>=today {}", now.time())).apply(&context));
1189+
assert!(q(&format!("published>={}", now)).apply(&context));
1190+
assert!(q(&format!("published<={}", now.date())).apply(&context));
1191+
assert!(q(&format!("published~{}", now.time())).apply(&context));
11461192

11471193
Ok(())
11481194
}
1195+
11491196
/////////////////////////////////////////////////////////////////////////
11501197
// Test helpers
11511198
/////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)