35
35
//! assert_eq!(7.0, c.value(2));
36
36
//! ```
37
37
38
- use chrono:: { NaiveTime , TimeZone , Timelike , Utc } ;
38
+ use chrono:: { NaiveTime , Offset , TimeZone , Timelike , Utc } ;
39
39
use std:: cmp:: Ordering ;
40
40
use std:: sync:: Arc ;
41
41
@@ -1770,7 +1770,7 @@ pub fn cast_with_options(
1770
1770
tz. clone ( ) ,
1771
1771
) ) ,
1772
1772
1773
- ( Timestamp ( from_unit, _ ) , Timestamp ( to_unit, to_tz) ) => {
1773
+ ( Timestamp ( from_unit, from_tz ) , Timestamp ( to_unit, to_tz) ) => {
1774
1774
let array = cast_with_options ( array, & Int64 , cast_options) ?;
1775
1775
let time_array = array. as_primitive :: < Int64Type > ( ) ;
1776
1776
let from_size = time_unit_multiple ( from_unit) ;
@@ -1792,8 +1792,52 @@ pub fn cast_with_options(
1792
1792
}
1793
1793
}
1794
1794
} ;
1795
+ // Normalize timezone
1796
+ let adjusted = match ( from_tz, to_tz) {
1797
+ // Only this case needs to be adjusted because we're casting from
1798
+ // unknown time offset to some time offset, we want the time to be
1799
+ // unchanged.
1800
+ //
1801
+ // i.e. Timestamp('2001-01-01T00:00', None) -> Timestamp('2001-01-01T00:00', '+0700')
1802
+ ( None , Some ( to_tz) ) => {
1803
+ let to_tz: Tz = to_tz. parse ( ) ?;
1804
+ match to_unit {
1805
+ TimeUnit :: Second => {
1806
+ adjust_timestamp_to_timezone :: < TimestampSecondType > (
1807
+ converted,
1808
+ & to_tz,
1809
+ cast_options,
1810
+ ) ?
1811
+ }
1812
+ TimeUnit :: Millisecond => {
1813
+ adjust_timestamp_to_timezone :: < TimestampMillisecondType > (
1814
+ converted,
1815
+ & to_tz,
1816
+ cast_options,
1817
+ ) ?
1818
+ }
1819
+ TimeUnit :: Microsecond => {
1820
+ adjust_timestamp_to_timezone :: < TimestampMicrosecondType > (
1821
+ converted,
1822
+ & to_tz,
1823
+ cast_options,
1824
+ ) ?
1825
+ }
1826
+ TimeUnit :: Nanosecond => {
1827
+ adjust_timestamp_to_timezone :: < TimestampNanosecondType > (
1828
+ converted,
1829
+ & to_tz,
1830
+ cast_options,
1831
+ ) ?
1832
+ }
1833
+ }
1834
+ }
1835
+ _ => {
1836
+ converted
1837
+ }
1838
+ } ;
1795
1839
Ok ( make_timestamp_array (
1796
- & converted ,
1840
+ & adjusted ,
1797
1841
to_unit. clone ( ) ,
1798
1842
to_tz. clone ( ) ,
1799
1843
) )
@@ -3005,6 +3049,30 @@ fn cast_string_to_month_day_nano_interval<Offset: OffsetSizeTrait>(
3005
3049
Ok ( Arc :: new ( interval_array) as ArrayRef )
3006
3050
}
3007
3051
3052
+ fn adjust_timestamp_to_timezone < T : ArrowTimestampType > (
3053
+ array : PrimitiveArray < Int64Type > ,
3054
+ to_tz : & Tz ,
3055
+ cast_options : & CastOptions ,
3056
+ ) -> Result < PrimitiveArray < Int64Type > , ArrowError > {
3057
+ let adjust = |o| {
3058
+ let local = as_datetime :: < T > ( o) ?;
3059
+ let offset = to_tz. offset_from_local_datetime ( & local) . single ( ) ?;
3060
+ T :: make_value ( local - offset. fix ( ) )
3061
+ } ;
3062
+ let adjusted = if cast_options. safe {
3063
+ array. unary_opt :: < _ , Int64Type > ( adjust)
3064
+ } else {
3065
+ array. try_unary :: < _ , Int64Type , _ > ( |o| {
3066
+ adjust ( o) . ok_or_else ( || {
3067
+ ArrowError :: CastError (
3068
+ "Cannot cast timezone to different timezone" . to_string ( ) ,
3069
+ )
3070
+ } )
3071
+ } ) ?
3072
+ } ;
3073
+ Ok ( adjusted)
3074
+ }
3075
+
3008
3076
/// Casts Utf8 to Boolean
3009
3077
fn cast_utf8_to_boolean < OffsetSize > (
3010
3078
from : & dyn Array ,
@@ -5978,6 +6046,83 @@ mod tests {
5978
6046
assert ! ( b. is_err( ) ) ;
5979
6047
}
5980
6048
6049
+ // Cast Timestamp(_, None) -> Timestamp(_, Some(timezone))
6050
+ #[ test]
6051
+ fn test_cast_timestamp_with_timezone_1 ( ) {
6052
+ let string_array: Arc < dyn Array > = Arc :: new ( StringArray :: from ( vec ! [
6053
+ Some ( "2000-01-01T00:00:00.123456789" ) ,
6054
+ Some ( "2010-01-01T00:00:00.123456789" ) ,
6055
+ None ,
6056
+ ] ) ) ;
6057
+ let to_type = DataType :: Timestamp ( TimeUnit :: Nanosecond , None ) ;
6058
+ let timestamp_array = cast ( & string_array, & to_type) . unwrap ( ) ;
6059
+
6060
+ let to_type = DataType :: Timestamp ( TimeUnit :: Microsecond , Some ( "+0700" . into ( ) ) ) ;
6061
+ let timestamp_array = cast ( & timestamp_array, & to_type) . unwrap ( ) ;
6062
+
6063
+ let string_array = cast ( & timestamp_array, & DataType :: Utf8 ) . unwrap ( ) ;
6064
+ let result = string_array. as_string :: < i32 > ( ) ;
6065
+ assert_eq ! ( "2000-01-01T00:00:00.123456+07:00" , result. value( 0 ) ) ;
6066
+ assert_eq ! ( "2010-01-01T00:00:00.123456+07:00" , result. value( 1 ) ) ;
6067
+ assert ! ( result. is_null( 2 ) ) ;
6068
+ }
6069
+
6070
+ // Cast Timestamp(_, Some(timezone)) -> Timestamp(_, None)
6071
+ #[ test]
6072
+ fn test_cast_timestamp_with_timezone_2 ( ) {
6073
+ let string_array: Arc < dyn Array > = Arc :: new ( StringArray :: from ( vec ! [
6074
+ Some ( "2000-01-01T07:00:00.123456789" ) ,
6075
+ Some ( "2010-01-01T07:00:00.123456789" ) ,
6076
+ None ,
6077
+ ] ) ) ;
6078
+ let to_type = DataType :: Timestamp ( TimeUnit :: Millisecond , Some ( "+0700" . into ( ) ) ) ;
6079
+ let timestamp_array = cast ( & string_array, & to_type) . unwrap ( ) ;
6080
+
6081
+ // Check intermediate representation is correct
6082
+ let string_array = cast ( & timestamp_array, & DataType :: Utf8 ) . unwrap ( ) ;
6083
+ let result = string_array. as_string :: < i32 > ( ) ;
6084
+ assert_eq ! ( "2000-01-01T07:00:00.123+07:00" , result. value( 0 ) ) ;
6085
+ assert_eq ! ( "2010-01-01T07:00:00.123+07:00" , result. value( 1 ) ) ;
6086
+ assert ! ( result. is_null( 2 ) ) ;
6087
+
6088
+ let to_type = DataType :: Timestamp ( TimeUnit :: Nanosecond , None ) ;
6089
+ let timestamp_array = cast ( & timestamp_array, & to_type) . unwrap ( ) ;
6090
+
6091
+ let string_array = cast ( & timestamp_array, & DataType :: Utf8 ) . unwrap ( ) ;
6092
+ let result = string_array. as_string :: < i32 > ( ) ;
6093
+ assert_eq ! ( "2000-01-01T00:00:00.123" , result. value( 0 ) ) ;
6094
+ assert_eq ! ( "2010-01-01T00:00:00.123" , result. value( 1 ) ) ;
6095
+ assert ! ( result. is_null( 2 ) ) ;
6096
+ }
6097
+
6098
+ // Cast Timestamp(_, Some(timezone)) -> Timestamp(_, Some(timezone))
6099
+ #[ test]
6100
+ fn test_cast_timestamp_with_timezone_3 ( ) {
6101
+ let string_array: Arc < dyn Array > = Arc :: new ( StringArray :: from ( vec ! [
6102
+ Some ( "2000-01-01T07:00:00.123456789" ) ,
6103
+ Some ( "2010-01-01T07:00:00.123456789" ) ,
6104
+ None ,
6105
+ ] ) ) ;
6106
+ let to_type = DataType :: Timestamp ( TimeUnit :: Microsecond , Some ( "+0700" . into ( ) ) ) ;
6107
+ let timestamp_array = cast ( & string_array, & to_type) . unwrap ( ) ;
6108
+
6109
+ // Check intermediate representation is correct
6110
+ let string_array = cast ( & timestamp_array, & DataType :: Utf8 ) . unwrap ( ) ;
6111
+ let result = string_array. as_string :: < i32 > ( ) ;
6112
+ assert_eq ! ( "2000-01-01T07:00:00.123456+07:00" , result. value( 0 ) ) ;
6113
+ assert_eq ! ( "2010-01-01T07:00:00.123456+07:00" , result. value( 1 ) ) ;
6114
+ assert ! ( result. is_null( 2 ) ) ;
6115
+
6116
+ let to_type = DataType :: Timestamp ( TimeUnit :: Second , Some ( "-08:00" . into ( ) ) ) ;
6117
+ let timestamp_array = cast ( & timestamp_array, & to_type) . unwrap ( ) ;
6118
+
6119
+ let string_array = cast ( & timestamp_array, & DataType :: Utf8 ) . unwrap ( ) ;
6120
+ let result = string_array. as_string :: < i32 > ( ) ;
6121
+ assert_eq ! ( "1999-12-31T16:00:00-08:00" , result. value( 0 ) ) ;
6122
+ assert_eq ! ( "2009-12-31T16:00:00-08:00" , result. value( 1 ) ) ;
6123
+ assert ! ( result. is_null( 2 ) ) ;
6124
+ }
6125
+
5981
6126
#[ test]
5982
6127
fn test_cast_date64_to_timestamp ( ) {
5983
6128
let array =
0 commit comments