7373 IntegerType ,
7474 LongType ,
7575 StringType ,
76+ TimestampNanoType ,
7677 TimestampType ,
78+ TimestamptzNanoType ,
7779 TimestamptzType ,
7880 TimeType ,
7981 UUIDType ,
@@ -457,13 +459,20 @@ def year_func(v: Any) -> int:
457459
458460 return datetime .micros_to_years (v )
459461
462+ elif isinstance (source , (TimestampNanoType , TimestamptzNanoType )):
463+
464+ def year_func (v : Any ) -> int :
465+ # python datetime has no nanoseconds support.
466+ # nanosecond datetimes will be expressed as int as a workaround
467+ return datetime .nanos_to_years (v )
468+
460469 else :
461470 raise ValueError (f"Cannot apply year transform for type: { source } " )
462471
463472 return lambda v : year_func (v ) if v is not None else None
464473
465474 def can_transform (self , source : IcebergType ) -> bool :
466- return isinstance (source , (DateType , TimestampType , TimestamptzType ))
475+ return isinstance (source , (DateType , TimestampType , TimestamptzType , TimestampNanoType , TimestamptzNanoType ))
467476
468477 @property
469478 def granularity (self ) -> TimeResolution :
@@ -481,15 +490,19 @@ def pyarrow_transform(self, source: IcebergType) -> "Callable[[pa.Array], pa.Arr
481490 import pyarrow .compute as pc
482491
483492 if isinstance (source , DateType ):
484- epoch = datetime .EPOCH_DATE
493+ epoch = pa . scalar ( datetime .EPOCH_DATE )
485494 elif isinstance (source , TimestampType ):
486- epoch = datetime .EPOCH_TIMESTAMP
495+ epoch = pa . scalar ( datetime .EPOCH_TIMESTAMP )
487496 elif isinstance (source , TimestamptzType ):
488- epoch = datetime .EPOCH_TIMESTAMPTZ
497+ epoch = pa .scalar (datetime .EPOCH_TIMESTAMPTZ )
498+ elif isinstance (source , TimestampNanoType ):
499+ epoch = pa .scalar (datetime .EPOCH_TIMESTAMP ).cast (pa .timestamp ("ns" ))
500+ elif isinstance (source , TimestamptzNanoType ):
501+ epoch = pa .scalar (datetime .EPOCH_TIMESTAMPTZ ).cast (pa .timestamp ("ns" ))
489502 else :
490503 raise ValueError (f"Cannot apply year transform for type: { source } " )
491504
492- return lambda v : pc .years_between (pa . scalar ( epoch ) , v ) if v is not None else None
505+ return lambda v : pc .years_between (epoch , v ) if v is not None else None
493506
494507
495508class MonthTransform (TimeTransform [S ]):
@@ -520,13 +533,20 @@ def month_func(v: Any) -> int:
520533
521534 return datetime .micros_to_months (v )
522535
536+ elif isinstance (source , (TimestampNanoType , TimestamptzNanoType )):
537+
538+ def month_func (v : Any ) -> int :
539+ # python datetime has no nanoseconds support.
540+ # nanosecond datetimes will be expressed as int as a workaround
541+ return datetime .nanos_to_months (v )
542+
523543 else :
524544 raise ValueError (f"Cannot apply month transform for type: { source } " )
525545
526546 return lambda v : month_func (v ) if v is not None else None
527547
528548 def can_transform (self , source : IcebergType ) -> bool :
529- return isinstance (source , (DateType , TimestampType , TimestamptzType ))
549+ return isinstance (source , (DateType , TimestampType , TimestamptzType , TimestampNanoType , TimestamptzNanoType ))
530550
531551 @property
532552 def granularity (self ) -> TimeResolution :
@@ -544,17 +564,21 @@ def pyarrow_transform(self, source: IcebergType) -> "Callable[[pa.Array], pa.Arr
544564 import pyarrow .compute as pc
545565
546566 if isinstance (source , DateType ):
547- epoch = datetime .EPOCH_DATE
567+ epoch = pa . scalar ( datetime .EPOCH_DATE )
548568 elif isinstance (source , TimestampType ):
549- epoch = datetime .EPOCH_TIMESTAMP
569+ epoch = pa . scalar ( datetime .EPOCH_TIMESTAMP )
550570 elif isinstance (source , TimestamptzType ):
551- epoch = datetime .EPOCH_TIMESTAMPTZ
571+ epoch = pa .scalar (datetime .EPOCH_TIMESTAMPTZ )
572+ elif isinstance (source , TimestampNanoType ):
573+ epoch = pa .scalar (datetime .EPOCH_TIMESTAMP ).cast (pa .timestamp ("ns" ))
574+ elif isinstance (source , TimestamptzNanoType ):
575+ epoch = pa .scalar (datetime .EPOCH_TIMESTAMPTZ ).cast (pa .timestamp ("ns" ))
552576 else :
553577 raise ValueError (f"Cannot apply month transform for type: { source } " )
554578
555579 def month_func (v : pa .Array ) -> pa .Array :
556580 return pc .add (
557- pc .multiply (pc .years_between (pa . scalar ( epoch ) , v ), pa .scalar (12 )),
581+ pc .multiply (pc .years_between (epoch , v ), pa .scalar (12 )),
558582 pc .add (pc .month (v ), pa .scalar (- 1 )),
559583 )
560584
@@ -589,13 +613,20 @@ def day_func(v: Any) -> int:
589613
590614 return datetime .micros_to_days (v )
591615
616+ elif isinstance (source , (TimestampNanoType , TimestamptzNanoType )):
617+
618+ def day_func (v : Any ) -> int :
619+ # python datetime has no nanoseconds support.
620+ # nanosecond datetimes will be expressed as int as a workaround
621+ return datetime .nanos_to_days (v )
622+
592623 else :
593624 raise ValueError (f"Cannot apply day transform for type: { source } " )
594625
595626 return lambda v : day_func (v ) if v is not None else None
596627
597628 def can_transform (self , source : IcebergType ) -> bool :
598- return isinstance (source , (DateType , TimestampType , TimestamptzType ))
629+ return isinstance (source , (DateType , TimestampType , TimestamptzType , TimestampNanoType , TimestamptzNanoType ))
599630
600631 def result_type (self , source : IcebergType ) -> IcebergType :
601632 """Return the result type of a day transform.
@@ -621,15 +652,19 @@ def pyarrow_transform(self, source: IcebergType) -> "Callable[[pa.Array], pa.Arr
621652 import pyarrow .compute as pc
622653
623654 if isinstance (source , DateType ):
624- epoch = datetime .EPOCH_DATE
655+ epoch = pa . scalar ( datetime .EPOCH_DATE )
625656 elif isinstance (source , TimestampType ):
626- epoch = datetime .EPOCH_TIMESTAMP
657+ epoch = pa . scalar ( datetime .EPOCH_TIMESTAMP )
627658 elif isinstance (source , TimestamptzType ):
628- epoch = datetime .EPOCH_TIMESTAMPTZ
659+ epoch = pa .scalar (datetime .EPOCH_TIMESTAMPTZ )
660+ elif isinstance (source , TimestampNanoType ):
661+ epoch = pa .scalar (datetime .EPOCH_TIMESTAMP ).cast (pa .timestamp ("ns" ))
662+ elif isinstance (source , TimestamptzNanoType ):
663+ epoch = pa .scalar (datetime .EPOCH_TIMESTAMPTZ ).cast (pa .timestamp ("ns" ))
629664 else :
630665 raise ValueError (f"Cannot apply day transform for type: { source } " )
631666
632- return lambda v : pc .days_between (pa . scalar ( epoch ) , v ) if v is not None else None
667+ return lambda v : pc .days_between (epoch , v ) if v is not None else None
633668
634669
635670class HourTransform (TimeTransform [S ]):
@@ -652,13 +687,20 @@ def hour_func(v: Any) -> int:
652687
653688 return datetime .micros_to_hours (v )
654689
690+ elif isinstance (source , (TimestampNanoType , TimestamptzNanoType )):
691+
692+ def day_func (v : Any ) -> int :
693+ # python datetime has no nanoseconds support.
694+ # nanosecond datetimes will be expressed as int as a workaround
695+ return datetime .nanos_to_hours (v )
696+
655697 else :
656698 raise ValueError (f"Cannot apply hour transform for type: { source } " )
657699
658700 return lambda v : hour_func (v ) if v is not None else None
659701
660702 def can_transform (self , source : IcebergType ) -> bool :
661- return isinstance (source , (TimestampType , TimestamptzType ))
703+ return isinstance (source , (TimestampType , TimestamptzType , TimestampNanoType , TimestamptzNanoType ))
662704
663705 @property
664706 def granularity (self ) -> TimeResolution :
@@ -676,13 +718,17 @@ def pyarrow_transform(self, source: IcebergType) -> "Callable[[pa.Array], pa.Arr
676718 import pyarrow .compute as pc
677719
678720 if isinstance (source , TimestampType ):
679- epoch = datetime .EPOCH_TIMESTAMP
721+ epoch = pa . scalar ( datetime .EPOCH_TIMESTAMP )
680722 elif isinstance (source , TimestamptzType ):
681- epoch = datetime .EPOCH_TIMESTAMPTZ
723+ epoch = pa .scalar (datetime .EPOCH_TIMESTAMPTZ )
724+ elif isinstance (source , TimestampNanoType ):
725+ epoch = pa .scalar (datetime .EPOCH_TIMESTAMP ).cast (pa .timestamp ("ns" ))
726+ elif isinstance (source , TimestamptzNanoType ):
727+ epoch = pa .scalar (datetime .EPOCH_TIMESTAMPTZ ).cast (pa .timestamp ("ns" ))
682728 else :
683729 raise ValueError (f"Cannot apply hour transform for type: { source } " )
684730
685- return lambda v : pc .hours_between (pa . scalar ( epoch ) , v ) if v is not None else None
731+ return lambda v : pc .hours_between (epoch , v ) if v is not None else None
686732
687733
688734def _base64encode (buffer : bytes ) -> str :
0 commit comments