use rusqlite::Connection;
use time::{OffsetDateTime, format_description::well_known::Rfc3339};
#[must_use]
pub fn format_iso8601(mtime_ms: u64) -> Option<String> {
let secs = i128::from(mtime_ms) * 1_000_000;
let dt = OffsetDateTime::from_unix_timestamp_nanos(secs).ok()?;
let dt = dt.replace_nanosecond(0).ok()?;
dt.format(&Rfc3339).ok()
}
const MTIME_CASE_SQL: &str = "CASE \
WHEN MS_EXPR / 1000 >= CAST(strftime('%s', 'now') AS INTEGER) - 86400 \
THEN strftime('%H:%M', MS_EXPR / 1000, 'unixepoch', 'localtime') \
ELSE strftime('%Y-%m-%d', MS_EXPR / 1000, 'unixepoch', 'localtime') \
END";
fn mtime_case(ms_expr: &str) -> String {
MTIME_CASE_SQL.replace("MS_EXPR", ms_expr)
}
#[must_use]
pub fn format_local_mtime(conn: &Connection, mtime_ms: i64) -> Option<String> {
let sql = format!("SELECT {}", mtime_case("?1"));
conn.query_row(&sql, [mtime_ms], |row| row.get::<_, Option<String>>(0))
.ok()
.flatten()
}
#[must_use]
pub fn local_mtime_for_path(conn: &Connection, vault_path: &str) -> Option<String> {
let sql = format!(
"SELECT {} FROM notes WHERE vault_path = ?1 AND active = 1",
mtime_case("mtime_ms")
);
conn.query_row(&sql, [vault_path], |row| row.get::<_, Option<String>>(0))
.ok()
.flatten()
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
use crate::indexing::migrations::run_migrations;
fn fresh_db() -> Connection {
let mut conn = Connection::open_in_memory().unwrap();
run_migrations(&mut conn).unwrap();
conn
}
#[test]
fn formats_unix_ms_as_rfc3339_zulu() {
assert_eq!(
format_iso8601(1_776_211_200_000),
Some("2026-04-15T00:00:00Z".to_string())
);
}
#[test]
fn truncates_subsecond_precision() {
assert_eq!(
format_iso8601(1_776_211_200_789),
Some("2026-04-15T00:00:00Z".to_string())
);
}
#[test]
fn out_of_range_returns_none() {
assert!(format_iso8601(u64::MAX).is_none());
}
#[test]
fn format_local_mtime_old_timestamp_returns_date() {
let conn = fresh_db();
let s = format_local_mtime(&conn, 1_579_046_400_000).expect("should format");
assert_eq!(s.len(), 10, "expected YYYY-MM-DD, got {s:?}");
assert_eq!(s.chars().nth(4), Some('-'));
assert_eq!(s.chars().nth(7), Some('-'));
}
#[test]
fn format_local_mtime_recent_timestamp_returns_hh_mm() {
let conn = fresh_db();
let now_ms = i64::try_from(
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis(),
)
.unwrap();
let s = format_local_mtime(&conn, now_ms).expect("should format");
assert_eq!(s.len(), 5, "expected HH:MM, got {s:?}");
assert_eq!(s.chars().nth(2), Some(':'));
}
#[test]
fn local_mtime_for_missing_path_returns_none() {
let conn = fresh_db();
assert!(local_mtime_for_path(&conn, "does/not/exist.md").is_none());
}
}