1use 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
23pub trait TimeUnitSpec {
27 type Arrow: arrow_array::types::ArrowTimestampType;
29 fn unit() -> TimeUnit;
31}
32
33#[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#[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#[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#[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#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
75pub struct Timestamp<U: TimeUnitSpec>(i64, PhantomData<U>);
76impl<U: TimeUnitSpec> Timestamp<U> {
77 #[inline]
79 #[must_use]
80 pub fn new(value: i64) -> Self {
81 Self(value, PhantomData)
82 }
83 #[inline]
85 #[must_use]
86 pub fn value(&self) -> i64 {
87 self.0
88 }
89 #[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
142pub trait TimeZoneSpec {
144 const NAME: Option<&'static str>;
146}
147
148pub enum Utc {}
150impl TimeZoneSpec for Utc {
151 const NAME: Option<&'static str> = Some("UTC");
152}
153
154#[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 #[inline]
160 #[must_use]
161 pub fn new(value: i64) -> Self {
162 Self(value, PhantomData)
163 }
164 #[inline]
166 #[must_use]
167 pub fn value(&self) -> i64 {
168 self.0
169 }
170 #[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#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
227pub struct Date32(i32);
228impl Date32 {
229 #[inline]
231 #[must_use]
232 pub fn new(value: i32) -> Self {
233 Self(value)
234 }
235 #[inline]
237 #[must_use]
238 pub fn value(&self) -> i32 {
239 self.0
240 }
241 #[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#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
296pub struct Date64(i64);
297impl Date64 {
298 #[inline]
300 #[must_use]
301 pub fn new(value: i64) -> Self {
302 Self(value)
303 }
304 #[inline]
306 #[must_use]
307 pub fn value(&self) -> i64 {
308 self.0
309 }
310 #[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
363pub 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#[derive(Debug, Clone)]
385pub struct Time32<U: Time32UnitSpec>(i32, PhantomData<U>);
386impl<U: Time32UnitSpec> Time32<U> {
387 #[inline]
389 #[must_use]
390 pub fn new(value: i32) -> Self {
391 Self(value, PhantomData)
392 }
393 #[inline]
395 #[must_use]
396 pub fn value(&self) -> i32 {
397 self.0
398 }
399 #[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
458pub 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#[derive(Debug, Clone)]
478pub struct Time64<U: Time64UnitSpec>(i64, PhantomData<U>);
479impl<U: Time64UnitSpec> Time64<U> {
480 #[inline]
482 #[must_use]
483 pub fn new(value: i64) -> Self {
484 Self(value, PhantomData)
485 }
486 #[inline]
488 #[must_use]
489 pub fn value(&self) -> i64 {
490 self.0
491 }
492 #[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
551pub 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#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
585pub struct Duration<U: DurationUnitSpec>(i64, PhantomData<U>);
586impl<U: DurationUnitSpec> Duration<U> {
587 #[inline]
589 #[must_use]
590 pub fn new(value: i64) -> Self {
591 Self(value, PhantomData)
592 }
593 #[inline]
595 #[must_use]
596 pub fn value(&self) -> i64 {
597 self.0
598 }
599 #[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}