// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms.
use std::{cell::RefCell, collections::hash_map, env, fs, hash::Hasher, time::SystemTime};
// we have to store the `Cache` in an option as it can't // be initialized in a static context.
thread_local! { static TZ_INFO: RefCell<Option<Cache>> = Default::default();
}
impl Source {
fn new(env_tz: Option<&str>) -> Source { match env_tz {
Some(tz) => { letmut hasher = hash_map::DefaultHasher::new();
hasher.write(tz.as_bytes()); let hash = hasher.finish();
Source::Environment { hash }
}
None => match fs::symlink_metadata("/etc/localtime") {
Ok(data) => Source::LocalTime { // we have to pick a sensible default when the mtime fails // by picking SystemTime::now() we raise the probability of // the cache being invalidated if/when the mtime starts working
mtime: data.modified().unwrap_or_else(|_| SystemTime::now()),
},
Err(_) => { // as above, now() should be a better default than some constant // TODO: see if we can improve caching in the case where the fallback is a valid timezone
Source::LocalTime { mtime: SystemTime::now() }
}
},
}
}
}
impl Default for Cache {
fn default() -> Cache { // default to UTC if no local timezone can be found let env_tz = env::var("TZ").ok(); let env_ref = env_tz.as_deref();
Cache {
last_checked: SystemTime::now(),
source: Source::new(env_ref),
zone: current_zone(env_ref),
}
}
}
impl Cache {
fn offset(&mutself, d: NaiveDateTime, local: bool) -> MappedLocalTime<FixedOffset> { let now = SystemTime::now();
match now.duration_since(self.last_checked) { // If the cache has been around for less than a second then we reuse it // unconditionally. This is a reasonable tradeoff because the timezone // generally won't be changing _that_ often, but if the time zone does // change, it will reflect sufficiently quickly from an application // user's perspective.
Ok(d) if d.as_secs() < 1 => (),
Ok(_) | Err(_) => { let env_tz = env::var("TZ").ok(); let env_ref = env_tz.as_deref(); let new_source = Source::new(env_ref);
let out_of_date = match (&self.source, &new_source) { // change from env to file or file to env, must recreate the zone
(Source::Environment { .. }, Source::LocalTime { .. })
| (Source::LocalTime { .. }, Source::Environment { .. }) => true, // stay as file, but mtime has changed
(Source::LocalTime { mtime: old_mtime }, Source::LocalTime { mtime }) if old_mtime != mtime =>
{ true
} // stay as env, but hash of variable has changed
(Source::Environment { hash: old_hash }, Source::Environment { hash }) if old_hash != hash =>
{ true
} // cache can be reused
_ => false,
};
if out_of_date { self.zone = current_zone(env_ref);
}
// we pass through the year as the year of a local point in time must either be valid in that locale, or // the entire time was skipped in which case we will return MappedLocalTime::None anyway. self.zone
.find_local_time_type_from_local(d)
.expect("unable to select local time type")
.and_then(|o| FixedOffset::east_opt(o.offset()))
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.