@@ -947,7 +947,9 @@ def struct_field_op_impl(x: ibis_types.Value, op: ops.StructFieldOp):
947947 return result .cast (result .type ()(nullable = True )).name (name )
948948
949949
950- def numeric_to_datetime (x : ibis_types .Value , unit : str ) -> ibis_types .TimestampValue :
950+ def numeric_to_datetime (
951+ x : ibis_types .Value , unit : str , safe : bool = False
952+ ) -> ibis_types .TimestampValue :
951953 if not isinstance (x , ibis_types .IntegerValue ) and not isinstance (
952954 x , ibis_types .FloatingValue
953955 ):
@@ -956,7 +958,11 @@ def numeric_to_datetime(x: ibis_types.Value, unit: str) -> ibis_types.TimestampV
956958 if unit not in UNIT_TO_US_CONVERSION_FACTORS :
957959 raise ValueError (f"Cannot convert input with unit '{ unit } '." )
958960 x_converted = x * UNIT_TO_US_CONVERSION_FACTORS [unit ]
959- x_converted = x_converted .cast (ibis_dtypes .int64 )
961+ x_converted = (
962+ x_converted .try_cast (ibis_dtypes .int64 )
963+ if safe
964+ else x_converted .cast (ibis_dtypes .int64 )
965+ )
960966
961967 # Note: Due to an issue where casting directly to a timestamp
962968 # without a timezone does not work, we first cast to UTC. This
@@ -978,8 +984,11 @@ def astype_op_impl(x: ibis_types.Value, op: ops.AsTypeOp):
978984
979985 # When casting DATETIME column into INT column, we need to convert the column into TIMESTAMP first.
980986 if to_type == ibis_dtypes .int64 and x .type () == ibis_dtypes .timestamp :
981- x_converted = x .cast (ibis_dtypes .Timestamp (timezone = "UTC" ))
982- return bigframes .core .compile .ibis_types .cast_ibis_value (x_converted , to_type )
987+ utc_time_type = ibis_dtypes .Timestamp (timezone = "UTC" )
988+ x_converted = x .try_cast (utc_time_type ) if op .safe else x .cast (utc_time_type )
989+ return bigframes .core .compile .ibis_types .cast_ibis_value (
990+ x_converted , to_type , safe = op .safe
991+ )
983992
984993 if to_type == ibis_dtypes .int64 and x .type () == ibis_dtypes .time :
985994 # The conversion unit is set to "us" (microseconds) for consistency
@@ -991,15 +1000,20 @@ def astype_op_impl(x: ibis_types.Value, op: ops.AsTypeOp):
9911000 # with pandas converting int64[pyarrow] to timestamp[us][pyarrow],
9921001 # timestamp[us, tz=UTC][pyarrow], and time64[us][pyarrow].
9931002 unit = "us"
994- x_converted = numeric_to_datetime (x , unit )
1003+ x_converted = numeric_to_datetime (x , unit , safe = op . safe )
9951004 if to_type == ibis_dtypes .timestamp :
996- return x_converted .cast (ibis_dtypes .Timestamp ())
1005+ return (
1006+ x_converted .try_cast (ibis_dtypes .Timestamp ())
1007+ if op .safe
1008+ else x_converted .cast (ibis_dtypes .Timestamp ())
1009+ )
9971010 elif to_type == ibis_dtypes .Timestamp (timezone = "UTC" ):
9981011 return x_converted
9991012 elif to_type == ibis_dtypes .time :
10001013 return x_converted .time ()
10011014
1002- return bigframes .core .compile .ibis_types .cast_ibis_value (x , to_type )
1015+ # TODO: either inline this function, or push rest of this op into the function
1016+ return bigframes .core .compile .ibis_types .cast_ibis_value (x , to_type , safe = op .safe )
10031017
10041018
10051019@scalar_op_compiler .register_unary_op (ops .IsInOp , pass_op = True )
0 commit comments