mirror of
https://github.com/atuinsh/atuin.git
synced 2025-03-08 12:13:45 +01:00
Add timezone configuration option & CLI overrides (#1517)
* Allow specifying a timezone in history search/list * Fix clippy complaints * Add a bit more comment on supporting named timezones * Add rudimentary tests * Ditch local timezone test * Timezone configuration support * Set default timezone to `local` * `--tz` -> `--timezone` `--tz` is kept as a visible alias
This commit is contained in:
parent
8372abb613
commit
318bdd8955
133
Cargo.lock
generated
133
Cargo.lock
generated
@ -55,6 +55,21 @@ version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[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 = "anstream"
|
||||
version = "0.6.11"
|
||||
@ -240,6 +255,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_regex",
|
||||
"serde_with",
|
||||
"sha2",
|
||||
"shellexpand",
|
||||
"sql-builder",
|
||||
@ -568,6 +584,19 @@ dependencies = [
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.3.0"
|
||||
@ -865,6 +894,41 @@ dependencies = [
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.8"
|
||||
@ -1567,6 +1631,35 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"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 = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
@ -1591,6 +1684,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown 0.12.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1601,6 +1695,7 @@ checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2946,6 +3041,35 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5c9fdb6b00a489875b22efd4b78fe2b363b72265cc5f6eb2e2b9ee270e6140c"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"chrono",
|
||||
"hex",
|
||||
"indexmap 1.9.3",
|
||||
"indexmap 2.1.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with_macros",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "3.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbff351eb4b33600a2e138dfa0b10b65a238ea8ff8fb2387c422c5022a3e8298"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
@ -4088,6 +4212,15 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
@ -22,7 +22,7 @@ atuin-common = { path = "../atuin-common", version = "17.2.1" }
|
||||
|
||||
log = { workspace = true }
|
||||
base64 = { workspace = true }
|
||||
time = { workspace = true }
|
||||
time = { workspace = true, features = ["macros", "formatting"] }
|
||||
clap = { workspace = true }
|
||||
eyre = { workspace = true }
|
||||
directories = { workspace = true }
|
||||
@ -53,6 +53,7 @@ thiserror = { workspace = true }
|
||||
futures = "0.3"
|
||||
crypto_secretbox = "0.1.1"
|
||||
generic-array = { version = "0.14", features = ["serde"] }
|
||||
serde_with = "3.5.1"
|
||||
|
||||
# encryption
|
||||
rusty_paseto = { version = "0.6.0", default-features = false }
|
||||
|
@ -16,6 +16,12 @@
|
||||
## date format used, either "us" or "uk"
|
||||
# dialect = "us"
|
||||
|
||||
## default timezone to use when displaying time
|
||||
## either "l", "local" to use the system's current local timezone, or an offset
|
||||
## from UTC in the format of "<+|->H[H][:M[M][:S[S]]]"
|
||||
## for example: "+9", "-05", "+03:30", "-01:23:45", etc.
|
||||
# timezone = "local"
|
||||
|
||||
## enable or disable automatic sync
|
||||
# auto_sync = true
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
convert::TryFrom,
|
||||
fmt,
|
||||
io::prelude::*,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
@ -11,13 +12,18 @@ use clap::ValueEnum;
|
||||
use config::{
|
||||
builder::DefaultState, Config, ConfigBuilder, Environment, File as ConfigFile, FileFormat,
|
||||
};
|
||||
use eyre::{eyre, Context, Result};
|
||||
use eyre::{bail, eyre, Context, Error, Result};
|
||||
use fs_err::{create_dir_all, File};
|
||||
use parse_duration::parse;
|
||||
use regex::RegexSet;
|
||||
use semver::Version;
|
||||
use serde::Deserialize;
|
||||
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
|
||||
use serde_with::DeserializeFromStr;
|
||||
use time::{
|
||||
format_description::{well_known::Rfc3339, FormatItem},
|
||||
macros::format_description,
|
||||
OffsetDateTime, UtcOffset,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub const HISTORY_PAGE_SIZE: i64 = 100;
|
||||
@ -123,6 +129,46 @@ impl From<Dialect> for interim::Dialect {
|
||||
}
|
||||
}
|
||||
|
||||
/// Type wrapper around `time::UtcOffset` to support a wider variety of timezone formats.
|
||||
///
|
||||
/// Note that the parsing of this struct needs to be done before starting any
|
||||
/// multithreaded runtime, otherwise it will fail on most Unix systems.
|
||||
///
|
||||
/// See: https://github.com/atuinsh/atuin/pull/1517#discussion_r1447516426
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, DeserializeFromStr)]
|
||||
pub struct Timezone(pub UtcOffset);
|
||||
impl fmt::Display for Timezone {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
/// format: <+|-><hour>[:<minute>[:<second>]]
|
||||
static OFFSET_FMT: &[FormatItem<'_>] =
|
||||
format_description!("[offset_hour sign:mandatory padding:none][optional [:[offset_minute padding:none][optional [:[offset_second padding:none]]]]]");
|
||||
impl FromStr for Timezone {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
// local timezone
|
||||
if matches!(s.to_lowercase().as_str(), "l" | "local") {
|
||||
let offset = UtcOffset::current_local_offset()?;
|
||||
return Ok(Self(offset));
|
||||
}
|
||||
|
||||
// offset from UTC
|
||||
if let Ok(offset) = UtcOffset::parse(s, OFFSET_FMT) {
|
||||
return Ok(Self(offset));
|
||||
}
|
||||
|
||||
// IDEA: Currently named timezones are not supported, because the well-known crate
|
||||
// for this is `chrono_tz`, which is not really interoperable with the datetime crate
|
||||
// that we currently use - `time`. If ever we migrate to using `chrono`, this would
|
||||
// be a good feature to add.
|
||||
|
||||
bail!(r#""{s}" is not a valid timezone spec"#)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Copy)]
|
||||
pub enum Style {
|
||||
#[serde(rename = "auto")]
|
||||
@ -251,6 +297,7 @@ pub struct Sync {
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct Settings {
|
||||
pub dialect: Dialect,
|
||||
pub timezone: Timezone,
|
||||
pub style: Style,
|
||||
pub auto_sync: bool,
|
||||
pub update_check: bool,
|
||||
@ -305,11 +352,6 @@ pub struct Settings {
|
||||
// config! Keep secrets and settings apart.
|
||||
#[serde(skip)]
|
||||
pub session_token: String,
|
||||
|
||||
// This is determined at startup and cached.
|
||||
// This is due to non-threadsafe get-env limitations.
|
||||
#[serde(skip)]
|
||||
pub local_tz: Option<time::UtcOffset>,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
@ -488,6 +530,7 @@ impl Settings {
|
||||
.set_default("key_path", key_path.to_str())?
|
||||
.set_default("session_path", session_path.to_str())?
|
||||
.set_default("dialect", "us")?
|
||||
.set_default("timezone", "local")?
|
||||
.set_default("auto_sync", true)?
|
||||
.set_default("update_check", cfg!(feature = "check-update"))?
|
||||
.set_default("sync_address", "https://api.atuin.sh")?
|
||||
@ -599,8 +642,6 @@ impl Settings {
|
||||
settings.session_token = String::from("not logged in");
|
||||
}
|
||||
|
||||
settings.local_tz = time::UtcOffset::current_local_offset().ok();
|
||||
|
||||
Ok(settings)
|
||||
}
|
||||
|
||||
@ -621,3 +662,42 @@ impl Default for Settings {
|
||||
.expect("Could not deserialize config")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
|
||||
use eyre::Result;
|
||||
|
||||
use super::Timezone;
|
||||
|
||||
#[test]
|
||||
fn can_parse_offset_timezone_spec() -> Result<()> {
|
||||
assert_eq!(Timezone::from_str("+02")?.0.as_hms(), (2, 0, 0));
|
||||
assert_eq!(Timezone::from_str("-04")?.0.as_hms(), (-4, 0, 0));
|
||||
assert_eq!(Timezone::from_str("+05:30")?.0.as_hms(), (5, 30, 0));
|
||||
assert_eq!(Timezone::from_str("-09:30")?.0.as_hms(), (-9, -30, 0));
|
||||
|
||||
// single digit hours are allowed
|
||||
assert_eq!(Timezone::from_str("+2")?.0.as_hms(), (2, 0, 0));
|
||||
assert_eq!(Timezone::from_str("-4")?.0.as_hms(), (-4, 0, 0));
|
||||
assert_eq!(Timezone::from_str("+5:30")?.0.as_hms(), (5, 30, 0));
|
||||
assert_eq!(Timezone::from_str("-9:30")?.0.as_hms(), (-9, -30, 0));
|
||||
|
||||
// fully qualified form
|
||||
assert_eq!(Timezone::from_str("+09:30:00")?.0.as_hms(), (9, 30, 0));
|
||||
assert_eq!(Timezone::from_str("-09:30:00")?.0.as_hms(), (-9, -30, 0));
|
||||
|
||||
// these offsets don't really exist but are supported anyway
|
||||
assert_eq!(Timezone::from_str("+0:5")?.0.as_hms(), (0, 5, 0));
|
||||
assert_eq!(Timezone::from_str("-0:5")?.0.as_hms(), (0, -5, 0));
|
||||
assert_eq!(Timezone::from_str("+01:23:45")?.0.as_hms(), (1, 23, 45));
|
||||
assert_eq!(Timezone::from_str("-01:23:45")?.0.as_hms(), (-1, -23, -45));
|
||||
|
||||
// require a leading sign for clarity
|
||||
assert!(Timezone::from_str("5").is_err());
|
||||
assert!(Timezone::from_str("10:30").is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ use atuin_client::{
|
||||
record::sqlite_store::SqliteStore,
|
||||
settings::{
|
||||
FilterMode::{Directory, Global, Session},
|
||||
Settings,
|
||||
Settings, Timezone,
|
||||
},
|
||||
};
|
||||
|
||||
@ -71,6 +71,14 @@ pub enum Cmd {
|
||||
#[arg(action = clap::ArgAction::Set)]
|
||||
reverse: bool,
|
||||
|
||||
/// Display the command time in another timezone other than the configured default.
|
||||
///
|
||||
/// This option takes one of the following kinds of values:
|
||||
/// - the special value "local" (or "l") which refers to the system time zone
|
||||
/// - an offset from UTC (e.g. "+9", "-2:30")
|
||||
#[arg(long, visible_alias = "tz")]
|
||||
timezone: Option<Timezone>,
|
||||
|
||||
/// Available variables: {command}, {directory}, {duration}, {user}, {host}, {exit} and {time}.
|
||||
/// Example: --format "{time} - [{duration}] - {directory}$\t{command}"
|
||||
#[arg(long, short)]
|
||||
@ -86,6 +94,14 @@ pub enum Cmd {
|
||||
#[arg(long)]
|
||||
cmd_only: bool,
|
||||
|
||||
/// Display the command time in another timezone other than the configured default.
|
||||
///
|
||||
/// This option takes one of the following kinds of values:
|
||||
/// - the special value "local" (or "l") which refers to the system time zone
|
||||
/// - an offset from UTC (e.g. "+9", "-2:30")
|
||||
#[arg(long, visible_alias = "tz")]
|
||||
timezone: Option<Timezone>,
|
||||
|
||||
/// Available variables: {command}, {directory}, {duration}, {user}, {host} and {time}.
|
||||
/// Example: --format "{time} - [{duration}] - {directory}$\t{command}"
|
||||
#[arg(long, short)]
|
||||
@ -121,6 +137,7 @@ pub fn print_list(
|
||||
format: Option<&str>,
|
||||
print0: bool,
|
||||
reverse: bool,
|
||||
tz: Timezone,
|
||||
) {
|
||||
let w = std::io::stdout();
|
||||
let mut w = w.lock();
|
||||
@ -150,8 +167,12 @@ pub fn print_list(
|
||||
let entry_terminator = if print0 { "\0" } else { "\n" };
|
||||
let flush_each_line = print0;
|
||||
|
||||
for h in iterator {
|
||||
let fh = FmtHistory(h, CmdFormat::for_output(&w));
|
||||
for history in iterator {
|
||||
let fh = FmtHistory {
|
||||
history,
|
||||
cmd_format: CmdFormat::for_output(&w),
|
||||
tz: &tz,
|
||||
};
|
||||
let args = parsed_fmt.with_args(&fh);
|
||||
let write = write!(w, "{args}{entry_terminator}");
|
||||
if let Err(err) = args.status() {
|
||||
@ -179,14 +200,19 @@ fn check_for_write_errors(write: Result<(), io::Error>) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around `History` so we can format output dynamically at runtime
|
||||
struct FmtHistory<'a>(&'a History, CmdFormat);
|
||||
/// Type wrapper around `History` with formatting settings.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct FmtHistory<'a> {
|
||||
history: &'a History,
|
||||
cmd_format: CmdFormat,
|
||||
tz: &'a Timezone,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum CmdFormat {
|
||||
Literal,
|
||||
Escaped,
|
||||
}
|
||||
|
||||
impl CmdFormat {
|
||||
fn for_output<O: IsTerminal>(out: &O) -> Self {
|
||||
if out.is_terminal() {
|
||||
@ -205,35 +231,41 @@ impl FormatKey for FmtHistory<'_> {
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
fn fmt(&self, key: &str, f: &mut fmt::Formatter<'_>) -> Result<(), FormatKeyError> {
|
||||
match key {
|
||||
"command" => match self.1 {
|
||||
CmdFormat::Literal => f.write_str(self.0.command.trim()),
|
||||
CmdFormat::Escaped => f.write_str(&self.0.command.trim().escape_control()),
|
||||
"command" => match self.cmd_format {
|
||||
CmdFormat::Literal => f.write_str(self.history.command.trim()),
|
||||
CmdFormat::Escaped => f.write_str(&self.history.command.trim().escape_control()),
|
||||
}?,
|
||||
"directory" => f.write_str(self.0.cwd.trim())?,
|
||||
"exit" => f.write_str(&self.0.exit.to_string())?,
|
||||
"directory" => f.write_str(self.history.cwd.trim())?,
|
||||
"exit" => f.write_str(&self.history.exit.to_string())?,
|
||||
"duration" => {
|
||||
let dur = Duration::from_nanos(std::cmp::max(self.0.duration, 0) as u64);
|
||||
let dur = Duration::from_nanos(std::cmp::max(self.history.duration, 0) as u64);
|
||||
format_duration_into(dur, f)?;
|
||||
}
|
||||
"time" => {
|
||||
self.0
|
||||
self.history
|
||||
.timestamp
|
||||
.to_offset(self.tz.0)
|
||||
.format(TIME_FMT)
|
||||
.map_err(|_| fmt::Error)?
|
||||
.fmt(f)?;
|
||||
}
|
||||
"relativetime" => {
|
||||
let since = OffsetDateTime::now_utc() - self.0.timestamp;
|
||||
let since = OffsetDateTime::now_utc() - self.history.timestamp;
|
||||
let d = Duration::try_from(since).unwrap_or_default();
|
||||
format_duration_into(d, f)?;
|
||||
}
|
||||
"host" => f.write_str(
|
||||
self.0
|
||||
self.history
|
||||
.hostname
|
||||
.split_once(':')
|
||||
.map_or(&self.0.hostname, |(host, _)| host),
|
||||
.map_or(&self.history.hostname, |(host, _)| host),
|
||||
)?,
|
||||
"user" => f.write_str(
|
||||
self.history
|
||||
.hostname
|
||||
.split_once(':')
|
||||
.map_or("", |(_, user)| user),
|
||||
)?,
|
||||
"user" => f.write_str(self.0.hostname.split_once(':').map_or("", |(_, user)| user))?,
|
||||
_ => return Err(FormatKeyError::UnknownKey),
|
||||
}
|
||||
Ok(())
|
||||
@ -353,6 +385,7 @@ impl Cmd {
|
||||
include_deleted: bool,
|
||||
print0: bool,
|
||||
reverse: bool,
|
||||
tz: Timezone,
|
||||
) -> Result<()> {
|
||||
let filters = match (session, cwd) {
|
||||
(true, true) => [Session, Directory],
|
||||
@ -374,6 +407,7 @@ impl Cmd {
|
||||
},
|
||||
print0,
|
||||
reverse,
|
||||
tz,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
@ -411,11 +445,13 @@ impl Cmd {
|
||||
cmd_only,
|
||||
print0,
|
||||
reverse,
|
||||
timezone,
|
||||
format,
|
||||
} => {
|
||||
let mode = ListMode::from_flags(human, cmd_only);
|
||||
let tz = timezone.unwrap_or(settings.timezone);
|
||||
Self::handle_list(
|
||||
db, settings, context, session, cwd, mode, format, false, print0, reverse,
|
||||
db, settings, context, session, cwd, mode, format, false, print0, reverse, tz,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@ -423,10 +459,12 @@ impl Cmd {
|
||||
Self::Last {
|
||||
human,
|
||||
cmd_only,
|
||||
timezone,
|
||||
format,
|
||||
} => {
|
||||
let last = db.last().await?;
|
||||
let last = last.as_ref().map(std::slice::from_ref).unwrap_or_default();
|
||||
let tz = timezone.unwrap_or(settings.timezone);
|
||||
print_list(
|
||||
last,
|
||||
ListMode::from_flags(human, cmd_only),
|
||||
@ -436,6 +474,7 @@ impl Cmd {
|
||||
},
|
||||
false,
|
||||
true,
|
||||
tz,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
@ -10,7 +10,7 @@ use atuin_client::{
|
||||
encryption,
|
||||
history::{store::HistoryStore, History},
|
||||
record::sqlite_store::SqliteStore,
|
||||
settings::{FilterMode, KeymapMode, SearchMode, Settings},
|
||||
settings::{FilterMode, KeymapMode, SearchMode, Settings, Timezone},
|
||||
};
|
||||
|
||||
use super::history::ListMode;
|
||||
@ -101,6 +101,14 @@ pub struct Cmd {
|
||||
#[arg(long, short)]
|
||||
reverse: bool,
|
||||
|
||||
/// Display the command time in another timezone other than the configured default.
|
||||
///
|
||||
/// This option takes one of the following kinds of values:
|
||||
/// - the special value "local" (or "l") which refers to the system time zone
|
||||
/// - an offset from UTC (e.g. "+9", "-2:30")
|
||||
#[arg(long, visible_alias = "tz")]
|
||||
timezone: Option<Timezone>,
|
||||
|
||||
/// Available variables: {command}, {directory}, {duration}, {user}, {host}, {time}, {exit} and
|
||||
/// {relativetime}.
|
||||
/// Example: --format "{time} - [{duration}] - {directory}$\t{command}"
|
||||
@ -220,12 +228,15 @@ impl Cmd {
|
||||
None => Some(settings.history_format.as_str()),
|
||||
_ => self.format.as_deref(),
|
||||
};
|
||||
let tz = self.timezone.unwrap_or(settings.timezone);
|
||||
|
||||
super::history::print_list(
|
||||
&entries,
|
||||
ListMode::from_flags(self.human, self.cmd_only),
|
||||
format,
|
||||
false,
|
||||
true,
|
||||
tz,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
@ -86,8 +86,7 @@ impl Cmd {
|
||||
self.period.join(" ")
|
||||
};
|
||||
|
||||
let now = OffsetDateTime::now_utc();
|
||||
let now = settings.local_tz.map_or(now, |local| now.to_offset(local));
|
||||
let now = OffsetDateTime::now_utc().to_offset(settings.timezone.0);
|
||||
let last_night = now.replace_time(Time::MIDNIGHT);
|
||||
|
||||
let history = if words.as_str() == "all" {
|
||||
|
Loading…
Reference in New Issue
Block a user