Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add timezone configuration option & CLI overrides #1517

Merged
merged 8 commits into from Feb 6, 2024

Conversation

cyqsimon
Copy link
Contributor

@cyqsimon cyqsimon commented Jan 7, 2024

When reviewing history (usually for the purpose of filling out a work log), I often find it helpful if the timestamps are displayed in the local timezone. This PR adds this feature.

Currently, the --tz option either accepts an offset from UTC in the form of <+|-><hour>[:<minute>[:<second>]], or the special value l/local to use the system's current timezones.

I also wanted to add support for named timezones using chrono_tz, but that seems to require pulling in chrono as well, which feels very redundant considering that we already depend on time. So I left that bit out for now.

Unresolved questions

  • Are you (maintainers) willing to add chrono as a dependency for named timezone support? Or are there alternative, independent crates to chrono_tz?
  • Do you think we need tests for this? I feel like it's borderline "too simple to warrant testing", but I would be happy to add them too if you so desire.

Copy link

vercel bot commented Jan 7, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
atuin-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 10, 2024 8:42am

@ellie
Copy link
Member

ellie commented Jan 7, 2024

Generally looks good, thank you!

Are you (maintainers) willing to add chrono as a dependency for named timezone support? Or are there alternative, independent crates to chrono_tz?

If there are any other options, then I'd rather not. I'm not aware of any other crates, though time is not named particularly helpfully for googling things. If chrono is the only possible choice then that's ok though

Do you think we need tests for this? I feel like it's borderline "too simple to warrant testing", but I would be happy to add them too if you so desire.

Tests would be good, even if very bare. It would be nice to ensure this continues to work with any possible future changes too

Otherwise, would you be able to update the docs too please?

@cyqsimon
Copy link
Contributor Author

cyqsimon commented Jan 8, 2024

So I tried to implement named timezones using chrono_tz but as we all know, trying is the first step to failure.

It turns out (unsurprisingly so) that time and chrono have complete different ways and syntices to handle timezones, and are barely interoperable. With enough effort I think I can get it to work, but the maintainability will surely be bad and frankly I think it's just not worth it.

I'll add tests and call it a day I guess. If ever in the future we decide to migrate to using chrono, I will be happy to add this retroactively.

Copy link
Contributor Author

@cyqsimon cyqsimon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wrote some basic unit tests. Maybe they'll help ensure that in the future a different implementation won't change the behaviour, but apart from that they really aren't too helpful.

