Crates.io | time-local |
lib.rs | time-local |
version | 0.4.1 |
source | src |
created_at | 2024-06-19 09:51:20.126335 |
updated_at | 2024-06-20 10:36:03.793594 |
description | Make working with local offsets from the [time](https://crates.io/crates/time) crate a little easier. |
homepage | https://github.com/mickvangelderen/time-local |
repository | https://github.com/mickvangelderen/time-local |
max_upload_size | |
id | 1276707 |
size | 9,390 |
In order to obtain the local time offset, time calls out to libc
s localtime_r
function.
Implementations of localtime_r
, like glibc
and musl
, call getenv("TZ")
to obtain the current value for the TZ
environment variable.
Unfortunately, values returned by getenv()
can be invalidated by calls that modify the environment, like setenv()
, unsetenv()
, or putenv()
.
For example, the following single-threaded application has a potential use after free bug:
char * value = getenv("KEY"); // obtain pointer
setenv("KEY", "new value"); // potential free
printf("KEY = %s", value); // potential use after free
The functions in Rust's std::env
module synchronize access to the environment through a lock.
However, any foreign code (including libc
implementations) is free to modify the environment without acquiring that lock.
This has led to discussion about whether Rust's std::env::set_var
should be marked unsafe.
Under the assumption that accessing the environment is implemented correctly everywhere for single-threaded programs, there can only be issues in multi-threaded programs. This is why the time crate lets you obtain the UTC offset while the number of threads is 1.
This crate provides a solution for applications that can accept using a cached value of the UTC offset by doing exactly that: caching the UTC offset at the time of invocation. Here is an example:
use time_local::{OffsetDateTimeExt, UtcOffsetExt};
fn main() {
time_local::init().expect("initialization should succeed before spawning threads");
let date = std::thread::spawn(|| {
// We can not convert a date time to it's local representation.
assert!(time::OffsetDateTime::now_utc()
.to_local()
.is_err(), "to_local should fail");
// We can use the cached UTC offset computed at application startup. Note that this is computing something
// different entirely, but it may be good enough for your application.
time::OffsetDateTime::now_utc().to_offset(time::UtcOffset::cached_local_offset())
})
.join()
.expect("thread should not panic");
println!("{date:?}")
}
Note that a UTC offset depends on both the timezone and a particular date and time.
The cached UTC offset is computed from the current machine's timezone and time.
Changes to the system's local time and/or the TZ
environment variable will not be reflected by the cached UTC offset, and the cached UTC offset used in .to_local()
does not depend on the OffsetDateTime
.
See https://github.com/time-rs/time/issues/688#issue-2346267822 for origins.