simple-web-app

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 5be90e38ef14a2134b181bb9d8e12e8088e8719b
parent 4b95f59b077a512fa1f78c4d5e506666eb3259e7
Author: Silas Brack <silasbrack@gmail.com>
Date:   Sat, 13 Jun 2026 15:31:12 +0200

feat: epoch integer timestamps, drop chrono

All timestamps are now epoch seconds (i64) instead of text strings.
Unambiguous (always UTC), smaller (8 bytes vs 19), no parsing.

time_ago() and hot_score() use integer subtraction instead of
chrono date parsing. Removes chrono dependency (3 transitive deps).

Migration 0005 converts all existing text timestamps via
strftime('%s', column).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Diffstat:
MCargo.lock | 190-------------------------------------------------------------------------------
MCargo.toml | 1-
Amigrations/0005_epoch_timestamps.sql | 203+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/database.rs | 88+++++++++++++++++++++++++------------------------------------------------------
Msrc/handlers/feed.rs | 2+-
Msrc/handlers/story.rs | 4++--
Msrc/handlers/tag.rs | 2+-
Msrc/handlers/user.rs | 4++--
Msrc/migrations.rs | 1+
9 files changed, 238 insertions(+), 257 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -15,15 +15,6 @@ dependencies = [ ] [[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] name = "argon2" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -206,48 +197,18 @@ dependencies = [ ] [[package]] -name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] -name = "cc" -version = "1.2.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link", -] - -[[package]] name = "cookie" version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -259,12 +220,6 @@ dependencies = [ ] [[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] name = "cpufeatures" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -316,12 +271,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] name = "form_urlencoded" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -498,46 +447,12 @@ dependencies = [ ] [[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] name = "itoa" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] -name = "js-sys" -version = "0.3.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] name = "libc" version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -826,12 +741,6 @@ dependencies = [ ] [[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] name = "simple-web-app" version = "0.1.0" dependencies = [ @@ -839,7 +748,6 @@ dependencies = [ "askama", "axum", "axum-extra", - "chrono", "rand", "rusqlite", "serde", @@ -1058,110 +966,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasm-bindgen" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] name = "windows-sys" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" axum = "0.8" axum-extra = { version = "0.10", features = ["cookie"] } askama = "0.12" -chrono = { version = "0.4", features = ["serde"] } tokio = { version = "1", features = ["macros", "rt-multi-thread", "net"] } serde = { version = "1", features = ["derive"] } tokio-stream = { version = "0.1", features = ["sync"] } diff --git a/migrations/0005_epoch_timestamps.sql b/migrations/0005_epoch_timestamps.sql @@ -0,0 +1,203 @@ +-- Convert all timestamp columns from TEXT to INTEGER (epoch seconds). +-- Epoch integers are unambiguous (always UTC), smaller, and don't +-- need parsing. + +PRAGMA foreign_keys=OFF; +BEGIN IMMEDIATE; + +-- Drop triggers and indices (same as migration 0003) +DROP TRIGGER IF EXISTS story_fts_insert; +DROP TRIGGER IF EXISTS story_fts_update; +DROP TRIGGER IF EXISTS story_fts_delete; +DROP TRIGGER IF EXISTS comment_count_insert; +DROP TRIGGER IF EXISTS comment_count_delete; +DROP TRIGGER IF EXISTS vote_insert_story; +DROP TRIGGER IF EXISTS vote_delete_story; +DROP TRIGGER IF EXISTS vote_insert_comment; +DROP TRIGGER IF EXISTS vote_delete_comment; + +DROP INDEX IF EXISTS idx_story_published; +DROP INDEX IF EXISTS idx_story_score; +DROP INDEX IF EXISTS idx_story_created_by; +DROP INDEX IF EXISTS idx_comment_story_id; +DROP INDEX IF EXISTS idx_comment_parent_id; +DROP INDEX IF EXISTS idx_comment_created_by; +DROP INDEX IF EXISTS idx_vote_user_target; +DROP INDEX IF EXISTS idx_story_category_news_item; +DROP INDEX IF EXISTS idx_story_category_category; +DROP INDEX IF EXISTS idx_session_user_id; + +DROP TABLE IF EXISTS story_fts; + +-- Recreate tables with INTEGER timestamps + +ALTER TABLE user RENAME TO user_old; +CREATE TABLE user ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + email TEXT UNIQUE NOT NULL, + password_hash TEXT NOT NULL, + username TEXT UNIQUE NOT NULL, + about TEXT, + karma INTEGER DEFAULT 0, + created_at INTEGER NOT NULL, + email_verified INTEGER NOT NULL DEFAULT 0, + verification_token TEXT +) STRICT; +INSERT INTO user (id, email, password_hash, username, about, karma, created_at, email_verified, verification_token) +SELECT id, email, password_hash, username, about, karma, + CAST(strftime('%s', created_at) AS INTEGER), + email_verified, verification_token +FROM user_old; +DROP TABLE user_old; + +ALTER TABLE category RENAME TO category_old; +CREATE TABLE category ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT +) STRICT; +INSERT INTO category SELECT * FROM category_old; +DROP TABLE category_old; + +ALTER TABLE story RENAME TO story_old; +CREATE TABLE story ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + url TEXT, + title TEXT NOT NULL, + text TEXT NOT NULL DEFAULT '', + published INTEGER NOT NULL, + author TEXT, + language TEXT NOT NULL DEFAULT 'en', + created_by INTEGER REFERENCES user(id), + score INTEGER NOT NULL DEFAULT 0, + comment_count INTEGER NOT NULL DEFAULT 0, + domain TEXT +) STRICT; +INSERT INTO story (id, url, title, text, published, author, language, created_by, score, comment_count, domain) +SELECT id, url, title, text, + CAST(strftime('%s', published) AS INTEGER), + author, language, created_by, score, comment_count, domain +FROM story_old; +DROP TABLE story_old; + +ALTER TABLE comment RENAME TO comment_old; +CREATE TABLE comment ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + story_id INTEGER NOT NULL REFERENCES story(id), + parent_id INTEGER, + path TEXT NOT NULL DEFAULT '', + depth INTEGER NOT NULL DEFAULT 0, + text TEXT NOT NULL DEFAULT '', + score INTEGER NOT NULL DEFAULT 0, + created_at INTEGER NOT NULL, + created_by INTEGER NOT NULL REFERENCES user(id) +) STRICT; +INSERT INTO comment (id, story_id, parent_id, path, depth, text, score, created_at, created_by) +SELECT id, story_id, parent_id, path, depth, text, score, + CAST(strftime('%s', created_at) AS INTEGER), + created_by +FROM comment_old; +DROP TABLE comment_old; + +ALTER TABLE vote RENAME TO vote_old; +CREATE TABLE vote ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL REFERENCES user(id), + target_type INTEGER NOT NULL, + target_id INTEGER NOT NULL, + created_at INTEGER NOT NULL, + UNIQUE(user_id, target_type, target_id) +) STRICT; +INSERT INTO vote (id, user_id, target_type, target_id, created_at) +SELECT id, user_id, target_type, target_id, + CAST(strftime('%s', created_at) AS INTEGER) +FROM vote_old; +DROP TABLE vote_old; + +ALTER TABLE story_category RENAME TO story_category_old; +CREATE TABLE story_category ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + news_item_id INTEGER NOT NULL REFERENCES story(id), + category_id INTEGER NOT NULL REFERENCES category(id) +) STRICT; +INSERT INTO story_category SELECT * FROM story_category_old; +DROP TABLE story_category_old; + +ALTER TABLE session RENAME TO session_old; +CREATE TABLE session ( + token TEXT PRIMARY KEY NOT NULL, + user_id INTEGER NOT NULL REFERENCES user(id), + created_at INTEGER NOT NULL +) STRICT; +INSERT INTO session (token, user_id, created_at) +SELECT token, user_id, + CAST(strftime('%s', created_at) AS INTEGER) +FROM session_old; +DROP TABLE session_old; + +-- Recreate indices +CREATE INDEX idx_story_published ON story(published DESC); +CREATE INDEX idx_story_score ON story(score DESC); +CREATE INDEX idx_story_created_by ON story(created_by); +CREATE INDEX idx_comment_story_id ON comment(story_id); +CREATE INDEX idx_comment_parent_id ON comment(parent_id); +CREATE INDEX idx_comment_created_by ON comment(created_by); +CREATE INDEX idx_vote_user_target ON vote(user_id, target_type, target_id); +CREATE INDEX idx_story_category_news_item ON story_category(news_item_id); +CREATE INDEX idx_story_category_category ON story_category(category_id); +CREATE INDEX idx_session_user_id ON session(user_id); + +-- Recreate FTS +CREATE VIRTUAL TABLE story_fts USING fts5( + title, text, + content='story', + content_rowid='id', + tokenize='porter unicode61' +); +INSERT INTO story_fts(rowid, title, text) SELECT id, title, text FROM story; + +-- Recreate triggers +CREATE TRIGGER story_fts_insert AFTER INSERT ON story BEGIN + INSERT INTO story_fts(rowid, title, text) VALUES (new.id, new.title, new.text); +END; + +CREATE TRIGGER story_fts_update AFTER UPDATE ON story BEGIN + INSERT INTO story_fts(story_fts, rowid, title, text) VALUES ('delete', old.id, old.title, old.text); + INSERT INTO story_fts(rowid, title, text) VALUES (new.id, new.title, new.text); +END; + +CREATE TRIGGER story_fts_delete AFTER DELETE ON story BEGIN + INSERT INTO story_fts(story_fts, rowid, title, text) VALUES ('delete', old.id, old.title, old.text); +END; + +CREATE TRIGGER comment_count_insert AFTER INSERT ON comment BEGIN + UPDATE story SET comment_count = comment_count + 1 WHERE id = new.story_id; +END; + +CREATE TRIGGER comment_count_delete AFTER DELETE ON comment BEGIN + UPDATE story SET comment_count = comment_count - 1 WHERE id = old.story_id; +END; + +CREATE TRIGGER vote_insert_story AFTER INSERT ON vote WHEN new.target_type = 1 BEGIN + UPDATE story SET score = score + 1 WHERE id = new.target_id; + UPDATE user SET karma = karma + 1 WHERE id = (SELECT created_by FROM story WHERE id = new.target_id); +END; + +CREATE TRIGGER vote_delete_story AFTER DELETE ON vote WHEN old.target_type = 1 BEGIN + UPDATE story SET score = score - 1 WHERE id = old.target_id; + UPDATE user SET karma = karma - 1 WHERE id = (SELECT created_by FROM story WHERE id = old.target_id); +END; + +CREATE TRIGGER vote_insert_comment AFTER INSERT ON vote WHEN new.target_type = 2 BEGIN + UPDATE comment SET score = score + 1 WHERE id = new.target_id; + UPDATE user SET karma = karma + 1 WHERE id = (SELECT created_by FROM comment WHERE id = new.target_id); +END; + +CREATE TRIGGER vote_delete_comment AFTER DELETE ON vote WHEN old.target_type = 2 BEGIN + UPDATE comment SET score = score - 1 WHERE id = old.target_id; + UPDATE user SET karma = karma - 1 WHERE id = (SELECT created_by FROM comment WHERE id = old.target_id); +END; + +UPDATE migration_version SET version = 5; +COMMIT; + +PRAGMA foreign_keys=ON; diff --git a/src/database.rs b/src/database.rs @@ -22,7 +22,7 @@ pub struct Story { pub domain: Option<String>, pub score: i64, pub comment_count: i64, - pub published: String, + pub published: i64, pub created_by: Option<String>, pub author: Option<String>, pub language: String, @@ -37,7 +37,7 @@ pub struct Comment { pub depth: i64, pub text: String, pub score: i64, - pub created_at: String, + pub created_at: i64, pub created_by: String, } @@ -55,7 +55,7 @@ pub struct User { pub username: String, pub about: Option<String>, pub karma: i64, - pub created_at: String, + pub created_at: i64, pub email_verified: bool, pub verification_token: Option<String>, } @@ -79,7 +79,7 @@ impl From<User> for UserProfile { username: u.username, about: u.about, karma: u.karma, - created_at: u.created_at, + created_at: time_ago(u.created_at), } } } @@ -113,7 +113,7 @@ pub struct StoryWithMeta { pub domain: Option<String>, pub score: i64, pub comment_count: i64, - pub published: String, + pub published: i64, pub created_by: Option<String>, pub tags: Vec<Category>, pub author_username: Option<String>, @@ -160,7 +160,7 @@ pub struct CommentWithMeta { pub indent_px: i64, pub text: String, pub score: i64, - pub created_at: String, + pub created_at: i64, pub created_by: String, pub author_username: Option<String>, pub time_ago: String, @@ -325,8 +325,8 @@ impl Database { .map_err(|e| AppError::Database(e.to_string()))?; stories.sort_by(|a, b| { - let score_a = hot_score(a.score, &a.published); - let score_b = hot_score(b.score, &b.published); + let score_a = hot_score(a.score, a.published); + let score_b = hot_score(b.score, b.published); score_b.partial_cmp(&score_a).unwrap_or(std::cmp::Ordering::Equal) }); @@ -359,7 +359,7 @@ impl Database { language: &str, ) -> Result<Story, AppError> { let conn = self.write_conn()?; - let published = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(); + let published = now_epoch(); conn.execute( "INSERT INTO story (title, url, text, domain, published, language, created_by) @@ -949,66 +949,34 @@ impl Database { // Helper functions // ========================================================================== -/// Calculate hot score for ranking (HN-style algorithm) -pub fn hot_score(points: i64, published: &str) -> f64 { - let age_hours = hours_since(published); - (points as f64) / (age_hours + 2.0).powf(1.8) +pub fn now_epoch() -> i64 { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as i64 } -/// Calculate hours since a timestamp -fn hours_since(timestamp: &str) -> f64 { - use chrono::{DateTime, Utc}; - - let published = timestamp - .parse::<DateTime<Utc>>() - .or_else(|_| { - chrono::NaiveDateTime::parse_from_str(timestamp, "%Y-%m-%d %H:%M:%S") - .map(|dt| dt.and_utc()) - }) - .unwrap_or_else(|_| Utc::now()); - - let now = Utc::now(); - let duration = now.signed_duration_since(published); - duration.num_minutes() as f64 / 60.0 +pub fn hot_score(points: i64, published: i64) -> f64 { + let age_hours = (now_epoch() - published) as f64 / 3600.0; + (points as f64) / (age_hours + 2.0).powf(1.8) } -/// Format a timestamp as "X ago" string -pub fn time_ago(timestamp: &str) -> String { - use chrono::{DateTime, Utc}; - - let published = timestamp - .parse::<DateTime<Utc>>() - .or_else(|_| { - chrono::NaiveDateTime::parse_from_str(timestamp, "%Y-%m-%d %H:%M:%S") - .map(|dt| dt.and_utc()) - }) - .unwrap_or_else(|_| Utc::now()); - - let now = Utc::now(); - let duration = now.signed_duration_since(published); +pub fn time_ago(timestamp: i64) -> String { + let elapsed = now_epoch() - timestamp; - let minutes = duration.num_minutes(); - let hours = duration.num_hours(); - let days = duration.num_days(); + let days = elapsed / 86400; + let hours = elapsed / 3600; + let minutes = elapsed / 60; if days > 0 { - if days == 1 { - "1 day ago".to_string() - } else { - format!("{} days ago", days) - } + if days == 1 { "1 day ago".to_string() } + else { format!("{days} days ago") } } else if hours > 0 { - if hours == 1 { - "1 hour ago".to_string() - } else { - format!("{} hours ago", hours) - } + if hours == 1 { "1 hour ago".to_string() } + else { format!("{hours} hours ago") } } else if minutes > 0 { - if minutes == 1 { - "1 minute ago".to_string() - } else { - format!("{} minutes ago", minutes) - } + if minutes == 1 { "1 minute ago".to_string() } + else { format!("{minutes} minutes ago") } } else { "just now".to_string() } diff --git a/src/handlers/feed.rs b/src/handlers/feed.rs @@ -90,7 +90,7 @@ async fn fetch_feed( .unwrap_or_default(); let author_username = story_meta.and_then(|m| m.author_username.clone()); - let time_ago = database::time_ago(&story.published); + let time_ago = database::time_ago(story.published); let user_voted = voted_set.contains(&story.id); let rank = base_rank + idx + 1; diff --git a/src/handlers/story.rs b/src/handlers/story.rs @@ -50,7 +50,7 @@ async fn fetch_enriched_comments( for comment in comments { let uid: i64 = comment.created_by.parse().unwrap_or(0); let comment_author = author_map.get(&uid).cloned(); - let comment_time_ago = database::time_ago(&comment.created_at); + let comment_time_ago = database::time_ago(comment.created_at); let comment_voted = voted_comment_ids.contains(&comment.id); enriched.push(CommentWithMeta::from_comment( @@ -102,7 +102,7 @@ pub async fn show_story( Ok((story, tags, author_username, story_voted)) }).await?; - let time_ago = database::time_ago(&story.published); + let time_ago = database::time_ago(story.published); let story_with_meta = StoryWithMeta::from_story(story, tags, author_username, time_ago, story_voted, 0); let enriched_comments = fetch_enriched_comments(&state, user_id, id).await?; diff --git a/src/handlers/tag.rs b/src/handlers/tag.rs @@ -69,7 +69,7 @@ pub async fn show_tag( .unwrap_or_default(); let author_username = story_meta.and_then(|m| m.author_username.clone()); - let time_ago = database::time_ago(&story.published); + let time_ago = database::time_ago(story.published); let user_voted = voted_ids.contains(&story.id); let rank = base_rank + idx + 1; diff --git a/src/handlers/user.rs b/src/handlers/user.rs @@ -67,7 +67,7 @@ pub async fn show_user( let tags = story_meta .map(|m| m.tags.iter().map(|name| database::Category { id: 0, name: Some(name.clone()) }).collect()) .unwrap_or_default(); - let time_ago = database::time_ago(&story.published); + let time_ago = database::time_ago(story.published); let user_voted = voted_ids.contains(&story.id); submissions.push(StoryWithMeta::from_story( @@ -84,7 +84,7 @@ pub async fn show_user( let user_comments = db.get_comments_by_user(user_id_num, 30, 0)?; for comment in user_comments { - let time_ago = database::time_ago(&comment.created_at); + let time_ago = database::time_ago(comment.created_at); comments.push(CommentWithMeta::from_comment( comment, Some(profile.username.clone()), diff --git a/src/migrations.rs b/src/migrations.rs @@ -5,6 +5,7 @@ const MIGRATIONS: &[&str] = &[ include_str!("../migrations/0002_email_verification.sql"), include_str!("../migrations/0003_strict_tables.sql"), include_str!("../migrations/0004_sessions.sql"), + include_str!("../migrations/0005_epoch_timestamps.sql"), ]; pub fn run(conn: &Connection) -> Result<(), String> {