Skip to content

Commit

Permalink
Remove ability to determine local offset on Unix
Browse files Browse the repository at this point in the history
  • Loading branch information
jhpratt committed Nov 18, 2020
1 parent e499a6c commit 01513cb
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 86 deletions.
2 changes: 2 additions & 0 deletions src/offset_date_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ impl OffsetDateTime {
///
/// ```rust
/// # use time::OffsetDateTime;
/// # if false {
/// assert!(OffsetDateTime::now_local().is_ok());
/// # }
/// ```
#[cfg(feature = "local-offset")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "local-offset")))]
Expand Down
172 changes: 90 additions & 82 deletions src/utc_offset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,9 @@ impl UtcOffset {
/// # use time::{UtcOffset, OffsetDateTime};
/// let unix_epoch = OffsetDateTime::unix_epoch();
/// let local_offset = UtcOffset::local_offset_at(unix_epoch);
/// # if false {
/// assert!(local_offset.is_ok());
/// # }
/// ```
#[cfg(feature = "local-offset")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "local-offset")))]
Expand All @@ -267,7 +269,9 @@ impl UtcOffset {
/// ```rust
/// # use time::UtcOffset;
/// let local_offset = UtcOffset::current_local_offset();
/// # if false {
/// assert!(local_offset.is_ok());
/// # }
/// ```
#[cfg(feature = "local-offset")]
#[cfg_attr(__time_03_docs, doc(cfg(feature = "local-offset")))]
Expand Down Expand Up @@ -337,92 +341,96 @@ impl Display for UtcOffset {
/// Attempt to obtain the system's UTC offset. If the offset cannot be
/// determined, `None` is returned.
#[cfg(feature = "local-offset")]
#[allow(clippy::too_many_lines)]
#[allow(clippy::too_many_lines, clippy::missing_const_for_fn)]
fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> {
#[cfg(target_family = "unix")]
{
use core::{convert::TryInto, mem::MaybeUninit};

/// Convert the given Unix timestamp to a `libc::tm`. Returns `None` on
/// any error.
fn timestamp_to_tm(timestamp: i64) -> Option<libc::tm> {
extern "C" {
#[cfg_attr(target_os = "netbsd", link_name = "__tzset50")]
fn tzset();
}

// The exact type of `timestamp` beforehand can vary, so this
// conversion is necessary.
#[allow(clippy::useless_conversion)]
let timestamp = timestamp.try_into().ok()?;

let mut tm = MaybeUninit::uninit();

// Update timezone information from system. `localtime_r` does not
// do this for us.
//
// Safety: tzset is thread-safe.
#[allow(unsafe_code)]
unsafe {
tzset();
}

// Safety: We are calling a system API, which mutates the `tm`
// variable. If a null pointer is returned, an error occurred.
#[allow(unsafe_code)]
let tm_ptr = unsafe { libc::localtime_r(&timestamp, tm.as_mut_ptr()) };

if tm_ptr.is_null() {
None
} else {
// Safety: The value was initialized, as we no longer have a
// null pointer.
#[allow(unsafe_code)]
{
Some(unsafe { tm.assume_init() })
}
}
}

let tm = timestamp_to_tm(datetime.unix_timestamp())?;

// `tm_gmtoff` extension
#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
{
UtcOffset::seconds(tm.tm_gmtoff.try_into().ok()?).ok()
}

// No `tm_gmtoff` extension
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
{
use crate::Date;
use core::convert::TryFrom;

let mut tm = tm;
if tm.tm_sec == 60 {
// Leap seconds are not currently supported.
tm.tm_sec = 59;
}
// See #293 for details.
let _ = datetime;
None

let local_timestamp =
Date::from_yo(1900 + tm.tm_year, u16::try_from(tm.tm_yday).ok()? + 1)
.ok()?
.with_hms(
tm.tm_hour.try_into().ok()?,
tm.tm_min.try_into().ok()?,
tm.tm_sec.try_into().ok()?,
)
.ok()?
.assume_utc()
.unix_timestamp();

UtcOffset::seconds(
(local_timestamp - datetime.unix_timestamp())
.try_into()
.ok()?,
)
.ok()
}
// use core::{convert::TryInto, mem::MaybeUninit};
//
// /// Convert the given Unix timestamp to a `libc::tm`. Returns `None` on
// /// any error.
// fn timestamp_to_tm(timestamp: i64) -> Option<libc::tm> {
// extern "C" {
// #[cfg_attr(target_os = "netbsd", link_name = "__tzset50")]
// fn tzset();
// }
//
// // The exact type of `timestamp` beforehand can vary, so this
// // conversion is necessary.
// #[allow(clippy::useless_conversion)]
// let timestamp = timestamp.try_into().ok()?;
//
// let mut tm = MaybeUninit::uninit();
//
// // Update timezone information from system. `localtime_r` does not
// // do this for us.
// //
// // Safety: tzset is thread-safe.
// #[allow(unsafe_code)]
// unsafe {
// tzset();
// }
//
// // Safety: We are calling a system API, which mutates the `tm`
// // variable. If a null pointer is returned, an error occurred.
// #[allow(unsafe_code)]
// let tm_ptr = unsafe { libc::localtime_r(&timestamp, tm.as_mut_ptr()) };
//
// if tm_ptr.is_null() {
// None
// } else {
// // Safety: The value was initialized, as we no longer have a
// // null pointer.
// #[allow(unsafe_code)]
// {
// Some(unsafe { tm.assume_init() })
// }
// }
// }
//
// let tm = timestamp_to_tm(datetime.unix_timestamp())?;
//
// // `tm_gmtoff` extension
// #[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
// {
// UtcOffset::seconds(tm.tm_gmtoff.try_into().ok()?).ok()
// }
//
// // No `tm_gmtoff` extension
// #[cfg(any(target_os = "solaris", target_os = "illumos"))]
// {
// use crate::Date;
// use core::convert::TryFrom;
//
// let mut tm = tm;
// if tm.tm_sec == 60 {
// // Leap seconds are not currently supported.
// tm.tm_sec = 59;
// }
//
// let local_timestamp =
// Date::from_yo(1900 + tm.tm_year, u16::try_from(tm.tm_yday).ok()? + 1)
// .ok()?
// .with_hms(
// tm.tm_hour.try_into().ok()?,
// tm.tm_min.try_into().ok()?,
// tm.tm_sec.try_into().ok()?,
// )
// .ok()?
// .assume_utc()
// .unix_timestamp();
//
// UtcOffset::seconds(
// (local_timestamp - datetime.unix_timestamp())
// .try_into()
// .ok()?,
// )
// .ok()
// }
}
#[cfg(target_family = "windows")]
{
Expand Down
2 changes: 1 addition & 1 deletion tests/offset_date_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn now_utc() {
}

#[test]
#[cfg(feature = "local-offset")]
#[cfg(all(feature = "local-offset", not(target_family = "unix")))]
fn now_local() {
assert!(OffsetDateTime::now_local().is_ok());
}
Expand Down
6 changes: 3 additions & 3 deletions tests/utc_offset.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#[cfg(feature = "local-offset")]
#[cfg(all(feature = "local-offset", not(target_family = "unix")))]
use time::OffsetDateTime;
use time::{Result, UtcOffset};
use time_macros::offset;
Expand Down Expand Up @@ -125,13 +125,13 @@ fn display() {
}

#[test]
#[cfg(feature = "local-offset")]
#[cfg(all(feature = "local-offset", not(target_family = "unix")))]
fn local_offset_at() {
assert!(UtcOffset::local_offset_at(OffsetDateTime::unix_epoch()).is_ok());
}

#[test]
#[cfg(feature = "local-offset")]
#[cfg(all(feature = "local-offset", not(target_family = "unix")))]
fn current_local_offset() {
assert!(UtcOffset::current_local_offset().is_ok());
}

0 comments on commit 01513cb

Please sign in to comment.