Skip to content

Commit af0c393

Browse files
committed
Add datetime support
1 parent 34af6e2 commit af0c393

File tree

2 files changed

+93
-15
lines changed

2 files changed

+93
-15
lines changed

crates/ark/src/data_explorer/summary_stats.rs

+68-15
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ fn summary_stats_(
3939
ColumnDisplayType::String => Ok(summary_stats_string(column)?.into()),
4040
ColumnDisplayType::Boolean => Ok(summary_stats_boolean(column)?.into()),
4141
ColumnDisplayType::Date => Ok(summary_stats_date(column)?.into()),
42+
ColumnDisplayType::Datetime => Ok(summary_stats_datetime(column)?.into()),
4243
_ => Err(anyhow::anyhow!("Unkown type")),
4344
}
4445
}
@@ -79,21 +80,6 @@ fn summary_stats_boolean(column: SEXP) -> anyhow::Result<SummaryStatsBoolean> {
7980
}
8081

8182
fn summary_stats_date(column: SEXP) -> anyhow::Result<SummaryStatsDate> {
82-
let robj_to_string = |robj: &RObject| -> String {
83-
let string: Option<String> = unwrap!(robj.clone().try_into(), Err(e) => {
84-
log::error!("Date stats: Error converting RObject to String: {e}");
85-
None
86-
});
87-
88-
match string {
89-
Some(s) => s,
90-
None => {
91-
log::warn!("Date stats: Expected a string, got NA");
92-
"NA".to_string()
93-
},
94-
}
95-
};
96-
9783
let r_stats: HashMap<String, RObject> =
9884
call_summary_fn("summary_stats_date", column)?.try_into()?;
9985

@@ -108,6 +94,44 @@ fn summary_stats_date(column: SEXP) -> anyhow::Result<SummaryStatsDate> {
10894
}))
10995
}
11096

97+
fn summary_stats_datetime(column: SEXP) -> anyhow::Result<SummaryStatsDatetime> {
98+
// use the same implementationas the date
99+
let r_stats: HashMap<String, RObject> =
100+
call_summary_fn("summary_stats_date", column)?.try_into()?;
101+
102+
let num_unique: i32 = r_stats["num_unique"].clone().try_into()?;
103+
let timezone: Option<String> = RFunction::from("summary_stats_get_timezone")
104+
.add(column)
105+
.call_in(ARK_ENVS.positron_ns)?
106+
.try_into()?;
107+
108+
Ok(SummaryStatsDatetime(
109+
data_explorer_comm::SummaryStatsDatetime {
110+
min_date: robj_to_string(&r_stats["min_date"]),
111+
mean_date: robj_to_string(&r_stats["mean_date"]),
112+
median_date: robj_to_string(&r_stats["median_date"]),
113+
max_date: robj_to_string(&r_stats["max_date"]),
114+
num_unique: num_unique as i64,
115+
timezone,
116+
},
117+
))
118+
}
119+
120+
fn robj_to_string(robj: &RObject) -> String {
121+
let string: Option<String> = unwrap!(robj.clone().try_into(), Err(e) => {
122+
log::error!("Date stats: Error converting RObject to String: {e}");
123+
None
124+
});
125+
126+
match string {
127+
Some(s) => s,
128+
None => {
129+
log::warn!("Date stats: Expected a string, got NA");
130+
"NA".to_string()
131+
},
132+
}
133+
}
134+
111135
fn call_summary_fn(function: &str, column: SEXP) -> anyhow::Result<RObject> {
112136
Ok(RFunction::from(function)
113137
.add(column)
@@ -153,6 +177,12 @@ impl_summary_stats_conversion!(
153177
data_explorer_comm::SummaryStatsDate,
154178
ColumnDisplayType::Date
155179
);
180+
impl_summary_stats_conversion!(
181+
datetime_stats,
182+
SummaryStatsDatetime,
183+
data_explorer_comm::SummaryStatsDatetime,
184+
ColumnDisplayType::Datetime
185+
);
156186

157187
fn empty_column_summary_stats() -> data_explorer_comm::ColumnSummaryStats {
158188
data_explorer_comm::ColumnSummaryStats {
@@ -243,4 +273,27 @@ mod tests {
243273
assert_eq!(stats, expected);
244274
})
245275
}
276+
277+
#[test]
278+
fn test_datetime_summary() {
279+
r_test(|| {
280+
let column = r_parse_eval0(
281+
"as.POSIXct(c('2015-07-24 23:15:07', '2015-07-24 23:15:07', NA), tz = 'Japan')",
282+
R_ENVS.global,
283+
)
284+
.unwrap();
285+
let stats = summary_stats_(column.sexp, ColumnDisplayType::Datetime).unwrap();
286+
let expected: ColumnSummaryStats =
287+
SummaryStatsDatetime(data_explorer_comm::SummaryStatsDatetime {
288+
num_unique: 2,
289+
min_date: "2015-07-24 23:15:07".to_string(),
290+
mean_date: "2015-07-24 23:15:07".to_string(),
291+
median_date: "2015-07-24 23:15:07".to_string(),
292+
max_date: "2015-07-24 23:15:07".to_string(),
293+
timezone: Some("Japan".to_string()),
294+
})
295+
.into();
296+
assert_eq!(stats, expected);
297+
})
298+
}
246299
}

crates/ark/src/modules/positron/r_data_explorer.R

+25
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,31 @@ summary_stats_date <- function(col) {
7272
)
7373
}
7474

75+
summary_stats_get_timezone <- function(x) {
76+
# this is the implementation in lubridate for POSIXt objects
77+
tz <- function (x) {
78+
tzone <- attr(x, "tzone")
79+
if (is.null(tzone)) {
80+
""
81+
}
82+
else {
83+
tzone[[1]]
84+
}
85+
}
86+
87+
if (inherits(x, "POSIXt")) {
88+
timezone <- tz(x)
89+
# when the timezone is reported as "", it will actually be formatted
90+
# using the system timzeone, so we report it instead.
91+
if (timezone == "") {
92+
timezone <- Sys.timezone()
93+
}
94+
timezone
95+
} else {
96+
stop("Timezone can't be obtained for this object type")
97+
}
98+
}
99+
75100
col_filter_indices <- function(col, idx = NULL) {
76101
if (!is.null(idx)) {
77102
col <- col[idx]

0 commit comments

Comments
 (0)