atuin/src/command/client/history.rs Show resolved Hide resolved
Comment on lines 567 to 573
fn can_parse_local_timezone_spec() -> Result<()> {
let local_tz = Timezone(UtcOffset::current_local_offset()?);

assert_eq!(Timezone::from_str("l")?, local_tz);
assert_eq!(Timezone::from_str("local")?, local_tz);
Ok(())
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this test is failing on Linux. I looked at it with a debugger, and noticed a contradiction between time's code and documentation. I'll submit an issue there to ask about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did some digging, and it appears that there's no mistake in time; rather it's my own misunderstanding. Still though, I have to find a way to fix this test. Has to do with some really annoying thread-safety issues so it might take me a while to get it right.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I am going down this massive rabbit hole that basically boils down to "there's no thread-safe way to get a system's timezone on POSIX systems". To be honest I'm completely flustered at this point.

To deal with this, time has implemented some rather stringent checks to ensure safety. This means this test either has to run in a single-threaded environment, or we have to accept unsoundness (which admittedly can only ever happen if libc's setenv function is called directly without locking, but still). I'll see what I can do tomorrow.

@ellie
Copy link
Member

ellie commented Jan 23, 2024

Hey! Just wondering if there's anything I can do to help this one move forwards? 🙏

@cyqsimon
Copy link
Contributor Author

Hey! Just wondering if there's anything I can do to help this one move forwards? 🙏

Thanks for offering. Here's where I'm at:

As mentioned previously, the safety checks implemented by time while justified, is making acquiring the local timezone failure-prone on Unix systems, both in release and in test.

Currently, the specific problem is that cargo test runs the tests multithreaded by default, which makes time refuse to report the local timezone. I've been trying to find a way around it (without mandating the user run tests with -j1) but for now have come up empty-handed.

There's also the long-term concern that this is essentially a landmine if in the future we ever want to make atuin-history multithreaded for whatever reason. It's just annoying all-round.

@cyqsimon
Copy link
Contributor Author

I've also explored the very hacky method of obtaining the local timezone using a shell command, something like this:

#[cfg(target_os = "linux")]
let offset = {
    let tz_raw = process::Command::new("date").arg("+%:::z").output()?.stdout;
    UtcOffset::parse(std::str::from_utf8(&tz_raw)?.trim(), OFFSET_FMT)?
};
#[cfg(not(target_os = "linux"))]
let offset = UtcOffset::current_local_offset()?;

Unfortunately, not even this is viable because unlike GNU date, POSIX date only supports printing named timezones.

I'm honestly dumbfounded that such a simple problem can cause so much trouble.

@cyqsimon
Copy link
Contributor Author

I guess I'm just looking for ideas at this point. As a last resort we can get rid of local timezone support altogether, although it really is the last resort.

@ellie
Copy link
Member

ellie commented Jan 23, 2024

I've also been unable to find anything that would be a reasonable workaround, and keep local timezone support in this way.

What might work is

  1. Add timezone config to Settings
  2. Read the local timezone right at the very beginning, before we potentially start any multithreaded runtimes.
  3. If the config file doesn't specify a timezone, override with the value we read at program start
  4. For tests, specify an offset - they have a different entry point, so will never try to read anything from the env

Doesn't quite achieve everything we'd like to, but is 80% of the way there I think.

@ellie
Copy link
Member

ellie commented Jan 23, 2024

also, thank you for looking into this so much, ik it's been more than expected

@atuin-bot
Copy link

This pull request has been mentioned on Atuin Community. There might be relevant details there:

https://forum.atuin.sh/t/feedback-on-the-inspector/89/2

@cyqsimon
Copy link
Contributor Author

  1. Read the local timezone right at the very beginning, before we potentially start any multithreaded runtimes.

This is already the case, which is why the feature works but the test fails. I can add some comments in relevant places to remind future maintainers of this requirement.

  1. Add timezone config to Settings
  2. If the config file doesn't specify a timezone, override with the value we read at program start

From my POV I don't really see a reason to make a global config for this. But maybe that's just because I'm tunnel-visioned on this single feature while you have a broader view of the whole project. Can you please clarify what other components of atuin plan to make use of this config (if indeed this is the case)?

  1. For tests, specify an offset - they have a different entry point, so will never try to read anything from the env

This test already exists as can_parse_offset_timezone_spec. So is it okay if I just get rid of can_parse_local_timezone_spec?

@ellie
Copy link
Member

ellie commented Jan 25, 2024

This is already the case, which is why the feature works but the test fails. I can add some comments in relevant places to remind future maintainers of this requirement.

I think if we keep the timezone-reading separate to everything else, we don't need to call it in the tests - if it's going to be this difficult, anyway.

Can you please clarify what other components of atuin plan to make use of this config (if indeed this is the case)?

I'm being asked about timezones a lot right now, essentially for any area that displays time. In the TUI, in the stats, etc. A global config means we can resolve that everywhere.

@atuin-bot
Copy link

This pull request has been mentioned on Atuin Community. There might be relevant details there:

https://forum.atuin.sh/t/can-i-use-local-timezone-for-my-history/101/2

@cyqsimon
Copy link
Contributor Author

cyqsimon commented Jan 26, 2024

I'm working on adding timezone as an option in settings, but saw that there is already the field local_tz. Apparently this was added about the same time as this PR (#1477).

Currently it's marked with #[serde(skip)]. I'm thinking, maybe I should just promote this (with a rename to timezone perhaps) to a full configuration option and use it. What do you think?

Edit: I thought I might as well just do it now and change it if necessary. Please take a look at the code, thanks.


Edit 2: I only realised this after the change, but this unification is a breaking change. This is because previously atuin stats uses local timezone exclusively, while atuin history uses UTC exclusively. By uniting them under one configuration option (which admittedly, is more consistent, and probably is what we want to do anyway), one of them has to break. I would like to know your opinion on this.

@cyqsimon
Copy link
Contributor Author

Rebased.

@cyqsimon cyqsimon changed the title Allow specifying a timezone in history search/list Add timezone configuration option & CLI overrides Jan 26, 2024
@ellie
Copy link
Member

ellie commented Jan 26, 2024

I'm working on adding timezone as an option in settings, but saw that there is already the field local_tz. Apparently this was added about the same time as this PR (#1477).

Ahhh I'm sorry I totally blanked on that 😭

Currently it's marked with #[serde(skip)]. I'm thinking, maybe I should just promote this (with a rename to timezone perhaps) to a full configuration option and use it. What do you think?

Seems reasonable

By uniting them under one configuration option (which admittedly, is more consistent, and probably is what we want to do anyway), one of them has to break. I would like to know your opinion on this.

I think for general guidance on timezones, we should follow

  1. Always always store UTC
  2. Display either in the locally fetched timezone, or the configured timezone

atuin-client/src/settings.rs Outdated Show resolved Hide resolved
@ellie
Copy link
Member

ellie commented Jan 26, 2024

couple of minor comments, but looks great - thank you!

@ellie
Copy link
Member

ellie commented Jan 30, 2024

Only a few conflicts left and I'll get it merged!

@cyqsimon
Copy link
Contributor Author

Rebased.

@ellie
Copy link
Member

ellie commented Feb 6, 2024

I was convinced I'd merged this, sorry!

@ellie ellie merged commit 318bdd8 into atuinsh:main Feb 6, 2024
7 of 8 checks passed
@ellie
Copy link
Member

ellie commented Feb 6, 2024

Thank you! Really happy to get this in 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants