Skip to content

Commit

Permalink
Fixes for IntoValue for chrono::DateTime<Utc> and chrono::DateTime<Fi…
Browse files Browse the repository at this point in the history
…xedOffset>
  • Loading branch information
emwalker committed Jul 28, 2024
1 parent 0d9d781 commit 77233f6
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 16 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
between `chrono::DateTime<Utc>` and `chrono::DateTime<FixedOffset>` and Ruby
`Time` objects.
- `typed_data::Writebarrier::writebarrier` and `writebarrier_unprotect`.
- Bug fixes for `IntoValue` for `chrono::DateTime<Utc>` and
`chrono::DateTime<FixedOffset>`

### Changed
- Conversions between Ruby's `Time` and Rust's `SystemTime` now preserve
Expand Down
33 changes: 17 additions & 16 deletions src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,9 +261,10 @@ 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()
let val: Time = ruby
.time_nano_new(delta.num_seconds(), delta.subsec_nanos() as _)
.unwrap();
val.funcall("utc", ()).unwrap()
}
}

Expand All @@ -272,12 +273,15 @@ impl IntoValue for chrono::DateTime<chrono::Utc> {
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 _)
use chrono::{DateTime, FixedOffset, Utc};
let delta = self.signed_duration_since(DateTime::<Utc>::UNIX_EPOCH);
let val = ruby
.time_nano_new(delta.num_seconds(), delta.subsec_nanos() as _)
.unwrap()
.as_value();
let offset: FixedOffset = self.timezone().into();
val.funcall("getlocal", (offset.local_minus_utc(),))
.unwrap()
.as_value()
}
}

Expand Down Expand Up @@ -335,15 +339,12 @@ impl TryConvert for chrono::DateTime<chrono::Utc> {
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(
match chrono::Duration::new(timespec.tv_sec as _, timespec.tv_nsec as _) {
Some(duration) => Ok(Self::UNIX_EPOCH + duration),
None => Err(Error::new(
Ruby::get_with(val).exception_arg_error(),
"time must not be negative",
))
"invalid timespec",
)),
}
}
}
Expand Down
24 changes: 24 additions & 0 deletions tests/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#[cfg(feature = "chrono")]
fn it_supports_chrono() {
use chrono::{DateTime, Datelike, FixedOffset, Utc};
use magnus::rb_assert;
let ruby = unsafe { magnus::embed::init() };

let t = ruby.eval::<DateTime<Utc>>("Time.at(0, 10, :nsec)").unwrap();
Expand All @@ -14,11 +15,34 @@ fn it_supports_chrono() {
.eval::<DateTime<Utc>>(r#"Time.new(1971, 1, 1, 2, 2, 2.0000001, "Z")"#)
.unwrap();
assert_eq!(&dt.to_rfc3339(), "1971-01-01T02:02:02.000000099+00:00");
rb_assert!(ruby, "dt.utc?", dt);
rb_assert!(ruby, "dt.utc_offset == 0", dt);

let dt = ruby
.eval::<DateTime<Utc>>(r#"Time.new(1950, 1, 1)"#)
.unwrap();
assert_eq!(&dt.to_rfc3339(), "1950-01-01T07:00:00+00:00");

let dt = ruby
.eval::<DateTime<Utc>>(r#"Time.new(1971, 1, 1, 2, 2, 2.0000001, "-07:00")"#)
.unwrap();
assert_eq!(&dt.to_rfc3339(), "1971-01-01T09:02:02.000000099+00:00");

let dt = ruby
.eval::<DateTime<FixedOffset>>(
r#"Time.new(2022, 5, 31, 9, 8, 123456789/1000000000r, "-07:00")"#,
)
.unwrap();
assert_eq!(&dt.to_rfc3339(), "2022-05-31T09:08:00.123456789-07:00");
rb_assert!(ruby, "!dt.utc?", dt);
rb_assert!(ruby, "dt.utc_offset == -25200", dt);

let dt = ruby
.eval::<DateTime<FixedOffset>>(
r#"Time.new(2022, 5, 31, 9, 8, 123456789/1000000000r, "+05:30")"#,
)
.unwrap();
assert_eq!(&dt.to_rfc3339(), "2022-05-31T09:08:00.123456789+05:30");
rb_assert!(ruby, "!dt.utc?", dt);
rb_assert!(ruby, "dt.utc_offset == 19800", dt);
}

0 comments on commit 77233f6

Please sign in to comment.