Skip to content

Commit

Permalink
use tz::TimeZone for az-cli time conversion on unix platforms (#1431)
Browse files Browse the repository at this point in the history
As indicated in #1371, there are numerous issues with handling the
az-cli's output of an access token's expiration date when it comes to
time zones.

An update to the Azure CLI is in progress that will add a _new_ field
that will handle the conversion, currently tracked as Azure/azure-cli#27476.

We should make use of this new feature once it's available, but we
should also keep this functionality as a fallback for supporting older
Azure CLIs.
  • Loading branch information
demoray committed Oct 3, 2023
1 parent 669d80f commit 1455c3d
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 1 deletion.
3 changes: 3 additions & 0 deletions sdk/identity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ openssl = { version = "0.10.46", optional=true }
uuid = { version = "1.0", features = ["v4"] }
pin-project = "1.0"

[target.'cfg(unix)'.dependencies]
tz-rs = "0.6"

[dev-dependencies]
reqwest = { version = "0.11", features = ["json"], default-features = false }
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
Expand Down
32 changes: 31 additions & 1 deletion sdk/identity/src/token_credentials/azure_cli_credentials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ mod az_cli_date_format {
use serde::{self, Deserialize, Deserializer};
use time::format_description::FormatItem;
use time::macros::format_description;
use time::{OffsetDateTime, PrimitiveDateTime, UtcOffset};
#[cfg(not(unix))]
use time::UtcOffset;
use time::{OffsetDateTime, PrimitiveDateTime};

const FORMAT: &[FormatItem] =
format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:6]");
Expand All @@ -24,7 +26,35 @@ mod az_cli_date_format {
Ok(assume_local(&dt))
}

#[cfg(unix)]
/// attempt to convert `PrimitiveDateTime` to `OffsetDate` using
/// `tz::TimeZone`. If any part of the conversion fails, such as if no
/// timezone can be found, then use use the value as UTC.
pub(crate) fn assume_local(date: &PrimitiveDateTime) -> OffsetDateTime {
let as_utc = date.assume_utc();

// if we can't get the local timezone, just return the UTC date
let Ok(tz) = tz::TimeZone::local() else {
return as_utc;
};

let as_unix = as_utc.unix_timestamp();
// if we can't convert the unix timestamp to a DateTime, just return the UTC date
let Ok(date) = tz::DateTime::from_timespec(as_unix, 0, tz.as_ref()) else {
return as_utc;
};

// if we can't then convert to unix time (with the timezone) and then
// back into an OffsetDateTime, then return the UTC date
let Ok(date) = OffsetDateTime::from_unix_timestamp(date.unix_time()) else {
return as_utc;
};

date
}

/// Assumes the local offset. Default to UTC if unable to get local offset.
#[cfg(not(unix))]
pub(crate) fn assume_local(date: &PrimitiveDateTime) -> OffsetDateTime {
date.assume_offset(UtcOffset::current_local_offset().unwrap_or(UtcOffset::UTC))
}
Expand Down

0 comments on commit 1455c3d

Please sign in to comment.