diff --git a/Cargo.lock b/Cargo.lock index 1ed64bb..4cdb574 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -246,6 +246,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "const_fn" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" + [[package]] name = "cookie" version = "0.18.0" @@ -297,6 +303,7 @@ dependencies = [ "tokio", "tokio-tungstenite", "toss", + "tz-rs", "unicode-segmentation", "unicode-width", "vault", @@ -1507,6 +1514,15 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "tz-rs" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33851b15c848fad2cf4b105c6bb66eb9512b6f6c44a4b13f57c53c73c707e2b4" +dependencies = [ + "const_fn", +] + [[package]] name = "unicode-bidi" version = "0.3.14" diff --git a/cove/Cargo.toml b/cove/Cargo.toml index 9687080..b764a4a 100644 --- a/cove/Cargo.toml +++ b/cove/Cargo.toml @@ -24,6 +24,7 @@ open = "5.0.1" rusqlite = { version = "0.30.0", features = ["bundled", "time"] } serde_json = "1.0.108" tokio = { version = "1.35.1", features = ["full"] } +tz-rs = "0.6.14" unicode-segmentation = "1.10.1" unicode-width = "0.1.11" diff --git a/cove/src/main.rs b/cove/src/main.rs index e5eeb5c..0308604 100644 --- a/cove/src/main.rs +++ b/cove/src/main.rs @@ -129,14 +129,18 @@ fn update_config_with_args(config: &mut Config, args: &Args) { config.offline |= args.offline; } -fn open_vault(config: &Config, dirs: &ProjectDirs) -> rusqlite::Result { - if config.ephemeral { - vault::launch_in_memory() +fn open_vault(config: &Config, dirs: &ProjectDirs) -> anyhow::Result { + let time_zone = util::load_time_zone(config.time_zone_ref())?; + + let vault = if config.ephemeral { + vault::launch_in_memory(time_zone)? } else { let data_dir = data_dir(config, dirs); eprintln!("Data dir: {}", data_dir.to_string_lossy()); - vault::launch(&data_dir.join("vault.db")) - } + vault::launch(&data_dir.join("vault.db"), time_zone)? + }; + + Ok(vault) } #[tokio::main] diff --git a/cove/src/util.rs b/cove/src/util.rs index b6e7c97..67c0e68 100644 --- a/cove/src/util.rs +++ b/cove/src/util.rs @@ -1,4 +1,7 @@ use std::convert::Infallible; +use std::env; + +use tz::{TimeZone, TzError}; pub trait InfallibleExt { type Inner; @@ -13,3 +16,22 @@ impl InfallibleExt for Result { self.expect("infallible") } } + +/// Load a [`TimeZone`] specified by a string, or by the `TZ` environment +/// variable if no string is provided. +/// +/// If a string is provided, it is interpreted in the same format that the `TZ` +/// environment variable uses. +/// +/// If no string and no `TZ` environment variable could be found, the system +/// local time is used. +pub fn load_time_zone(tz_string: Option<&str>) -> Result { + let env_tz = env::var("TZ").ok(); + let tz_string = tz_string.or(env_tz.as_ref().map(|s| s as &str)); + + match &tz_string { + // At the moment, TimeZone::from_posix_tz does not support "localtime" on windows, but other time zon + Some("localtime") | None => TimeZone::local(), + Some(tz_string) => TimeZone::from_posix_tz(tz_string), + } +} diff --git a/cove/src/vault.rs b/cove/src/vault.rs index 6861901..250456d 100644 --- a/cove/src/vault.rs +++ b/cove/src/vault.rs @@ -6,6 +6,7 @@ use std::fs; use std::path::Path; use rusqlite::Connection; +use tz::TimeZone; use vault::tokio::TokioVault; use vault::Action; @@ -14,6 +15,7 @@ pub use self::euph::{EuphRoomVault, EuphVault, RoomIdentifier}; #[derive(Debug, Clone)] pub struct Vault { tokio_vault: TokioVault, + time_zone: TimeZone, ephemeral: bool, } @@ -46,18 +48,23 @@ impl Vault { } } -fn launch_from_connection(conn: Connection, ephemeral: bool) -> rusqlite::Result { +fn launch_from_connection( + conn: Connection, + time_zone: TimeZone, + ephemeral: bool, +) -> rusqlite::Result { conn.pragma_update(None, "foreign_keys", true)?; conn.pragma_update(None, "trusted_schema", false)?; let tokio_vault = TokioVault::launch_and_prepare(conn, &migrate::MIGRATIONS, prepare::prepare)?; Ok(Vault { tokio_vault, + time_zone, ephemeral, }) } -pub fn launch(path: &Path) -> rusqlite::Result { +pub fn launch(path: &Path, time_zone: TimeZone) -> rusqlite::Result { // If this fails, rusqlite will complain about not being able to open the db // file, which saves me from adding a separate vault error type. let _ = fs::create_dir_all(path.parent().expect("path to file")); @@ -72,10 +79,10 @@ pub fn launch(path: &Path) -> rusqlite::Result { conn.pragma_update(None, "locking_mode", "exclusive")?; conn.pragma_update(None, "journal_mode", "wal")?; - launch_from_connection(conn, false) + launch_from_connection(conn, time_zone, false) } -pub fn launch_in_memory() -> rusqlite::Result { +pub fn launch_in_memory(time_zone: TimeZone) -> rusqlite::Result { let conn = Connection::open_in_memory()?; - launch_from_connection(conn, true) + launch_from_connection(conn, time_zone, true) }