From 7698c9230b52b9306770aea91070e1c88b925991 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Wed, 17 Jul 2024 19:10:38 +0200 Subject: [PATCH 1/6] allow conversion (with loss of precision) for arbitrary precision POSIXct objects --- src/convert/datetime.jl | 10 ++++++++-- test/convert/datetime.jl | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/convert/datetime.jl b/src/convert/datetime.jl index de0853cb..21b89a3c 100644 --- a/src/convert/datetime.jl +++ b/src/convert/datetime.jl @@ -6,8 +6,14 @@ rcopy(::Type{Date}, s::Ptr{RealSxp}) = rcopy(Date, s[1]) rcopy(::Type{DateTime}, s::Ptr{RealSxp}) = rcopy(DateTime, s[1]) rcopy(::Type{Date}, x::Float64) = Date(Dates.UTInstant(Dates.Day((isnan(x) ? 0 : x) + 719163))) -rcopy(::Type{DateTime}, x::Float64) = - DateTime(Dates.UTInstant(Dates.Millisecond(((isnan(x) ? 0 : x) + 62135683200) * 1000))) +function rcopy(::Type{DateTime}, x::Float64) + ms = ((isnan(x) ? 0 : x) + 62135683200) * 1_000 + if !isinteger(ms) + @warn "Precision lost in conversion to DateTime" + end + ms = round(Int, ms) + return DateTime(Dates.UTInstant(Millisecond(ms))) +end # implicit conversion `rcopy(d)`. function rcopytype(::Type{RClass{:Date}}, s::Ptr{RealSxp}) diff --git a/test/convert/datetime.jl b/test/convert/datetime.jl index a1b150e4..9b75c95a 100644 --- a/test/convert/datetime.jl +++ b/test/convert/datetime.jl @@ -135,3 +135,7 @@ r = RObject(d) @test ismissing(rcopy(r)[2]) @test rcopy(Array, r)[[1,3]] == d[[1,3]] @test ismissing(rcopy(Array, r)[2]) + +# microseconds on R side #396 +@test_logs((:warn, "Precision lost in conversion to DateTime"), + rcopy(R"as.POSIXct('2020-10-09 12:09:46.1234')")) From ed26c68dbc3c7f6b690905e33687a8a92da182d5 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Wed, 17 Jul 2024 19:11:07 +0200 Subject: [PATCH 2/6] version bump --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 487e620c..dc641176 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "RCall" uuid = "6f49c342-dc21-5d91-9882-a32aef131414" authors = ["Douglas Bates ", "Randy Lai ", "Simon Byrne "] -version = "0.14.2" +version = "0.14.3" [deps] CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597" From 6cd09a87c0ab9947f656a848c20437fbd0cd4411 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Wed, 17 Jul 2024 20:03:51 +0200 Subject: [PATCH 3/6] use debug instead of warn --- Project.toml | 4 +++- src/convert/datetime.jl | 2 +- test/convert/datetime.jl | 5 +++-- test/runtests.jl | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Project.toml b/Project.toml index dc641176..72a9f8a7 100644 --- a/Project.toml +++ b/Project.toml @@ -23,6 +23,7 @@ CategoricalArrays = "0.8, 0.9, 0.10" Conda = "1.4" DataFrames = "0.21, 0.22, 1.0" DataStructures = "0.5, 0.6, 0.7, 0.8, 0.9, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18" +Logging = "0, 1" Preferences = "1" Requires = "0.5.2, 1" StatsModels = "0.6, 0.7" @@ -33,10 +34,11 @@ julia = "1.6" AxisArrays = "39de3d68-74b9-583c-8d2d-e117c070f3a9" CondaPkg = "992eb4ea-22a4-4c89-a5bb-47a3300528ab" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Dates", "AxisArrays", "REPL", "Test", "Random", "CondaPkg", "Pkg"] +test = ["Dates", "AxisArrays", "REPL", "Test", "Random", "CondaPkg", "Pkg", "Logging"] diff --git a/src/convert/datetime.jl b/src/convert/datetime.jl index 21b89a3c..6658cdf2 100644 --- a/src/convert/datetime.jl +++ b/src/convert/datetime.jl @@ -9,7 +9,7 @@ rcopy(::Type{Date}, x::Float64) = Date(Dates.UTInstant(Dates.Day((isnan(x) ? 0 : function rcopy(::Type{DateTime}, x::Float64) ms = ((isnan(x) ? 0 : x) + 62135683200) * 1_000 if !isinteger(ms) - @warn "Precision lost in conversion to DateTime" + @debug "Precision lost in conversion to DateTime" end ms = round(Int, ms) return DateTime(Dates.UTInstant(Millisecond(ms))) diff --git a/test/convert/datetime.jl b/test/convert/datetime.jl index 9b75c95a..188041e9 100644 --- a/test/convert/datetime.jl +++ b/test/convert/datetime.jl @@ -137,5 +137,6 @@ r = RObject(d) @test ismissing(rcopy(Array, r)[2]) # microseconds on R side #396 -@test_logs((:warn, "Precision lost in conversion to DateTime"), - rcopy(R"as.POSIXct('2020-10-09 12:09:46.1234')")) +@test_logs((:debug, "Precision lost in conversion to DateTime"), + min_level=Logging.Debug, + rcopy(R"as.POSIXct('2020-10-09 12:09:46.1234')")) diff --git a/test/runtests.jl b/test/runtests.jl index 4174dd26..03720949 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,5 @@ using RCall +using Logging using Test using DataStructures: OrderedDict From 3766f29272bccab282041152d0e6eb94f6fbfbf4 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Wed, 17 Jul 2024 21:02:17 +0200 Subject: [PATCH 4/6] docs note --- docs/Project.toml | 1 + docs/src/conversions.md | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/docs/Project.toml b/docs/Project.toml index 21ec351f..010da86e 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -2,6 +2,7 @@ AxisArrays = "39de3d68-74b9-583c-8d2d-e117c070f3a9" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +RCall = "6f49c342-dc21-5d91-9882-a32aef131414" [compat] Documenter = "0.27" diff --git a/docs/src/conversions.md b/docs/src/conversions.md index cd0c0f6c..be3ad3db 100644 --- a/docs/src/conversions.md +++ b/docs/src/conversions.md @@ -74,6 +74,16 @@ r = robject(d) rcopy(r) ``` +Note that R's `POSIXct` supports higher precision than DateTime: + +```@example 1 +r = reval("as.POSIXct('2020-10-09 12:09:46.1234')") +``` + +```@example 1 +d = rcopy(r) +``` + ## DataFrames ```@example 1 From 24de11324aed09a1962def490e19de008fba0d18 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Thu, 18 Jul 2024 02:33:28 -0500 Subject: [PATCH 5/6] Update test/convert/datetime.jl Co-authored-by: Alex Arslan --- test/convert/datetime.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/convert/datetime.jl b/test/convert/datetime.jl index 188041e9..f036a5a7 100644 --- a/test/convert/datetime.jl +++ b/test/convert/datetime.jl @@ -140,3 +140,4 @@ r = RObject(d) @test_logs((:debug, "Precision lost in conversion to DateTime"), min_level=Logging.Debug, rcopy(R"as.POSIXct('2020-10-09 12:09:46.1234')")) +@test rcopy(R"as.POSIXct('2020-10-09 12:09:46.1234')") == DateTime("2020-10-09T12:09:46.123") From ad6314f7299b217efced24a873b199bef1b6ab51 Mon Sep 17 00:00:00 2001 From: Phillip Alday Date: Thu, 18 Jul 2024 09:55:49 +0200 Subject: [PATCH 6/6] fix test, add note --- docs/src/conversions.md | 4 ++++ src/convert/datetime.jl | 7 ++++--- test/convert/datetime.jl | 6 +++++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/src/conversions.md b/docs/src/conversions.md index be3ad3db..602ff4c1 100644 --- a/docs/src/conversions.md +++ b/docs/src/conversions.md @@ -84,6 +84,10 @@ r = reval("as.POSIXct('2020-10-09 12:09:46.1234')") d = rcopy(r) ``` +!!! note "Conversions to `DateTime` are given in UTC!" + `POSIXct` stores times internally as UTC with a timezone attribute. + The conversion to `DateTime` necessarily strips away timezone information, resulting in UTC values. + ## DataFrames ```@example 1 diff --git a/src/convert/datetime.jl b/src/convert/datetime.jl index 6658cdf2..52ce658e 100644 --- a/src/convert/datetime.jl +++ b/src/convert/datetime.jl @@ -8,11 +8,12 @@ rcopy(::Type{DateTime}, s::Ptr{RealSxp}) = rcopy(DateTime, s[1]) rcopy(::Type{Date}, x::Float64) = Date(Dates.UTInstant(Dates.Day((isnan(x) ? 0 : x) + 719163))) function rcopy(::Type{DateTime}, x::Float64) ms = ((isnan(x) ? 0 : x) + 62135683200) * 1_000 - if !isinteger(ms) + ms_int = round(Int, ms) + if ms != ms_int @debug "Precision lost in conversion to DateTime" end - ms = round(Int, ms) - return DateTime(Dates.UTInstant(Millisecond(ms))) + + return DateTime(Dates.UTInstant(Millisecond(ms_int))) end # implicit conversion `rcopy(d)`. diff --git a/test/convert/datetime.jl b/test/convert/datetime.jl index f036a5a7..18e224b9 100644 --- a/test/convert/datetime.jl +++ b/test/convert/datetime.jl @@ -136,8 +136,12 @@ r = RObject(d) @test rcopy(Array, r)[[1,3]] == d[[1,3]] @test ismissing(rcopy(Array, r)[2]) +# note that POSIXct stores times internaly as UTC, but assumes local +# timezone information +@test rcopy(R"as.POSIXct('2020-10-09 12:09:46', tz='UTC')") == DateTime("2020-10-09T12:09:46") + # microseconds on R side #396 @test_logs((:debug, "Precision lost in conversion to DateTime"), min_level=Logging.Debug, rcopy(R"as.POSIXct('2020-10-09 12:09:46.1234')")) -@test rcopy(R"as.POSIXct('2020-10-09 12:09:46.1234')") == DateTime("2020-10-09T12:09:46.123") +@test rcopy(R"as.POSIXct('2020-10-09 12:09:46.1234', tz='UTC')") == DateTime("2020-10-09T12:09:46.123")