Skip to main content

typed_arrow/bridge/
temporal.rs

1//! Temporal types: Date, Time, Duration, Timestamp (with/without timezone).
2
3use std::{marker::PhantomData, sync::Arc};
4
5#[cfg(feature = "views")]
6use arrow_array::Array;
7use arrow_array::{
8    PrimitiveArray,
9    builder::PrimitiveBuilder,
10    types::{
11        Date32Type, Date64Type, DurationMicrosecondType, DurationMillisecondType,
12        DurationNanosecondType, DurationSecondType, Time32MillisecondType, Time32SecondType,
13        Time64MicrosecondType, Time64NanosecondType, TimestampMicrosecondType,
14        TimestampMillisecondType, TimestampNanosecondType, TimestampSecondType,
15    },
16};
17use arrow_schema::{DataType, TimeUnit};
18
19use super::ArrowBinding;
20#[cfg(feature = "views")]
21use super::ArrowBindingView;
22
23// ---------- Timestamp units and bindings ----------
24
25/// Marker describing a timestamp unit.
26pub trait TimeUnitSpec {
27    /// Typed Arrow timestamp marker for this unit.
28    type Arrow: arrow_array::types::ArrowTimestampType;
29    /// The `arrow_schema::TimeUnit` of this marker.
30    fn unit() -> TimeUnit;
31}
32
33/// Seconds since epoch.
34#[derive(Debug, Clone)]
35pub enum Second {}
36impl TimeUnitSpec for Second {
37    type Arrow = TimestampSecondType;
38    fn unit() -> TimeUnit {
39        TimeUnit::Second
40    }
41}
42
43/// Milliseconds since epoch.
44#[derive(Debug, Clone)]
45pub enum Millisecond {}
46impl TimeUnitSpec for Millisecond {
47    type Arrow = TimestampMillisecondType;
48    fn unit() -> TimeUnit {
49        TimeUnit::Millisecond
50    }
51}
52
53/// Microseconds since epoch.
54#[derive(Debug, Clone)]
55pub enum Microsecond {}
56impl TimeUnitSpec for Microsecond {
57    type Arrow = TimestampMicrosecondType;
58    fn unit() -> TimeUnit {
59        TimeUnit::Microsecond
60    }
61}
62
63/// Nanoseconds since epoch.
64#[derive(Debug, Clone)]
65pub enum Nanosecond {}
66impl TimeUnitSpec for Nanosecond {
67    type Arrow = TimestampNanosecondType;
68    fn unit() -> TimeUnit {
69        TimeUnit::Nanosecond
70    }
71}
72
73/// Timestamp value (unit only, timezone = None).
74#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
75pub struct Timestamp<U: TimeUnitSpec>(i64, PhantomData<U>);
76impl<U: TimeUnitSpec> Timestamp<U> {
77    /// Construct a new timestamp from an epoch value in the unit `U`.
78    #[inline]
79    #[must_use]
80    pub fn new(value: i64) -> Self {
81        Self(value, PhantomData)
82    }
83    /// Return the inner epoch value.
84    #[inline]
85    #[must_use]
86    pub fn value(&self) -> i64 {
87        self.0
88    }
89    /// Consume and return the inner epoch value.
90    #[inline]
91    #[must_use]
92    pub fn into_value(self) -> i64 {
93        self.0
94    }
95}
96impl<U: TimeUnitSpec> ArrowBinding for Timestamp<U> {
97    type Builder = PrimitiveBuilder<U::Arrow>;
98    type Array = PrimitiveArray<U::Arrow>;
99    fn data_type() -> DataType {
100        DataType::Timestamp(U::unit(), None)
101    }
102    fn new_builder(capacity: usize) -> Self::Builder {
103        PrimitiveBuilder::<U::Arrow>::with_capacity(capacity)
104    }
105    fn append_value(b: &mut Self::Builder, v: &Self) {
106        b.append_value(v.0);
107    }
108    fn append_null(b: &mut Self::Builder) {
109        b.append_null();
110    }
111    fn finish(mut b: Self::Builder) -> Self::Array {
112        b.finish()
113    }
114}
115
116#[cfg(feature = "views")]
117impl<U: TimeUnitSpec + 'static> ArrowBindingView for Timestamp<U> {
118    type Array = PrimitiveArray<U::Arrow>;
119    type View<'a> = Timestamp<U>;
120
121    fn get_view(
122        array: &Self::Array,
123        index: usize,
124    ) -> Result<Self::View<'_>, crate::schema::ViewAccessError> {
125        if index >= array.len() {
126            return Err(crate::schema::ViewAccessError::OutOfBounds {
127                index,
128                len: array.len(),
129                field_name: None,
130            });
131        }
132        if array.is_null(index) {
133            return Err(crate::schema::ViewAccessError::UnexpectedNull {
134                index,
135                field_name: None,
136            });
137        }
138        Ok(Timestamp::new(array.value(index)))
139    }
140}
141
142/// Marker describing a timestamp timezone.
143pub trait TimeZoneSpec {
144    /// The optional timezone name for this marker.
145    const NAME: Option<&'static str>;
146}
147
148/// UTC timezone marker.
149pub enum Utc {}
150impl TimeZoneSpec for Utc {
151    const NAME: Option<&'static str> = Some("UTC");
152}
153
154/// Timestamp with time unit `U` and timezone marker `Z`.
155#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
156pub struct TimestampTz<U: TimeUnitSpec, Z: TimeZoneSpec>(i64, PhantomData<(U, Z)>);
157impl<U: TimeUnitSpec, Z: TimeZoneSpec> TimestampTz<U, Z> {
158    /// Construct a new timezone-aware timestamp from an epoch value in the unit `U`.
159    #[inline]
160    #[must_use]
161    pub fn new(value: i64) -> Self {
162        Self(value, PhantomData)
163    }
164    /// Return the inner epoch value.
165    #[inline]
166    #[must_use]
167    pub fn value(&self) -> i64 {
168        self.0
169    }
170    /// Consume and return the inner epoch value.
171    #[inline]
172    #[must_use]
173    pub fn into_value(self) -> i64 {
174        self.0
175    }
176}
177impl<U: TimeUnitSpec, Z: TimeZoneSpec> ArrowBinding for TimestampTz<U, Z> {
178    type Builder = PrimitiveBuilder<U::Arrow>;
179    type Array = PrimitiveArray<U::Arrow>;
180    fn data_type() -> DataType {
181        DataType::Timestamp(U::unit(), Z::NAME.map(Arc::<str>::from))
182    }
183    fn new_builder(capacity: usize) -> Self::Builder {
184        PrimitiveBuilder::<U::Arrow>::with_capacity(capacity)
185    }
186    fn append_value(b: &mut Self::Builder, v: &Self) {
187        b.append_value(v.0);
188    }
189    fn append_null(b: &mut Self::Builder) {
190        b.append_null();
191    }
192    fn finish(mut b: Self::Builder) -> Self::Array {
193        b.finish()
194    }
195}
196
197#[cfg(feature = "views")]
198impl<U: TimeUnitSpec + 'static, Z: TimeZoneSpec + 'static> ArrowBindingView for TimestampTz<U, Z> {
199    type Array = PrimitiveArray<U::Arrow>;
200    type View<'a> = TimestampTz<U, Z>;
201
202    fn get_view(
203        array: &Self::Array,
204        index: usize,
205    ) -> Result<Self::View<'_>, crate::schema::ViewAccessError> {
206        if index >= array.len() {
207            return Err(crate::schema::ViewAccessError::OutOfBounds {
208                index,
209                len: array.len(),
210                field_name: None,
211            });
212        }
213        if array.is_null(index) {
214            return Err(crate::schema::ViewAccessError::UnexpectedNull {
215                index,
216                field_name: None,
217            });
218        }
219        Ok(TimestampTz::new(array.value(index)))
220    }
221}
222
223// ---------- Date32 / Date64 ----------
224
225/// Days since UNIX epoch.
226#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
227pub struct Date32(i32);
228impl Date32 {
229    /// Construct a new `Date32` from days since UNIX epoch.
230    #[inline]
231    #[must_use]
232    pub fn new(value: i32) -> Self {
233        Self(value)
234    }
235    /// Return the days since UNIX epoch.
236    #[inline]
237    #[must_use]
238    pub fn value(&self) -> i32 {
239        self.0
240    }
241    /// Consume and return the days since UNIX epoch.
242    #[inline]
243    #[must_use]
244    pub fn into_value(self) -> i32 {
245        self.0
246    }
247}
248impl ArrowBinding for Date32 {
249    type Builder = PrimitiveBuilder<Date32Type>;
250    type Array = PrimitiveArray<Date32Type>;
251    fn data_type() -> DataType {
252        DataType::Date32
253    }
254    fn new_builder(capacity: usize) -> Self::Builder {
255        PrimitiveBuilder::<Date32Type>::with_capacity(capacity)
256    }
257    fn append_value(b: &mut Self::Builder, v: &Self) {
258        b.append_value(v.0);
259    }
260    fn append_null(b: &mut Self::Builder) {
261        b.append_null();
262    }
263    fn finish(mut b: Self::Builder) -> Self::Array {
264        b.finish()
265    }
266}
267
268#[cfg(feature = "views")]
269impl ArrowBindingView for Date32 {
270    type Array = PrimitiveArray<Date32Type>;
271    type View<'a> = Date32;
272
273    fn get_view(
274        array: &Self::Array,
275        index: usize,
276    ) -> Result<Self::View<'_>, crate::schema::ViewAccessError> {
277        if index >= array.len() {
278            return Err(crate::schema::ViewAccessError::OutOfBounds {
279                index,
280                len: array.len(),
281                field_name: None,
282            });
283        }
284        if array.is_null(index) {
285            return Err(crate::schema::ViewAccessError::UnexpectedNull {
286                index,
287                field_name: None,
288            });
289        }
290        Ok(Date32::new(array.value(index)))
291    }
292}
293
294/// Milliseconds since UNIX epoch.
295#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
296pub struct Date64(i64);
297impl Date64 {
298    /// Construct a new `Date64` from milliseconds since UNIX epoch.
299    #[inline]
300    #[must_use]
301    pub fn new(value: i64) -> Self {
302        Self(value)
303    }
304    /// Return the milliseconds since UNIX epoch.
305    #[inline]
306    #[must_use]
307    pub fn value(&self) -> i64 {
308        self.0
309    }
310    /// Consume and return the milliseconds since UNIX epoch.
311    #[inline]
312    #[must_use]
313    pub fn into_value(self) -> i64 {
314        self.0
315    }
316}
317impl ArrowBinding for Date64 {
318    type Builder = PrimitiveBuilder<Date64Type>;
319    type Array = PrimitiveArray<Date64Type>;
320    fn data_type() -> DataType {
321        DataType::Date64
322    }
323    fn new_builder(capacity: usize) -> Self::Builder {
324        PrimitiveBuilder::<Date64Type>::with_capacity(capacity)
325    }
326    fn append_value(b: &mut Self::Builder, v: &Self) {
327        b.append_value(v.0);
328    }
329    fn append_null(b: &mut Self::Builder) {
330        b.append_null();
331    }
332    fn finish(mut b: Self::Builder) -> Self::Array {
333        b.finish()
334    }
335}
336
337#[cfg(feature = "views")]
338impl ArrowBindingView for Date64 {
339    type Array = PrimitiveArray<Date64Type>;
340    type View<'a> = Date64;
341
342    fn get_view(
343        array: &Self::Array,
344        index: usize,
345    ) -> Result<Self::View<'_>, crate::schema::ViewAccessError> {
346        if index >= array.len() {
347            return Err(crate::schema::ViewAccessError::OutOfBounds {
348                index,
349                len: array.len(),
350                field_name: None,
351            });
352        }
353        if array.is_null(index) {
354            return Err(crate::schema::ViewAccessError::UnexpectedNull {
355                index,
356                field_name: None,
357            });
358        }
359        Ok(Date64::new(array.value(index)))
360    }
361}
362
363// ---------- Time32 / Time64 ----------
364
365/// Marker mapping for `Time32` units to Arrow time types.
366pub trait Time32UnitSpec {
367    type Arrow;
368    fn unit() -> TimeUnit;
369}
370impl Time32UnitSpec for Second {
371    type Arrow = Time32SecondType;
372    fn unit() -> TimeUnit {
373        TimeUnit::Second
374    }
375}
376impl Time32UnitSpec for Millisecond {
377    type Arrow = Time32MillisecondType;
378    fn unit() -> TimeUnit {
379        TimeUnit::Millisecond
380    }
381}
382
383/// Number of seconds/milliseconds since midnight.
384#[derive(Debug, Clone)]
385pub struct Time32<U: Time32UnitSpec>(i32, PhantomData<U>);
386impl<U: Time32UnitSpec> Time32<U> {
387    /// Construct a new `Time32` value from an `i32` count in unit `U`.
388    #[inline]
389    #[must_use]
390    pub fn new(value: i32) -> Self {
391        Self(value, PhantomData)
392    }
393    /// Return the inner value.
394    #[inline]
395    #[must_use]
396    pub fn value(&self) -> i32 {
397        self.0
398    }
399    /// Consume and return the inner value.
400    #[inline]
401    #[must_use]
402    pub fn into_value(self) -> i32 {
403        self.0
404    }
405}
406impl<U: Time32UnitSpec> ArrowBinding for Time32<U>
407where
408    U::Arrow: arrow_array::types::ArrowPrimitiveType<Native = i32>,
409{
410    type Builder = PrimitiveBuilder<U::Arrow>;
411    type Array = PrimitiveArray<U::Arrow>;
412    fn data_type() -> DataType {
413        DataType::Time32(U::unit())
414    }
415    fn new_builder(capacity: usize) -> Self::Builder {
416        PrimitiveBuilder::<U::Arrow>::with_capacity(capacity)
417    }
418    fn append_value(b: &mut Self::Builder, v: &Self) {
419        b.append_value(v.0 as <U::Arrow as arrow_array::types::ArrowPrimitiveType>::Native);
420    }
421    fn append_null(b: &mut Self::Builder) {
422        b.append_null();
423    }
424    fn finish(mut b: Self::Builder) -> Self::Array {
425        b.finish()
426    }
427}
428
429#[cfg(feature = "views")]
430impl<U: Time32UnitSpec + 'static> ArrowBindingView for Time32<U>
431where
432    U::Arrow: arrow_array::types::ArrowPrimitiveType<Native = i32>,
433{
434    type Array = PrimitiveArray<U::Arrow>;
435    type View<'a> = Time32<U>;
436
437    fn get_view(
438        array: &Self::Array,
439        index: usize,
440    ) -> Result<Self::View<'_>, crate::schema::ViewAccessError> {
441        if index >= array.len() {
442            return Err(crate::schema::ViewAccessError::OutOfBounds {
443                index,
444                len: array.len(),
445                field_name: None,
446            });
447        }
448        if array.is_null(index) {
449            return Err(crate::schema::ViewAccessError::UnexpectedNull {
450                index,
451                field_name: None,
452            });
453        }
454        Ok(Time32::new(array.value(index)))
455    }
456}
457
458/// Marker mapping for `Time64` units to Arrow time types.
459pub trait Time64UnitSpec {
460    type Arrow;
461    fn unit() -> TimeUnit;
462}
463impl Time64UnitSpec for Microsecond {
464    type Arrow = Time64MicrosecondType;
465    fn unit() -> TimeUnit {
466        TimeUnit::Microsecond
467    }
468}
469impl Time64UnitSpec for Nanosecond {
470    type Arrow = Time64NanosecondType;
471    fn unit() -> TimeUnit {
472        TimeUnit::Nanosecond
473    }
474}
475
476/// Number of microseconds/nanoseconds since midnight.
477#[derive(Debug, Clone)]
478pub struct Time64<U: Time64UnitSpec>(i64, PhantomData<U>);
479impl<U: Time64UnitSpec> Time64<U> {
480    /// Construct a new `Time64` value from an `i64` count in unit `U`.
481    #[inline]
482    #[must_use]
483    pub fn new(value: i64) -> Self {
484        Self(value, PhantomData)
485    }
486    /// Return the inner value.
487    #[inline]
488    #[must_use]
489    pub fn value(&self) -> i64 {
490        self.0
491    }
492    /// Consume and return the inner value.
493    #[inline]
494    #[must_use]
495    pub fn into_value(self) -> i64 {
496        self.0
497    }
498}
499impl<U: Time64UnitSpec> ArrowBinding for Time64<U>
500where
501    U::Arrow: arrow_array::types::ArrowPrimitiveType<Native = i64>,
502{
503    type Builder = PrimitiveBuilder<U::Arrow>;
504    type Array = PrimitiveArray<U::Arrow>;
505    fn data_type() -> DataType {
506        DataType::Time64(U::unit())
507    }
508    fn new_builder(capacity: usize) -> Self::Builder {
509        PrimitiveBuilder::<U::Arrow>::with_capacity(capacity)
510    }
511    fn append_value(b: &mut Self::Builder, v: &Self) {
512        b.append_value(v.0 as <U::Arrow as arrow_array::types::ArrowPrimitiveType>::Native);
513    }
514    fn append_null(b: &mut Self::Builder) {
515        b.append_null();
516    }
517    fn finish(mut b: Self::Builder) -> Self::Array {
518        b.finish()
519    }
520}
521
522#[cfg(feature = "views")]
523impl<U: Time64UnitSpec + 'static> ArrowBindingView for Time64<U>
524where
525    U::Arrow: arrow_array::types::ArrowPrimitiveType<Native = i64>,
526{
527    type Array = PrimitiveArray<U::Arrow>;
528    type View<'a> = Time64<U>;
529
530    fn get_view(
531        array: &Self::Array,
532        index: usize,
533    ) -> Result<Self::View<'_>, crate::schema::ViewAccessError> {
534        if index >= array.len() {
535            return Err(crate::schema::ViewAccessError::OutOfBounds {
536                index,
537                len: array.len(),
538                field_name: None,
539            });
540        }
541        if array.is_null(index) {
542            return Err(crate::schema::ViewAccessError::UnexpectedNull {
543                index,
544                field_name: None,
545            });
546        }
547        Ok(Time64::new(array.value(index)))
548    }
549}
550
551// ---------- Duration ----------
552
553/// Marker mapping for `Duration` units to Arrow duration types.
554pub trait DurationUnitSpec {
555    type Arrow;
556    fn unit() -> TimeUnit;
557}
558impl DurationUnitSpec for Second {
559    type Arrow = DurationSecondType;
560    fn unit() -> TimeUnit {
561        TimeUnit::Second
562    }
563}
564impl DurationUnitSpec for Millisecond {
565    type Arrow = DurationMillisecondType;
566    fn unit() -> TimeUnit {
567        TimeUnit::Millisecond
568    }
569}
570impl DurationUnitSpec for Microsecond {
571    type Arrow = DurationMicrosecondType;
572    fn unit() -> TimeUnit {
573        TimeUnit::Microsecond
574    }
575}
576impl DurationUnitSpec for Nanosecond {
577    type Arrow = DurationNanosecondType;
578    fn unit() -> TimeUnit {
579        TimeUnit::Nanosecond
580    }
581}
582
583/// Duration in the given unit.
584#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
585pub struct Duration<U: DurationUnitSpec>(i64, PhantomData<U>);
586impl<U: DurationUnitSpec> Duration<U> {
587    /// Construct a new duration from an `i64` count in unit `U`.
588    #[inline]
589    #[must_use]
590    pub fn new(value: i64) -> Self {
591        Self(value, PhantomData)
592    }
593    /// Return the inner value.
594    #[inline]
595    #[must_use]
596    pub fn value(&self) -> i64 {
597        self.0
598    }
599    /// Consume and return the inner value.
600    #[inline]
601    #[must_use]
602    pub fn into_value(self) -> i64 {
603        self.0
604    }
605}
606impl<U: DurationUnitSpec> ArrowBinding for Duration<U>
607where
608    U::Arrow: arrow_array::types::ArrowPrimitiveType<Native = i64>,
609{
610    type Builder = PrimitiveBuilder<U::Arrow>;
611    type Array = PrimitiveArray<U::Arrow>;
612    fn data_type() -> DataType {
613        DataType::Duration(U::unit())
614    }
615    fn new_builder(capacity: usize) -> Self::Builder {
616        PrimitiveBuilder::<U::Arrow>::with_capacity(capacity)
617    }
618    fn append_value(b: &mut Self::Builder, v: &Self) {
619        b.append_value(v.0);
620    }
621    fn append_null(b: &mut Self::Builder) {
622        b.append_null();
623    }
624    fn finish(mut b: Self::Builder) -> Self::Array {
625        b.finish()
626    }
627}
628
629#[cfg(feature = "views")]
630impl<U: DurationUnitSpec + 'static> ArrowBindingView for Duration<U>
631where
632    U::Arrow: arrow_array::types::ArrowPrimitiveType<Native = i64>,
633{
634    type Array = PrimitiveArray<U::Arrow>;
635    type View<'a> = Duration<U>;
636
637    fn get_view(
638        array: &Self::Array,
639        index: usize,
640    ) -> Result<Self::View<'_>, crate::schema::ViewAccessError> {
641        if index >= array.len() {
642            return Err(crate::schema::ViewAccessError::OutOfBounds {
643                index,
644                len: array.len(),
645                field_name: None,
646            });
647        }
648        if array.is_null(index) {
649            return Err(crate::schema::ViewAccessError::UnexpectedNull {
650                index,
651                field_name: None,
652            });
653        }
654        Ok(Duration::new(array.value(index)))
655    }
656}
657
658#[cfg(feature = "jiff")]
659impl ArrowBinding for jiff::Timestamp {
660    type Builder = PrimitiveBuilder<TimestampMicrosecondType>;
661    type Array = PrimitiveArray<TimestampMicrosecondType>;
662
663    fn data_type() -> DataType {
664        DataType::Timestamp(TimeUnit::Microsecond, None)
665    }
666
667    fn new_builder(capacity: usize) -> Self::Builder {
668        PrimitiveBuilder::<TimestampMicrosecondType>::with_capacity(capacity)
669    }
670
671    fn append_value(b: &mut Self::Builder, v: &Self) {
672        b.append_value(v.as_microsecond());
673    }
674
675    fn append_null(b: &mut Self::Builder) {
676        b.append_null();
677    }
678
679    fn finish(mut b: Self::Builder) -> Self::Array {
680        b.finish()
681    }
682}
683
684#[cfg(all(feature = "jiff", feature = "views"))]
685impl ArrowBindingView for jiff::Timestamp {
686    type Array = PrimitiveArray<TimestampMicrosecondType>;
687    type View<'a> = jiff::Timestamp;
688
689    fn get_view(
690        array: &Self::Array,
691        index: usize,
692    ) -> Result<Self::View<'_>, crate::schema::ViewAccessError> {
693        if index >= array.len() {
694            return Err(crate::schema::ViewAccessError::OutOfBounds {
695                index,
696                len: array.len(),
697                field_name: None,
698            });
699        }
700        if array.is_null(index) {
701            return Err(crate::schema::ViewAccessError::UnexpectedNull {
702                index,
703                field_name: None,
704            });
705        }
706        jiff::Timestamp::from_microsecond(array.value(index))
707            .map_err(|e| crate::schema::ViewAccessError::Custom(Box::new(e)))
708    }
709}
710
711#[cfg(feature = "jiff")]
712const JIFF_UNIX_EPOCH_DATE: jiff::civil::Date = jiff::civil::Date::constant(1970, 1, 1);
713
714#[cfg(feature = "jiff")]
715impl ArrowBinding for jiff::civil::Date {
716    type Builder = PrimitiveBuilder<Date32Type>;
717    type Array = PrimitiveArray<Date32Type>;
718
719    fn data_type() -> DataType {
720        DataType::Date32
721    }
722
723    fn new_builder(capacity: usize) -> Self::Builder {
724        PrimitiveBuilder::<Date32Type>::with_capacity(capacity)
725    }
726
727    fn append_value(b: &mut Self::Builder, v: &Self) {
728        b.append_value(
729            v.since((jiff::Unit::Day, JIFF_UNIX_EPOCH_DATE))
730                .unwrap()
731                .get_days(),
732        );
733    }
734
735    fn append_null(b: &mut Self::Builder) {
736        b.append_null();
737    }
738
739    fn finish(mut b: Self::Builder) -> Self::Array {
740        b.finish()
741    }
742}
743
744#[cfg(all(feature = "jiff", feature = "views"))]
745impl ArrowBindingView for jiff::civil::Date {
746    type Array = PrimitiveArray<Date32Type>;
747    type View<'a> = jiff::civil::Date;
748
749    fn get_view(
750        array: &Self::Array,
751        index: usize,
752    ) -> Result<Self::View<'_>, crate::schema::ViewAccessError> {
753        if index >= array.len() {
754            return Err(crate::schema::ViewAccessError::OutOfBounds {
755                index,
756                len: array.len(),
757                field_name: None,
758            });
759        }
760        if array.is_null(index) {
761            return Err(crate::schema::ViewAccessError::UnexpectedNull {
762                index,
763                field_name: None,
764            });
765        }
766        JIFF_UNIX_EPOCH_DATE
767            .checked_add(jiff::Span::new().days(array.value(index)))
768            .map_err(|e| crate::schema::ViewAccessError::Custom(Box::new(e)))
769    }
770}