1use std::fmt::{self, Debug, Display, Formatter};
46use std::sync::OnceLock;
47
48use crate::enums::error::MinarrowError;
49use crate::enums::shape_dim::ShapeDim;
50use crate::traits::concatenate::Concatenate;
51use crate::traits::print::MAX_PREVIEW;
52use crate::traits::shape::Shape;
53use crate::{Array, ArrayV, BitmaskV, MaskedArray, TemporalArray};
54
55#[derive(Clone, PartialEq)]
80pub struct TemporalArrayV {
81 pub array: TemporalArray,
85 pub offset: usize,
87 len: usize,
89 null_count: OnceLock<usize>,
94}
95
96impl TemporalArrayV {
97 pub fn new(array: TemporalArray, offset: usize, len: usize) -> Self {
99 assert!(
100 offset + len <= array.len(),
101 "TemporalArrayView: window out of bounds (offset + len = {}, array.len = {})",
102 offset + len,
103 array.len()
104 );
105 Self {
106 array,
107 offset,
108 len,
109 null_count: OnceLock::new(),
110 }
111 }
112
113 pub fn new_nc(
115 array: TemporalArray,
116 offset: usize,
117 len: usize,
118 null_count: usize,
119 ) -> Self {
120 assert!(
121 offset + len <= array.len(),
122 "TemporalArrayView: window out of bounds (offset + len = {}, array.len = {})",
123 offset + len,
124 array.len()
125 );
126 let lock = OnceLock::new();
127 let _ = lock.set(null_count); Self {
129 array,
130 offset,
131 len,
132 null_count: lock,
133 }
134 }
135
136 #[inline]
138 pub fn is_empty(&self) -> bool {
139 self.len == 0
140 }
141
142 #[inline]
144 pub fn get_i64(&self, i: usize) -> Option<i64> {
145 if i >= self.len {
146 return None;
147 }
148 let phys_idx = self.offset + i;
149 match &self.array {
150 TemporalArray::Datetime32(arr) => arr.get(phys_idx).map(|v| v as i64),
151 TemporalArray::Datetime64(arr) => arr.get(phys_idx),
152 TemporalArray::Null => None,
153 }
154 }
155
156 #[inline]
158 pub fn get_i32(&self, i: usize) -> Option<i32> {
159 if i >= self.len {
160 return None;
161 }
162 let phys_idx = self.offset + i;
163 match &self.array {
164 TemporalArray::Datetime32(arr) => arr.get(phys_idx),
165 TemporalArray::Datetime64(_) => None,
166 TemporalArray::Null => None,
167 }
168 }
169
170 #[inline]
172 pub fn slice(&self, offset: usize, len: usize) -> Self {
173 assert!(
174 offset + len <= self.len,
175 "TemporalArrayView::slice: out of bounds"
176 );
177 Self {
178 array: self.array.clone(),
179 offset: self.offset + offset,
180 len,
181 null_count: OnceLock::new(),
182 }
183 }
184
185 #[inline]
187 pub fn len(&self) -> usize {
188 self.len
189 }
190
191 #[inline]
195 pub fn inner_array(&self) -> Array {
196 Array::TemporalArray(self.array.clone()) }
198
199 pub fn to_temporal_array(&self) -> TemporalArray {
205 if self.offset == 0 && self.len == self.array.len() {
206 return self.array.clone();
207 }
208 self.inner_array().slice_clone(self.offset, self.len).dt()
209 }
210
211 #[inline]
213 pub fn end(&self) -> usize {
214 self.offset + self.len
215 }
216
217 #[inline]
219 pub fn as_tuple(&self) -> (TemporalArray, usize, usize) {
220 (self.array.clone(), self.offset, self.len)
221 }
222
223 #[inline]
225 pub fn null_count(&self) -> usize {
226 *self
227 .null_count
228 .get_or_init(|| match self.array.null_mask() {
229 Some(mask) => mask.view(self.offset, self.len).count_zeros(),
230 None => 0,
231 })
232 }
233
234 #[inline]
240 pub fn has_nulls(&self) -> bool {
241 self.null_count() > 0
242 }
243
244 #[inline]
246 pub fn null_mask_view(&self) -> Option<BitmaskV> {
247 self.array
248 .null_mask()
249 .map(|mask| mask.view(self.offset, self.len))
250 }
251
252 #[inline]
254 pub fn set_null_count(&self, count: usize) -> Result<(), usize> {
255 self.null_count.set(count).map_err(|_| count)
256 }
257}
258
259impl From<TemporalArray> for TemporalArrayV {
260 fn from(array: TemporalArray) -> Self {
261 let len = array.len();
262 TemporalArrayV {
263 array,
264 offset: 0,
265 len,
266 null_count: OnceLock::new(),
267 }
268 }
269}
270
271impl From<Array> for TemporalArrayV {
272 fn from(array: Array) -> Self {
273 match array {
274 Array::TemporalArray(arr) => {
275 let len = arr.len();
276 TemporalArrayV {
277 array: arr,
278 offset: 0,
279 len,
280 null_count: OnceLock::new(),
281 }
282 }
283 _ => panic!("Array is not a TemporalArray"),
284 }
285 }
286}
287
288impl From<ArrayV> for TemporalArrayV {
289 fn from(view: ArrayV) -> Self {
291 let (array, offset, len) = view.as_tuple();
292 match array {
293 Array::TemporalArray(inner) => Self {
294 array: inner,
295 offset,
296 len,
297 null_count: OnceLock::new(),
298 },
299 _ => panic!("From<ArrayView>: expected TemporalArray variant"),
300 }
301 }
302}
303
304impl Debug for TemporalArrayV {
305 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
306 f.debug_struct("TemporalArrayView")
307 .field("offset", &self.offset)
308 .field("len", &self.len)
309 .field("array", &self.array)
310 .field("cached_null_count", &self.null_count.get())
311 .finish()
312 }
313}
314
315impl Display for TemporalArrayV {
316 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
317 let dtype = match &self.array {
318 TemporalArray::Datetime32(_) => "Datetime32<i32>",
319 TemporalArray::Datetime64(_) => "Datetime64<i64>",
320 TemporalArray::Null => "Null",
321 };
322
323 writeln!(
324 f,
325 "TemporalArrayView<{dtype}> [{} values] (offset: {}, nulls: {})",
326 self.len(),
327 self.offset,
328 self.null_count()
329 )?;
330
331 let max = self.len().min(MAX_PREVIEW);
332
333 #[cfg(feature = "datetime_ops")]
334 {
335 use time::OffsetDateTime;
336
337 use crate::TimeUnit;
338 let unit = match &self.array {
339 TemporalArray::Datetime32(arr) => &arr.time_unit,
340 TemporalArray::Datetime64(arr) => &arr.time_unit,
341 TemporalArray::Null => &TimeUnit::Milliseconds,
342 };
343 for i in 0..max {
344 match self.get_i64(i) {
345 Some(val) => match unit {
346 TimeUnit::Seconds => match OffsetDateTime::from_unix_timestamp(val) {
347 Ok(dt) => writeln!(f, " {dt}")?,
348 Err(_) => writeln!(f, " {val}s")?,
349 },
350 TimeUnit::Milliseconds => {
351 match OffsetDateTime::from_unix_timestamp_nanos(
352 (val as i128) * 1_000_000,
353 ) {
354 Ok(dt) => writeln!(f, " {dt}")?,
355 Err(_) => writeln!(f, " {val}ms")?,
356 }
357 }
358 TimeUnit::Microseconds => {
359 match OffsetDateTime::from_unix_timestamp_nanos((val as i128) * 1_000) {
360 Ok(dt) => writeln!(f, " {dt}")?,
361 Err(_) => writeln!(f, " {val}µs")?,
362 }
363 }
364 TimeUnit::Nanoseconds => {
365 match OffsetDateTime::from_unix_timestamp_nanos(val as i128) {
366 Ok(dt) => writeln!(f, " {dt}")?,
367 Err(_) => writeln!(f, " {val}ns")?,
368 }
369 }
370 TimeUnit::Days => {
371 use crate::structs::variants::datetime::UNIX_EPOCH_JULIAN_DAY;
372
373 let days = val;
374 match time::Date::from_julian_day((days + UNIX_EPOCH_JULIAN_DAY) as i32)
375 {
376 Ok(d) => writeln!(f, " {d}")?,
377 Err(_) => writeln!(f, " {days}d")?,
378 }
379 }
380 },
381 None => writeln!(f, " null")?,
382 }
383 }
384 }
385
386 #[cfg(not(feature = "datetime_ops"))]
387 {
388 use crate::TimeUnit;
389
390 let unit = match &self.array {
391 TemporalArray::Datetime32(arr) => &arr.time_unit,
392 TemporalArray::Datetime64(arr) => &arr.time_unit,
393 TemporalArray::Null => &TimeUnit::Milliseconds,
394 };
395 let suffix = match unit {
396 TimeUnit::Seconds => "s",
397 TimeUnit::Milliseconds => "ms",
398 TimeUnit::Microseconds => "µs",
399 TimeUnit::Nanoseconds => "ns",
400 TimeUnit::Days => "d",
401 };
402 for i in 0..max {
403 match self.get_i64(i) {
404 Some(val) => writeln!(f, " {}{}", val, suffix)?,
405 None => writeln!(f, " null")?,
406 }
407 }
408 }
409
410 if self.len() > MAX_PREVIEW {
411 writeln!(f, " ... ({} more)", self.len() - MAX_PREVIEW)?;
412 }
413
414 Ok(())
415 }
416}
417
418#[cfg(feature = "datetime_ops")]
419use crate::DatetimeOps;
420
421#[cfg(feature = "datetime_ops")]
422use crate::enums::time_units::TimeUnit;
423
424#[cfg(feature = "datetime_ops")]
425use time::Duration;
426
427#[cfg(feature = "datetime_ops")]
428use crate::structs::variants::{boolean::BooleanArray, integer::IntegerArray};
429
430#[cfg(feature = "datetime_ops")]
431use crate::DatetimeArray;
432
433#[cfg(feature = "datetime_ops")]
434use num_traits::{FromPrimitive, ToPrimitive};
435
436#[cfg(feature = "datetime_ops")]
437use std::sync::Arc;
438
439#[cfg(feature = "datetime_ops")]
444macro_rules! windowed_component {
445 ($self:expr, $extract:expr) => {{
446 let offset = $self.offset;
447 let len = $self.len();
448 match &$self.array {
449 TemporalArray::Datetime32(arr) => {
450 windowed_component_inner!(arr, offset, len, $extract)
451 }
452 TemporalArray::Datetime64(arr) => {
453 windowed_component_inner!(arr, offset, len, $extract)
454 }
455 TemporalArray::Null => IntegerArray::default(),
456 }
457 }};
458}
459
460#[cfg(feature = "datetime_ops")]
461macro_rules! windowed_component_inner {
462 ($arr:expr, $offset:expr, $len:expr, $extract:expr) => {{
463 let mut result = IntegerArray::with_capacity($len, $arr.is_nullable());
464 for i in $offset..$offset + $len {
465 if $arr.is_null(i) {
466 result.push_null();
467 } else if let Some(val_i64) = $arr.data[i].to_i64() {
468 if let Some(dt) = DatetimeArray::<i64>::i64_to_datetime(val_i64, $arr.time_unit) {
469 #[allow(clippy::redundant_closure_call)]
470 result.push(($extract)(&dt));
471 } else {
472 result.push_null();
473 }
474 } else {
475 result.push_null();
476 }
477 }
478 result
479 }};
480}
481
482#[cfg(feature = "datetime_ops")]
484macro_rules! windowed_self_op {
485 ($self:expr, $method:ident $(, $arg:expr)*) => {{
486 let offset = $self.offset;
487 let len = $self.len();
488 match &$self.array {
489 TemporalArray::Datetime32(arr) => {
490 let sliced = arr.slice_clone(offset, len);
491 let result = sliced.$method($($arg),*)?;
492 Ok(TemporalArrayV::from(TemporalArray::Datetime32(Arc::new(result))))
493 }
494 TemporalArray::Datetime64(arr) => {
495 let sliced = arr.slice_clone(offset, len);
496 let result = sliced.$method($($arg),*)?;
497 Ok(TemporalArrayV::from(TemporalArray::Datetime64(Arc::new(result))))
498 }
499 TemporalArray::Null => Err(MinarrowError::NullError { message: None }),
500 }
501 }};
502}
503
504#[cfg(feature = "datetime_ops")]
506macro_rules! windowed_self_op_infallible {
507 ($self:expr, $method:ident $(, $arg:expr)*) => {{
508 let offset = $self.offset;
509 let len = $self.len();
510 match &$self.array {
511 TemporalArray::Datetime32(arr) => {
512 let sliced = arr.slice_clone(offset, len);
513 let result = sliced.$method($($arg),*);
514 TemporalArrayV::from(TemporalArray::Datetime32(Arc::new(result)))
515 }
516 TemporalArray::Datetime64(arr) => {
517 let sliced = arr.slice_clone(offset, len);
518 let result = sliced.$method($($arg),*);
519 TemporalArrayV::from(TemporalArray::Datetime64(Arc::new(result)))
520 }
521 TemporalArray::Null => TemporalArrayV::from(TemporalArray::Null),
522 }
523 }};
524}
525
526#[cfg(feature = "datetime_ops")]
527impl DatetimeOps for TemporalArrayV {
528 fn year(&self) -> IntegerArray<i32> {
531 windowed_component!(self, |dt: &time::OffsetDateTime| dt.year())
532 }
533
534 fn month(&self) -> IntegerArray<i32> {
535 windowed_component!(self, |dt: &time::OffsetDateTime| dt.month() as i32)
536 }
537
538 fn day(&self) -> IntegerArray<i32> {
539 windowed_component!(self, |dt: &time::OffsetDateTime| dt.day() as i32)
540 }
541
542 fn hour(&self) -> IntegerArray<i32> {
543 windowed_component!(self, |dt: &time::OffsetDateTime| dt.hour() as i32)
544 }
545
546 fn minute(&self) -> IntegerArray<i32> {
547 windowed_component!(self, |dt: &time::OffsetDateTime| dt.minute() as i32)
548 }
549
550 fn second(&self) -> IntegerArray<i32> {
551 windowed_component!(self, |dt: &time::OffsetDateTime| dt.second() as i32)
552 }
553
554 fn weekday(&self) -> IntegerArray<i32> {
555 windowed_component!(
556 self,
557 |dt: &time::OffsetDateTime| dt.weekday().number_from_sunday() as i32
558 )
559 }
560
561 fn day_of_year(&self) -> IntegerArray<i32> {
562 windowed_component!(self, |dt: &time::OffsetDateTime| dt.ordinal() as i32)
563 }
564
565 fn iso_week(&self) -> IntegerArray<i32> {
566 windowed_component!(self, |dt: &time::OffsetDateTime| dt.iso_week() as i32)
567 }
568
569 fn quarter(&self) -> IntegerArray<i32> {
570 windowed_component!(self, |dt: &time::OffsetDateTime| {
571 let month = dt.month() as i32;
572 ((month - 1) / 3) + 1
573 })
574 }
575
576 fn week_of_year(&self) -> IntegerArray<i32> {
577 windowed_component!(self, |dt: &time::OffsetDateTime| {
578 let day_of_year = dt.ordinal() as i32;
579 let weekday = dt.weekday().number_from_sunday() as i32;
580 (day_of_year + 7 - weekday) / 7
581 })
582 }
583
584 fn is_leap_year(&self) -> BooleanArray<()> {
585 let offset = self.offset;
586 let len = self.len();
587 match &self.array {
588 TemporalArray::Datetime32(arr) => is_leap_year_windowed(arr, offset, len),
589 TemporalArray::Datetime64(arr) => is_leap_year_windowed(arr, offset, len),
590 TemporalArray::Null => BooleanArray::default(),
591 }
592 }
593
594 fn add_duration(&self, duration: Duration) -> Result<Self, MinarrowError> {
597 windowed_self_op!(self, add_duration, duration)
598 }
599
600 fn sub_duration(&self, duration: Duration) -> Result<Self, MinarrowError> {
601 windowed_self_op!(self, sub_duration, duration)
602 }
603
604 fn add_days(&self, days: i64) -> Result<Self, MinarrowError> {
605 windowed_self_op!(self, add_days, days)
606 }
607
608 fn add_months(&self, months: i32) -> Result<Self, MinarrowError> {
609 windowed_self_op!(self, add_months, months)
610 }
611
612 fn add_years(&self, years: i32) -> Result<Self, MinarrowError> {
613 windowed_self_op!(self, add_years, years)
614 }
615
616 fn diff(&self, other: &Self, unit: TimeUnit) -> Result<IntegerArray<i64>, MinarrowError> {
619 let self_arr = self.to_temporal_array();
620 let other_arr = other.to_temporal_array();
621 self_arr.diff(&other_arr, unit)
622 }
623
624 fn abs_diff(&self, other: &Self, unit: TimeUnit) -> Result<IntegerArray<i64>, MinarrowError> {
625 let self_arr = self.to_temporal_array();
626 let other_arr = other.to_temporal_array();
627 self_arr.abs_diff(&other_arr, unit)
628 }
629
630 fn is_before(&self, other: &Self) -> Result<BooleanArray<()>, MinarrowError> {
631 let self_arr = self.to_temporal_array();
632 let other_arr = other.to_temporal_array();
633 self_arr.is_before(&other_arr)
634 }
635
636 fn is_after(&self, other: &Self) -> Result<BooleanArray<()>, MinarrowError> {
637 let self_arr = self.to_temporal_array();
638 let other_arr = other.to_temporal_array();
639 self_arr.is_after(&other_arr)
640 }
641
642 fn between(&self, start: &Self, end: &Self) -> Result<BooleanArray<()>, MinarrowError> {
643 let self_arr = self.to_temporal_array();
644 let start_arr = start.to_temporal_array();
645 let end_arr = end.to_temporal_array();
646 self_arr.between(&start_arr, &end_arr)
647 }
648
649 fn truncate(&self, unit: &str) -> Result<Self, MinarrowError> {
652 windowed_self_op!(self, truncate, unit)
653 }
654
655 fn us(&self) -> Self {
656 windowed_self_op_infallible!(self, us)
657 }
658
659 fn ms(&self) -> Self {
660 windowed_self_op_infallible!(self, ms)
661 }
662
663 fn sec(&self) -> Self {
664 windowed_self_op_infallible!(self, sec)
665 }
666
667 fn min(&self) -> Self {
668 windowed_self_op_infallible!(self, min)
669 }
670
671 fn hr(&self) -> Self {
672 windowed_self_op_infallible!(self, hr)
673 }
674
675 fn week(&self) -> Self {
676 windowed_self_op_infallible!(self, week)
677 }
678
679 fn cast_time_unit(&self, new_unit: TimeUnit) -> Result<Self, MinarrowError> {
682 windowed_self_op!(self, cast_time_unit, new_unit)
683 }
684}
685
686#[cfg(feature = "datetime_ops")]
688fn is_leap_year_windowed<T: crate::Integer + FromPrimitive>(
689 arr: &DatetimeArray<T>,
690 offset: usize,
691 len: usize,
692) -> BooleanArray<()> {
693 let mut result = BooleanArray::with_capacity(len, arr.is_nullable());
694 for i in offset..offset + len {
695 if arr.is_null(i) {
696 result.push_null();
697 } else if let Some(val_i64) = arr.data[i].to_i64() {
698 if let Some(dt) = DatetimeArray::<i64>::i64_to_datetime(val_i64, arr.time_unit) {
699 result.push(time::util::is_leap_year(dt.year()));
700 } else {
701 result.push_null();
702 }
703 } else {
704 result.push_null();
705 }
706 }
707 result
708}
709
710impl Shape for TemporalArrayV {
711 fn shape(&self) -> ShapeDim {
712 ShapeDim::Rank1(self.len())
713 }
714}
715
716impl Concatenate for TemporalArrayV {
717 fn concat(self, other: Self) -> Result<Self, MinarrowError> {
724 let self_array = self.to_temporal_array();
726 let other_array = other.to_temporal_array();
727
728 let concatenated = self_array.concat(other_array)?;
730
731 Ok(TemporalArrayV::from(concatenated))
733 }
734}
735
736#[cfg(test)]
737mod tests {
738 use std::sync::Arc;
739
740 use super::*;
741 use crate::{Bitmask, DatetimeArray, TemporalArray};
742
743 #[test]
744 fn test_temporal_array_view_basic_indexing_and_slice() {
745 let arr = DatetimeArray::<i64>::from_slice(&[10_000, 20_000, 30_000, 40_000], None);
746 let temporal = TemporalArray::Datetime64(Arc::new(arr));
747 let view = TemporalArrayV::new(temporal, 1, 2);
748
749 assert_eq!(view.len(), 2);
750 assert_eq!(view.offset, 1);
751 assert_eq!(view.get_i64(0), Some(20_000));
752 assert_eq!(view.get_i64(1), Some(30_000));
753 assert_eq!(view.get_i64(2), None);
754
755 let sub = view.slice(1, 1);
756 assert_eq!(sub.len(), 1);
757 assert_eq!(sub.get_i64(0), Some(30_000));
758 assert_eq!(sub.get_i64(1), None);
759 }
760
761 #[test]
762 fn test_temporal_array_view_null_count_and_cache() {
763 let mut arr = DatetimeArray::<i64>::from_slice(&[1, 2, 3, 4], None);
764 let mut mask = Bitmask::new_set_all(4, true);
765 mask.set(2, false);
766 arr.null_mask = Some(mask);
767
768 let temporal = TemporalArray::Datetime64(Arc::new(arr));
769 let view = TemporalArrayV::new(temporal, 0, 4);
770 assert_eq!(view.null_count(), 1, "Null count should detect one null");
771 assert_eq!(view.null_count(), 1);
772
773 let view2 = view.slice(0, 2);
774 assert_eq!(view2.null_count(), 0);
775 let view3 = view.slice(2, 2);
776 assert_eq!(view3.null_count(), 1);
777 }
778
779 #[test]
780 fn test_temporal_array_view_with_supplied_null_count() {
781 let arr = DatetimeArray::<i64>::from_slice(&[5, 6], None);
782 let temporal = TemporalArray::Datetime64(Arc::new(arr));
783 let view = TemporalArrayV::new_nc(temporal, 0, 2, 99);
784 assert_eq!(view.null_count(), 99);
785 assert!(view.set_null_count(101).is_err());
787 assert_eq!(view.null_count(), 99);
789 }
790
791 #[test]
792 fn test_temporal_array_view_to_temporal_array_and_as_tuple() {
793 let arr = DatetimeArray::<i64>::from_slice(&(10..20).collect::<Vec<_>>(), None);
794 let temporal = TemporalArray::Datetime64(Arc::new(arr));
795 let view = TemporalArrayV::new(temporal.clone(), 4, 3);
796 let arr2 = view.to_temporal_array();
797 if let TemporalArray::Datetime64(a2) = arr2 {
798 assert_eq!(&a2.data[..], &[14, 15, 16]);
799 } else {
800 panic!("Unexpected variant");
801 }
802 let tup = view.as_tuple();
803 assert_eq!(&tup.0, &temporal);
804 assert_eq!(tup.1, 4);
805 assert_eq!(tup.2, 3);
806 }
807
808 #[test]
809 fn test_temporal_array_view_null_mask_view() {
810 let mut arr = DatetimeArray::<i64>::from_slice(&[2, 4, 6], None);
811 let mut mask = Bitmask::new_set_all(3, true);
812 mask.set(0, false);
813 arr.null_mask = Some(mask);
814
815 let temporal = TemporalArray::Datetime64(Arc::new(arr));
816 let view = TemporalArrayV::new(temporal, 1, 2);
817 let mask_view = view.null_mask_view().expect("Should have mask");
818 assert_eq!(mask_view.len(), 2);
819 assert!(mask_view.get(0));
820 assert!(mask_view.get(1));
821 }
822
823 #[test]
824 fn test_temporal_array_view_from_temporal_array_and_array() {
825 let arr = DatetimeArray::<i64>::from_slice(&[1, 2], None);
826 let temporal = TemporalArray::Datetime64(Arc::new(arr));
827 let view_from_temporal = TemporalArrayV::from(temporal.clone());
828 assert_eq!(view_from_temporal.len(), 2);
829 assert_eq!(view_from_temporal.get_i64(0), Some(1));
830
831 let array = Array::TemporalArray(temporal);
832 let view_from_array = TemporalArrayV::from(array);
833 assert_eq!(view_from_array.len(), 2);
834 assert_eq!(view_from_array.get_i64(1), Some(2));
835 }
836
837 #[test]
838 #[should_panic(expected = "Array is not a TemporalArray")]
839 fn test_temporal_array_view_from_array_panics_on_wrong_variant() {
840 let array = Array::Null;
841 let _view = TemporalArrayV::from(array);
842 }
843
844 #[test]
845 fn test_to_temporal_array_full_coverage_shares_arc() {
846 let arr = DatetimeArray::<i64>::from_slice(&[10, 20, 30], None);
847 let src = Arc::new(arr);
848 let temporal = TemporalArray::Datetime64(src.clone());
849
850 let view = TemporalArrayV::new(temporal, 0, 3);
851 let out = view.to_temporal_array();
852
853 match out {
854 TemporalArray::Datetime64(out_arc) => assert!(
855 Arc::ptr_eq(&src, &out_arc),
856 "full-coverage to_temporal_array should share the underlying Arc"
857 ),
858 _ => panic!("expected Datetime64 variant"),
859 }
860 }
861
862 #[test]
863 fn test_to_temporal_array_windowed_copies() {
864 let arr = DatetimeArray::<i64>::from_slice(&[10, 20, 30], None);
865 let src = Arc::new(arr);
866 let temporal = TemporalArray::Datetime64(src.clone());
867
868 let view = TemporalArrayV::new(temporal, 1, 2);
869 let out = view.to_temporal_array();
870
871 match out {
872 TemporalArray::Datetime64(out_arc) => assert!(
873 !Arc::ptr_eq(&src, &out_arc),
874 "windowed to_temporal_array must allocate a fresh buffer"
875 ),
876 _ => panic!("expected Datetime64 variant"),
877 }
878 }
879}