Skip to content

Commit

Permalink
Add support for chrono::DateTime<T>
Browse files Browse the repository at this point in the history
  • Loading branch information
emwalker committed Jul 20, 2024
1 parent 4109aec commit 5a39369
Show file tree
Hide file tree
Showing 4 changed files with 320 additions and 0 deletions.
234 changes: 234 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ exclude = [
[features]
default = ["old-api"]
bytes = ["dep:bytes"]
chrono = ["dep:chrono"]
embed = ["rb-sys/link-ruby"]
old-api = []
rb-sys = []

[dependencies]
bytes = { version = "1", optional = true }
chrono = { version = "0.4.38", optional = true }
magnus-macros = { version = "0.6.0", path = "magnus-macros" }
rb-sys = { version = "0.9.85", default-features = false, features = [
"bindgen-rbimpls",
Expand All @@ -42,6 +44,7 @@ magnus = { path = ".", default-features = false, features = [
"embed",
"rb-sys",
"bytes",
"chrono",
] }
rb-sys = { version = "0.9", default-features = false, features = [
"stable-api-compiled-fallback",
Expand Down
59 changes: 59 additions & 0 deletions src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,30 @@ impl IntoValue for SystemTime {
}
}

#[cfg(feature = "chrono")]
impl IntoValue for chrono::DateTime<chrono::Utc> {
#[inline]
fn into_value_with(self, ruby: &Ruby) -> Value {
let delta = self.signed_duration_since(Self::UNIX_EPOCH);
ruby.time_nano_new(delta.num_seconds(), delta.subsec_nanos() as _)
.unwrap()
.as_value()
}
}

#[cfg(feature = "chrono")]
impl IntoValue for chrono::DateTime<chrono::FixedOffset> {
#[inline]
fn into_value_with(self, ruby: &Ruby) -> Value {
use chrono::{DateTime, Utc};
let epoch = DateTime::<Utc>::UNIX_EPOCH.with_timezone(&self.timezone());
let delta = self.signed_duration_since(epoch);
ruby.time_nano_new(delta.num_seconds(), delta.subsec_nanos() as _)
.unwrap()
.as_value()
}
}

impl Object for Time {}

unsafe impl private::ReprValue for Time {}
Expand Down Expand Up @@ -294,3 +318,38 @@ impl TryConvert for SystemTime {
}
}
}

#[cfg(feature = "chrono")]
impl TryConvert for chrono::DateTime<chrono::Utc> {
fn try_convert(val: Value) -> Result<Self, Error> {
let mut timespec = timespec {
tv_sec: 0,
tv_nsec: 0,
};
protect(|| unsafe {
timespec = rb_time_timespec(val.as_rb_value());
Ruby::get_unchecked().qnil()
})?;
if timespec.tv_sec >= 0 && timespec.tv_nsec >= 0 {
let mut duration = Duration::from_secs(timespec.tv_sec as _);
duration += Duration::from_nanos(timespec.tv_nsec as _);
Ok(Self::UNIX_EPOCH + duration)
} else {
Err(Error::new(
Ruby::get_with(val).exception_arg_error(),
"time must not be negative",
))
}
}
}

#[cfg(feature = "chrono")]
impl TryConvert for chrono::DateTime<chrono::FixedOffset> {
fn try_convert(val: Value) -> Result<Self, Error> {
use chrono::{DateTime, FixedOffset, Utc};
let offset: i32 = val.funcall("utc_offset", ())?;
let dt: DateTime<Utc> = TryConvert::try_convert(val)?;
let tz = FixedOffset::east_opt(offset).unwrap();
Ok(dt.with_timezone(&tz))
}
}
Loading

0 comments on commit 5a39369

Please sign in to comment.