Skip to content

Commit

Permalink
Auto merge of #3523 - saethlin:localtime_r-env, r=RalfJung
Browse files Browse the repository at this point in the history
Use the interpreted program's TZ variable in localtime_r

This requires a bit of wiring and a new dependency, but the tests should correctly pass now regardless of what the host's time zone is.

Fixes #3522
  • Loading branch information
bors committed Apr 29, 2024
2 parents 8d600fa + 92715f5 commit fc0db10
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 151 deletions.
201 changes: 76 additions & 125 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ smallvec = "1.7"
aes = { version = "0.8.3", features = ["hazmat"] }
measureme = "11"
ctrlc = "3.2.5"
chrono = { version = "0.4.38", default-features = false, features = ["clock"] }
chrono = { version = "0.4.38", default-features = false }
chrono-tz = "0.9"
directories = "5"

# Copied from `compiler/rustc/Cargo.toml`.
Expand Down
15 changes: 13 additions & 2 deletions src/shims/env.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::ffi::OsString;
use std::ffi::{OsStr, OsString};

use rustc_data_structures::fx::FxHashMap;

Expand Down Expand Up @@ -99,4 +99,15 @@ impl<'tcx> EnvVars<'tcx> {
}

impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
/// Try to get an environment variable from the interpreted program's environment. This is
/// useful for implementing shims which are documented to read from the environment.
fn get_env_var(&mut self, name: &OsStr) -> InterpResult<'tcx, Option<OsString>> {
let this = self.eval_context_ref();
match &this.machine.env_vars {
EnvVars::Uninit => return Ok(None),
EnvVars::Unix(vars) => vars.get(this, name),
EnvVars::Windows(vars) => vars.get(name),
}
}
}
26 changes: 18 additions & 8 deletions src/shims/time.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use std::ffi::OsString;
use std::ffi::{OsStr, OsString};
use std::fmt::Write;
use std::str::FromStr;
use std::time::{Duration, SystemTime};

use chrono::{DateTime, Datelike, Local, Timelike, Utc};
use chrono::{DateTime, Datelike, Offset, Timelike, Utc};
use chrono_tz::Tz;

use crate::concurrency::thread::MachineCallback;
use crate::*;
Expand Down Expand Up @@ -136,8 +138,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
.unwrap();
let dt_utc: DateTime<Utc> =
DateTime::from_timestamp(sec_since_epoch, 0).expect("Invalid timestamp");

// Figure out what time zone is in use
let tz = this.get_env_var(OsStr::new("TZ"))?.unwrap_or_else(|| OsString::from("UTC"));
let tz = match tz.into_string() {
Ok(tz) => Tz::from_str(&tz).unwrap_or(Tz::UTC),
_ => Tz::UTC,
};

// Convert that to local time, then return the broken-down time value.
let dt: DateTime<Local> = DateTime::from(dt_utc);
let dt: DateTime<Tz> = dt_utc.with_timezone(&tz);

// This value is always set to -1, because there is no way to know if dst is in effect with
// chrono crate yet.
Expand All @@ -146,17 +156,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {

// tm_zone represents the timezone value in the form of: +0730, +08, -0730 or -08.
// This may not be consistent with libc::localtime_r's result.
let offset_in_second = Local::now().offset().local_minus_utc();
let tm_gmtoff = offset_in_second;
let offset_in_seconds = dt.offset().fix().local_minus_utc();
let tm_gmtoff = offset_in_seconds;
let mut tm_zone = String::new();
if offset_in_second < 0 {
if offset_in_seconds < 0 {
tm_zone.push('-');
} else {
tm_zone.push('+');
}
let offset_hour = offset_in_second.abs() / 3600;
let offset_hour = offset_in_seconds.abs() / 3600;
write!(tm_zone, "{:02}", offset_hour).unwrap();
let offset_min = (offset_in_second.abs() % 3600) / 60;
let offset_min = (offset_in_seconds.abs() % 3600) / 60;
if offset_min != 0 {
write!(tm_zone, "{:02}", offset_min).unwrap();
}
Expand Down
Loading

0 comments on commit fc0db10

Please sign in to comment.