use arrow::temporal_conversions::timestamp_ns_to_datetime;
use chrono::prelude::*;
use polars_core::prelude::*;
use crate::prelude::*;
#[test]
fn test_date_range() {
let start = NaiveDate::from_ymd_opt(2022, 1, 1)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap();
let end = NaiveDate::from_ymd_opt(2022, 4, 1)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap();
let dates = datetime_range_i64(
start.and_utc().timestamp_nanos_opt().unwrap(),
end.and_utc().timestamp_nanos_opt().unwrap(),
Duration::parse("1mo"),
ClosedWindow::Both,
TimeUnit::Nanoseconds,
None,
)
.unwrap();
let expected = [
NaiveDate::from_ymd_opt(2022, 1, 1).unwrap(),
NaiveDate::from_ymd_opt(2022, 2, 1).unwrap(),
NaiveDate::from_ymd_opt(2022, 3, 1).unwrap(),
NaiveDate::from_ymd_opt(2022, 4, 1).unwrap(),
]
.iter()
.map(|d| {
d.and_hms_opt(0, 0, 0)
.unwrap()
.and_utc()
.timestamp_nanos_opt()
.unwrap()
})
.collect::<Vec<_>>();
assert_eq!(dates, expected);
}
#[test]
fn test_feb_date_range() {
let start = NaiveDate::from_ymd_opt(2022, 2, 1)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap();
let end = NaiveDate::from_ymd_opt(2022, 3, 1)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap();
let dates = datetime_range_i64(
start.and_utc().timestamp_nanos_opt().unwrap(),
end.and_utc().timestamp_nanos_opt().unwrap(),
Duration::parse("1mo"),
ClosedWindow::Both,
TimeUnit::Nanoseconds,
None,
)
.unwrap();
let expected = [
NaiveDate::from_ymd_opt(2022, 2, 1).unwrap(),
NaiveDate::from_ymd_opt(2022, 3, 1).unwrap(),
]
.iter()
.map(|d| {
d.and_hms_opt(0, 0, 0)
.unwrap()
.and_utc()
.timestamp_nanos_opt()
.unwrap()
})
.collect::<Vec<_>>();
assert_eq!(dates, expected);
}
fn print_ns(ts: &[i64]) {
for ts in ts {
println!("{}", timestamp_ns_to_datetime(*ts));
}
}
fn take_groups_slice<'a>(groups: &'a GroupsSlice, idx: usize, ts: &'a [i64]) -> &'a [i64] {
let [first, len] = groups[idx];
let first = first as usize;
let len = len as usize;
&ts[first..first + len]
}
#[test]
fn test_groups_large_interval() {
let dates = &[
NaiveDate::from_ymd_opt(2020, 1, 1).unwrap(),
NaiveDate::from_ymd_opt(2020, 1, 11).unwrap(),
NaiveDate::from_ymd_opt(2020, 1, 12).unwrap(),
NaiveDate::from_ymd_opt(2020, 1, 13).unwrap(),
];
let ts = dates
.iter()
.map(|d| {
d.and_hms_opt(0, 0, 0)
.unwrap()
.and_utc()
.timestamp_nanos_opt()
.unwrap()
})
.collect::<Vec<_>>();
let dur = Duration::parse("2d");
let w = Window::new(Duration::parse("2d"), dur, Duration::from_nsecs(0));
let (groups, _, _) = group_by_windows(
w,
&ts,
ClosedWindow::Both,
TimeUnit::Nanoseconds,
&None,
false,
false,
Default::default(),
)
.unwrap();
assert_eq!(groups.len(), 4);
assert_eq!(groups[0], [0, 1]);
assert_eq!(groups[1], [1, 1]);
assert_eq!(groups[2], [1, 3]);
assert_eq!(groups[3], [3, 1]);
let (groups, _, _) = group_by_windows(
w,
&ts,
ClosedWindow::Left,
TimeUnit::Nanoseconds,
&None,
false,
false,
Default::default(),
)
.unwrap();
assert_eq!(groups.len(), 3);
assert_eq!(groups[2], [3, 1]);
let (groups, _, _) = group_by_windows(
w,
&ts,
ClosedWindow::Right,
TimeUnit::Nanoseconds,
&None,
false,
false,
Default::default(),
)
.unwrap();
assert_eq!(groups.len(), 3);
assert_eq!(groups[1], [1, 1]);
}
#[test]
fn test_offset() {
let t = NaiveDate::from_ymd_opt(2020, 1, 2)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap()
.and_utc()
.timestamp_nanos_opt()
.unwrap();
let w = Window::new(
Duration::parse("5m"),
Duration::parse("5m"),
Duration::parse("-2m"),
);
let b = w
.get_earliest_bounds_ns(t, ClosedWindow::Left, None)
.unwrap();
let start = NaiveDate::from_ymd_opt(2020, 1, 1)
.unwrap()
.and_hms_opt(23, 58, 0)
.unwrap()
.and_utc()
.timestamp_nanos_opt()
.unwrap();
assert_eq!(b.start, start);
}
#[test]
fn test_boundaries() {
let start = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap();
let stop = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(3, 0, 0)
.unwrap();
let ts = datetime_range_i64(
start.and_utc().timestamp_nanos_opt().unwrap(),
stop.and_utc().timestamp_nanos_opt().unwrap(),
Duration::parse("30m"),
ClosedWindow::Both,
TimeUnit::Nanoseconds,
None,
)
.unwrap();
let w = Window::new(
Duration::parse("1h"),
Duration::parse("1h"),
Duration::parse("0ns"),
);
let b = w
.get_earliest_bounds_ns(ts[0], ClosedWindow::Both, None)
.unwrap();
assert_eq!(b.start, start.and_utc().timestamp_nanos_opt().unwrap());
let (groups, lower, higher) = group_by_windows(
w,
&ts,
ClosedWindow::Both,
TimeUnit::Nanoseconds,
&None,
true,
true,
Default::default(),
)
.unwrap();
let g = take_groups_slice(&groups, 0, &ts);
let t0 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap();
let t1 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(0, 30, 0)
.unwrap();
let t2 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(1, 0, 0)
.unwrap();
assert_eq!(
g,
&[
t0.and_utc().timestamp_nanos_opt().unwrap(),
t1.and_utc().timestamp_nanos_opt().unwrap(),
t2.and_utc().timestamp_nanos_opt().unwrap()
]
);
let b_start = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap();
let b_end = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(1, 0, 0)
.unwrap();
assert_eq!(
&[lower[0], higher[0]],
&[
b_start.and_utc().timestamp_nanos_opt().unwrap(),
b_end.and_utc().timestamp_nanos_opt().unwrap()
]
);
let g = take_groups_slice(&groups, 1, &ts);
let t0 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(1, 0, 0)
.unwrap();
let t1 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(1, 30, 0)
.unwrap();
let t2 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(2, 0, 0)
.unwrap();
assert_eq!(
g,
&[
t0.and_utc().timestamp_nanos_opt().unwrap(),
t1.and_utc().timestamp_nanos_opt().unwrap(),
t2.and_utc().timestamp_nanos_opt().unwrap()
]
);
let b_start = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(1, 0, 0)
.unwrap();
let b_end = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(2, 0, 0)
.unwrap();
assert_eq!(
&[lower[1], higher[1]],
&[
b_start.and_utc().timestamp_nanos_opt().unwrap(),
b_end.and_utc().timestamp_nanos_opt().unwrap()
]
);
assert_eq!(groups[2], [4, 3]);
let (groups, _, _) = group_by_windows(
w,
&ts,
ClosedWindow::Left,
TimeUnit::Nanoseconds,
&None,
false,
false,
Default::default(),
)
.unwrap();
assert_eq!(groups[0], [0, 2]);
assert_eq!(groups[1], [2, 2]);
assert_eq!(groups[2], [4, 2]);
let (groups, _, _) = group_by_windows(
w,
&ts,
ClosedWindow::Right,
TimeUnit::Nanoseconds,
&None,
false,
false,
Default::default(),
)
.unwrap();
assert_eq!(groups[0], [0, 1]);
assert_eq!(groups[1], [1, 2]);
assert_eq!(groups[2], [3, 2]);
assert_eq!(groups[3], [5, 2]);
let (groups, _, _) = group_by_windows(
w,
&ts,
ClosedWindow::None,
TimeUnit::Nanoseconds,
&None,
false,
false,
Default::default(),
)
.unwrap();
assert_eq!(groups[0], [1, 1]);
assert_eq!(groups[1], [3, 1]);
assert_eq!(groups[2], [5, 1]);
}
#[test]
fn test_boundaries_2() {
let start = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap();
let stop = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(4, 0, 0)
.unwrap();
let ts = datetime_range_i64(
start.and_utc().timestamp_nanos_opt().unwrap(),
stop.and_utc().timestamp_nanos_opt().unwrap(),
Duration::parse("30m"),
ClosedWindow::Both,
TimeUnit::Nanoseconds,
None,
)
.unwrap();
print_ns(&ts);
let offset = Duration::parse("30m");
let every = Duration::parse("2h");
let w = Window::new(every, Duration::parse("1h"), offset);
let b = w
.get_earliest_bounds_ns(ts[0], ClosedWindow::Both, None)
.unwrap();
assert_eq!(
b.start,
start.and_utc().timestamp_nanos_opt().unwrap() + offset.duration_ns() - every.duration_ns()
);
let (groups, lower, higher) = group_by_windows(
w,
&ts,
ClosedWindow::Left,
TimeUnit::Nanoseconds,
&None,
true,
true,
Default::default(),
)
.unwrap();
let g = take_groups_slice(&groups, 0, &ts);
let t0 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(0, 30, 0)
.unwrap();
let t1 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(1, 0, 0)
.unwrap();
assert_eq!(
g,
&[
t0.and_utc().timestamp_nanos_opt().unwrap(),
t1.and_utc().timestamp_nanos_opt().unwrap()
]
);
let b_start = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(0, 30, 0)
.unwrap();
let b_end = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(1, 30, 0)
.unwrap();
assert_eq!(
&[lower[0], higher[0]],
&[
b_start.and_utc().timestamp_nanos_opt().unwrap(),
b_end.and_utc().timestamp_nanos_opt().unwrap()
]
);
let g = take_groups_slice(&groups, 1, &ts);
let t0 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(2, 30, 0)
.unwrap();
let t1 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(3, 0, 0)
.unwrap();
assert_eq!(
g,
&[
t0.and_utc().timestamp_nanos_opt().unwrap(),
t1.and_utc().timestamp_nanos_opt().unwrap()
]
);
let b_start = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(2, 30, 0)
.unwrap();
let b_end = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(3, 30, 0)
.unwrap();
assert_eq!(
&[lower[1], higher[1]],
&[
b_start.and_utc().timestamp_nanos_opt().unwrap(),
b_end.and_utc().timestamp_nanos_opt().unwrap()
]
);
}
#[test]
fn test_boundaries_ms() {
let start = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap();
let stop = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(3, 0, 0)
.unwrap();
let ts = datetime_range_i64(
start.and_utc().timestamp_millis(),
stop.and_utc().timestamp_millis(),
Duration::parse("30m"),
ClosedWindow::Both,
TimeUnit::Milliseconds,
None,
)
.unwrap();
let w = Window::new(
Duration::parse("1h"),
Duration::parse("1h"),
Duration::parse("0ns"),
);
let b = w
.get_earliest_bounds_ms(ts[0], ClosedWindow::Both, None)
.unwrap();
assert_eq!(b.start, start.and_utc().timestamp_millis());
let (groups, lower, higher) = group_by_windows(
w,
&ts,
ClosedWindow::Both,
TimeUnit::Milliseconds,
&None,
true,
true,
Default::default(),
)
.unwrap();
let g = take_groups_slice(&groups, 0, &ts);
let t0 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap();
let t1 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(0, 30, 0)
.unwrap();
let t2 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(1, 0, 0)
.unwrap();
assert_eq!(
g,
&[
t0.and_utc().timestamp_millis(),
t1.and_utc().timestamp_millis(),
t2.and_utc().timestamp_millis()
]
);
let b_start = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap();
let b_end = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(1, 0, 0)
.unwrap();
assert_eq!(
&[lower[0], higher[0]],
&[
b_start.and_utc().timestamp_millis(),
b_end.and_utc().timestamp_millis()
]
);
let g = take_groups_slice(&groups, 1, &ts);
let t0 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(1, 0, 0)
.unwrap();
let t1 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(1, 30, 0)
.unwrap();
let t2 = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(2, 0, 0)
.unwrap();
assert_eq!(
g,
&[
t0.and_utc().timestamp_millis(),
t1.and_utc().timestamp_millis(),
t2.and_utc().timestamp_millis()
]
);
let b_start = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(1, 0, 0)
.unwrap();
let b_end = NaiveDate::from_ymd_opt(2021, 12, 16)
.unwrap()
.and_hms_opt(2, 0, 0)
.unwrap();
assert_eq!(
&[lower[1], higher[1]],
&[
b_start.and_utc().timestamp_millis(),
b_end.and_utc().timestamp_millis()
]
);
assert_eq!(groups[2], [4, 3]);
let (groups, _, _) = group_by_windows(
w,
&ts,
ClosedWindow::Left,
TimeUnit::Milliseconds,
&None,
false,
false,
Default::default(),
)
.unwrap();
assert_eq!(groups[0], [0, 2]);
assert_eq!(groups[1], [2, 2]);
assert_eq!(groups[2], [4, 2]);
let (groups, _, _) = group_by_windows(
w,
&ts,
ClosedWindow::Right,
TimeUnit::Milliseconds,
&None,
false,
false,
Default::default(),
)
.unwrap();
assert_eq!(groups[0], [0, 1]);
assert_eq!(groups[1], [1, 2]);
assert_eq!(groups[2], [3, 2]);
assert_eq!(groups[3], [5, 2]);
let (groups, _, _) = group_by_windows(
w,
&ts,
ClosedWindow::None,
TimeUnit::Milliseconds,
&None,
false,
false,
Default::default(),
)
.unwrap();
assert_eq!(groups[0], [1, 1]);
assert_eq!(groups[1], [3, 1]);
assert_eq!(groups[2], [5, 1]);
}
#[test]
fn test_rolling_lookback() {
let start = NaiveDate::from_ymd_opt(1970, 1, 16)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap();
let end = NaiveDate::from_ymd_opt(1970, 1, 16)
.unwrap()
.and_hms_opt(4, 0, 0)
.unwrap();
let dates = datetime_range_i64(
start.and_utc().timestamp_millis(),
end.and_utc().timestamp_millis(),
Duration::parse("30m"),
ClosedWindow::Both,
TimeUnit::Milliseconds,
None,
)
.unwrap();
let groups = group_by_values(
Duration::parse("2h"),
Duration::parse("-2h"),
&dates,
ClosedWindow::Right,
TimeUnit::Milliseconds,
None,
)
.unwrap();
assert_eq!(dates.len(), groups.len());
assert_eq!(groups[0], [0, 1]);
assert_eq!(groups[1], [0, 2]);
assert_eq!(groups[2], [0, 3]);
assert_eq!(groups[3], [0, 4]);
assert_eq!(groups[4], [1, 4]);
assert_eq!(groups[5], [2, 4]);
assert_eq!(groups[6], [3, 4]);
assert_eq!(groups[7], [4, 4]);
assert_eq!(groups[8], [5, 4]);
let groups = group_by_values(
Duration::parse("2h"),
Duration::parse("-1h"),
&dates,
ClosedWindow::Right,
TimeUnit::Milliseconds,
None,
)
.unwrap();
assert_eq!(dates.len(), groups.len());
assert_eq!(groups[0], [0, 3]);
assert_eq!(groups[1], [0, 4]);
assert_eq!(groups[2], [1, 4]);
assert_eq!(groups[3], [2, 4]);
assert_eq!(groups[4], [3, 4]);
assert_eq!(groups[5], [4, 4]);
assert_eq!(groups[6], [5, 4]);
assert_eq!(groups[7], [6, 3]);
assert_eq!(groups[8], [7, 2]);
let groups = group_by_values(
Duration::parse("2h"),
Duration::parse("0h"),
&dates,
ClosedWindow::Right,
TimeUnit::Milliseconds,
None,
)
.unwrap();
assert_eq!(dates.len(), groups.len());
assert_eq!(groups[0], [1, 4]);
assert_eq!(groups[1], [2, 4]);
assert_eq!(groups[2], [3, 4]);
assert_eq!(groups[3], [4, 4]);
assert_eq!(groups[4], [5, 4]);
assert_eq!(groups[5], [6, 3]);
assert_eq!(groups[6], [7, 2]);
assert_eq!(groups[7], [8, 1]);
assert_eq!(groups[8], [9, 0]);
let period = Duration::parse("2h");
let tu = TimeUnit::Milliseconds;
for closed_window in [
ClosedWindow::Left,
ClosedWindow::Right,
ClosedWindow::Both,
ClosedWindow::None,
] {
let offset = Duration::parse("-2h");
let g0 = group_by_values_iter_lookbehind(
period,
offset,
&dates,
closed_window,
tu,
None,
0,
None,
)
.unwrap()
.collect::<PolarsResult<Vec<_>>>()
.unwrap();
let g1 = group_by_values_iter_partial_lookbehind(
period,
offset,
&dates,
closed_window,
tu,
None,
)
.collect::<PolarsResult<Vec<_>>>()
.unwrap();
assert_eq!(g0, g1);
}
}
#[test]
fn test_end_membership() {
let time = [
NaiveDate::from_ymd_opt(2021, 2, 1)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap()
.and_utc()
.timestamp_millis(),
NaiveDate::from_ymd_opt(2021, 5, 1)
.unwrap()
.and_hms_opt(0, 0, 0)
.unwrap()
.and_utc()
.timestamp_millis(),
];
let window = Window::new(
Duration::parse("1mo"),
Duration::parse("2mo"),
Duration::parse("-2mo"),
);
let (groups, _, _) = group_by_windows(
window,
&time,
ClosedWindow::Left,
TimeUnit::Milliseconds,
&None,
false,
false,
Default::default(),
)
.unwrap();
assert_eq!(groups[0], [0, 1]);
assert_eq!(groups[1], [0, 1]);
assert_eq!(groups[2], [1, 1]);
assert_eq!(groups[3], [1, 1]);
}
#[test]
fn test_group_by_windows_membership_2791() {
let dates = [0, 0, 2, 2];
let window = Window::new(
Duration::parse("1ms"),
Duration::parse("1ms"),
Duration::parse("0ns"),
);
let (groups, _, _) = group_by_windows(
window,
&dates,
ClosedWindow::Left,
TimeUnit::Milliseconds,
&None,
false,
false,
Default::default(),
)
.unwrap();
assert_eq!(groups[0], [0, 2]);
assert_eq!(groups[1], [2, 2]);
}
#[test]
fn test_group_by_windows_duplicates_2931() {
let dates = [0, 3, 3, 5, 5];
let window = Window::new(
Duration::parse("1ms"),
Duration::parse("1ms"),
Duration::parse("0ns"),
);
let (groups, _, _) = group_by_windows(
window,
&dates,
ClosedWindow::Left,
TimeUnit::Milliseconds,
&None,
false,
false,
Default::default(),
)
.unwrap();
assert_eq!(groups, [[0, 1], [1, 2], [3, 2]]);
}
#[test]
fn test_group_by_windows_offsets_3776() {
let dates = &[
NaiveDate::from_ymd_opt(2020, 12, 1).unwrap(),
NaiveDate::from_ymd_opt(2021, 2, 1).unwrap(),
NaiveDate::from_ymd_opt(2021, 5, 1).unwrap(),
];
let ts = dates
.iter()
.map(|d| {
d.and_hms_opt(0, 0, 0)
.unwrap()
.and_utc()
.timestamp_nanos_opt()
.unwrap()
})
.collect::<Vec<_>>();
let window = Window::new(
Duration::parse("2d"),
Duration::parse("2d"),
Duration::parse("-2d"),
);
let (groups, _, _) = group_by_windows(
window,
&ts,
ClosedWindow::Right,
TimeUnit::Nanoseconds,
&None,
false,
false,
Default::default(),
)
.unwrap();
assert_eq!(groups, [[0, 1], [1, 1], [2, 1]]);
}