@@ -47,10 +47,14 @@ pub enum DatePart {
47
47
Quarter ,
48
48
/// Calendar year
49
49
Year ,
50
+ /// ISO year, computed as per ISO 8601
51
+ YearISO ,
50
52
/// Month in the year, in range `1..=12`
51
53
Month ,
52
- /// ISO week of the year, in range `1..=53`
54
+ /// week of the year, in range `1..=53`, computed as per ISO 8601
53
55
Week ,
56
+ /// ISO week of the year, in range `1..=53`
57
+ WeekISO ,
54
58
/// Day of the month, in range `1..=31`
55
59
Day ,
56
60
/// Day of the week, in range `0..=6`, where Sunday is `0`
91
95
match part {
92
96
DatePart :: Quarter => |d| d. quarter ( ) as i32 ,
93
97
DatePart :: Year => |d| d. year ( ) ,
98
+ DatePart :: YearISO => |d| d. iso_week ( ) . year ( ) ,
94
99
DatePart :: Month => |d| d. month ( ) as i32 ,
95
- DatePart :: Week => |d| d. iso_week ( ) . week ( ) as i32 ,
100
+ DatePart :: Week | DatePart :: WeekISO => |d| d. iso_week ( ) . week ( ) as i32 ,
96
101
DatePart :: Day => |d| d. day ( ) as i32 ,
97
102
DatePart :: DayOfWeekSunday0 => |d| d. num_days_from_sunday ( ) ,
98
103
DatePart :: DayOfWeekMonday0 => |d| d. num_days_from_monday ( ) ,
@@ -102,7 +107,7 @@ where
102
107
DatePart :: Second => |d| d. second ( ) as i32 ,
103
108
DatePart :: Millisecond => |d| ( d. nanosecond ( ) / 1_000_000 ) as i32 ,
104
109
DatePart :: Microsecond => |d| ( d. nanosecond ( ) / 1_000 ) as i32 ,
105
- DatePart :: Nanosecond => |d| ( d. nanosecond ( ) ) as i32 ,
110
+ DatePart :: Nanosecond => |d| d. nanosecond ( ) as i32 ,
106
111
}
107
112
}
108
113
@@ -130,9 +135,14 @@ where
130
135
/// let input: TimestampMicrosecondArray =
131
136
/// vec![Some(1612025847000000), None, Some(1722015847000000)].into();
132
137
///
133
- /// let actual = date_part(&input, DatePart::Week).unwrap();
138
+ /// let week = date_part(&input, DatePart::Week).unwrap();
139
+ /// let week_iso = date_part(&input, DatePart::WeekISO).unwrap();
134
140
/// let expected: Int32Array = vec![Some(4), None, Some(30)].into();
135
- /// assert_eq!(actual.as_ref(), &expected);
141
+ /// assert_eq!(week.as_ref(), &expected);
142
+ /// assert_eq!(week_iso.as_ref(), &expected);
143
+ /// let year_iso = date_part(&input, DatePart::YearISO).unwrap();
144
+ /// let expected: Int32Array = vec![Some(2021), None, Some(2024)].into();
145
+ /// assert_eq!(year_iso.as_ref(), &expected);
136
146
/// ```
137
147
pub fn date_part ( array : & dyn Array , part : DatePart ) -> Result < ArrayRef , ArrowError > {
138
148
downcast_temporal_array ! (
@@ -430,6 +440,8 @@ impl ExtractDatePartExt for PrimitiveArray<IntervalYearMonthType> {
430
440
431
441
DatePart :: Quarter
432
442
| DatePart :: Week
443
+ | DatePart :: WeekISO
444
+ | DatePart :: YearISO
433
445
| DatePart :: Day
434
446
| DatePart :: DayOfWeekSunday0
435
447
| DatePart :: DayOfWeekMonday0
@@ -460,6 +472,8 @@ impl ExtractDatePartExt for PrimitiveArray<IntervalDayTimeType> {
460
472
461
473
DatePart :: Quarter
462
474
| DatePart :: Year
475
+ | DatePart :: YearISO
476
+ | DatePart :: WeekISO
463
477
| DatePart :: Month
464
478
| DatePart :: DayOfWeekSunday0
465
479
| DatePart :: DayOfWeekMonday0
@@ -495,6 +509,8 @@ impl ExtractDatePartExt for PrimitiveArray<IntervalMonthDayNanoType> {
495
509
DatePart :: Nanosecond => Ok ( self . unary_opt ( |d| d. nanoseconds . try_into ( ) . ok ( ) ) ) ,
496
510
497
511
DatePart :: Quarter
512
+ | DatePart :: WeekISO
513
+ | DatePart :: YearISO
498
514
| DatePart :: DayOfWeekSunday0
499
515
| DatePart :: DayOfWeekMonday0
500
516
| DatePart :: DayOfYear => {
@@ -523,6 +539,8 @@ impl ExtractDatePartExt for PrimitiveArray<DurationSecondType> {
523
539
) ,
524
540
525
541
DatePart :: Year
542
+ | DatePart :: YearISO
543
+ | DatePart :: WeekISO
526
544
| DatePart :: Quarter
527
545
| DatePart :: Month
528
546
| DatePart :: DayOfWeekSunday0
@@ -553,6 +571,8 @@ impl ExtractDatePartExt for PrimitiveArray<DurationMillisecondType> {
553
571
}
554
572
555
573
DatePart :: Year
574
+ | DatePart :: YearISO
575
+ | DatePart :: WeekISO
556
576
| DatePart :: Quarter
557
577
| DatePart :: Month
558
578
| DatePart :: DayOfWeekSunday0
@@ -583,6 +603,8 @@ impl ExtractDatePartExt for PrimitiveArray<DurationMicrosecondType> {
583
603
}
584
604
585
605
DatePart :: Year
606
+ | DatePart :: YearISO
607
+ | DatePart :: WeekISO
586
608
| DatePart :: Quarter
587
609
| DatePart :: Month
588
610
| DatePart :: DayOfWeekSunday0
@@ -613,6 +635,8 @@ impl ExtractDatePartExt for PrimitiveArray<DurationNanosecondType> {
613
635
DatePart :: Nanosecond => Ok ( self . unary_opt ( |d| d. try_into ( ) . ok ( ) ) ) ,
614
636
615
637
DatePart :: Year
638
+ | DatePart :: YearISO
639
+ | DatePart :: WeekISO
616
640
| DatePart :: Quarter
617
641
| DatePart :: Month
618
642
| DatePart :: DayOfWeekSunday0
@@ -2072,4 +2096,130 @@ mod tests {
2072
2096
ensure_returns_error ( & DurationMicrosecondArray :: from ( vec ! [ 0 ] ) ) ;
2073
2097
ensure_returns_error ( & DurationNanosecondArray :: from ( vec ! [ 0 ] ) ) ;
2074
2098
}
2099
+
2100
+ const TIMESTAMP_SECOND_1970_01_01 : i64 = 0 ;
2101
+ const TIMESTAMP_SECOND_2018_01_01 : i64 = 1_514_764_800 ;
2102
+ const TIMESTAMP_SECOND_2019_02_20 : i64 = 1_550_636_625 ;
2103
+ const SECONDS_IN_DAY : i64 = 24 * 60 * 60 ;
2104
+ // In 2018 the ISO year and calendar year start on the same date— 2018-01-01 or 2018-W01-1
2105
+ #[ test]
2106
+ fn test_temporal_array_date64_week_iso ( ) {
2107
+ let a: PrimitiveArray < Date64Type > = vec ! [
2108
+ Some ( TIMESTAMP_SECOND_2018_01_01 * 1000 ) ,
2109
+ Some ( TIMESTAMP_SECOND_2019_02_20 * 1000 ) ,
2110
+ ]
2111
+ . into ( ) ;
2112
+
2113
+ let b = date_part ( & a, DatePart :: WeekISO ) . unwrap ( ) ;
2114
+ let actual = b. as_primitive :: < Int32Type > ( ) ;
2115
+ assert_eq ! ( 1 , actual. value( 0 ) ) ;
2116
+ assert_eq ! ( 8 , actual. value( 1 ) ) ;
2117
+ }
2118
+
2119
+ #[ test]
2120
+ fn test_temporal_array_date64_year_iso ( ) {
2121
+ let a: PrimitiveArray < Date64Type > = vec ! [
2122
+ Some ( TIMESTAMP_SECOND_2018_01_01 * 1000 ) ,
2123
+ Some ( TIMESTAMP_SECOND_2019_02_20 * 1000 ) ,
2124
+ ]
2125
+ . into ( ) ;
2126
+
2127
+ let b = date_part ( & a, DatePart :: YearISO ) . unwrap ( ) ;
2128
+ let actual = b. as_primitive :: < Int32Type > ( ) ;
2129
+ assert_eq ! ( 2018 , actual. value( 0 ) ) ;
2130
+ assert_eq ! ( 2019 , actual. value( 1 ) ) ;
2131
+ }
2132
+
2133
+ #[ test]
2134
+ fn test_temporal_array_timestamp_week_iso ( ) {
2135
+ let a = TimestampSecondArray :: from ( vec ! [
2136
+ TIMESTAMP_SECOND_1970_01_01 , // 0 and is Thursday
2137
+ SECONDS_IN_DAY * 4 , // Monday of week 2
2138
+ SECONDS_IN_DAY * 4 - 1 , // Sunday of week 1
2139
+ ] ) ;
2140
+ let b = date_part ( & a, DatePart :: WeekISO ) . unwrap ( ) ;
2141
+ let actual = b. as_primitive :: < Int32Type > ( ) ;
2142
+ assert_eq ! ( 1 , actual. value( 0 ) ) ;
2143
+ assert_eq ! ( 2 , actual. value( 1 ) ) ;
2144
+ assert_eq ! ( 1 , actual. value( 2 ) ) ;
2145
+ }
2146
+
2147
+ #[ test]
2148
+ fn test_temporal_array_timestamp_year_iso ( ) {
2149
+ let a = TimestampSecondArray :: from ( vec ! [
2150
+ TIMESTAMP_SECOND_1970_01_01 ,
2151
+ SECONDS_IN_DAY * 4 ,
2152
+ SECONDS_IN_DAY * 4 - 1 ,
2153
+ ] ) ;
2154
+ let b = date_part ( & a, DatePart :: YearISO ) . unwrap ( ) ;
2155
+ let actual = b. as_primitive :: < Int32Type > ( ) ;
2156
+ assert_eq ! ( 1970 , actual. value( 0 ) ) ;
2157
+ assert_eq ! ( 1970 , actual. value( 1 ) ) ;
2158
+ assert_eq ! ( 1970 , actual. value( 2 ) ) ;
2159
+ }
2160
+
2161
+ const TIMESTAMP_SECOND_2015_12_28 : i64 = 1_451_260_800 ;
2162
+ const TIMESTAMP_SECOND_2016_01_03 : i64 = 1_451_779_200 ;
2163
+ // January 1st 2016 is a Friday, so 2015 week 53 runs from
2164
+ // 2015-12-28 to 2016-01-03 inclusive, and
2165
+ // 2016 week 1 runs from 2016-01-04 to 2016-01-10 inclusive.
2166
+ #[ test]
2167
+ fn test_temporal_array_date64_week_iso_edge_cases ( ) {
2168
+ let a: PrimitiveArray < Date64Type > = vec ! [
2169
+ Some ( TIMESTAMP_SECOND_2015_12_28 * 1000 ) ,
2170
+ Some ( TIMESTAMP_SECOND_2016_01_03 * 1000 ) ,
2171
+ Some ( ( TIMESTAMP_SECOND_2016_01_03 + SECONDS_IN_DAY ) * 1000 ) ,
2172
+ ]
2173
+ . into ( ) ;
2174
+
2175
+ let b = date_part ( & a, DatePart :: WeekISO ) . unwrap ( ) ;
2176
+ let actual = b. as_primitive :: < Int32Type > ( ) ;
2177
+ assert_eq ! ( 53 , actual. value( 0 ) ) ;
2178
+ assert_eq ! ( 53 , actual. value( 1 ) ) ;
2179
+ assert_eq ! ( 1 , actual. value( 2 ) ) ;
2180
+ }
2181
+
2182
+ #[ test]
2183
+ fn test_temporal_array_date64_year_iso_edge_cases ( ) {
2184
+ let a: PrimitiveArray < Date64Type > = vec ! [
2185
+ Some ( TIMESTAMP_SECOND_2015_12_28 * 1000 ) ,
2186
+ Some ( TIMESTAMP_SECOND_2016_01_03 * 1000 ) ,
2187
+ Some ( ( TIMESTAMP_SECOND_2016_01_03 + SECONDS_IN_DAY ) * 1000 ) ,
2188
+ ]
2189
+ . into ( ) ;
2190
+
2191
+ let b = date_part ( & a, DatePart :: YearISO ) . unwrap ( ) ;
2192
+ let actual = b. as_primitive :: < Int32Type > ( ) ;
2193
+ assert_eq ! ( 2015 , actual. value( 0 ) ) ;
2194
+ assert_eq ! ( 2015 , actual. value( 1 ) ) ;
2195
+ assert_eq ! ( 2016 , actual. value( 2 ) ) ;
2196
+ }
2197
+
2198
+ #[ test]
2199
+ fn test_temporal_array_timestamp_week_iso_edge_cases ( ) {
2200
+ let a = TimestampSecondArray :: from ( vec ! [
2201
+ TIMESTAMP_SECOND_2015_12_28 ,
2202
+ TIMESTAMP_SECOND_2016_01_03 ,
2203
+ TIMESTAMP_SECOND_2016_01_03 + SECONDS_IN_DAY ,
2204
+ ] ) ;
2205
+ let b = date_part ( & a, DatePart :: WeekISO ) . unwrap ( ) ;
2206
+ let actual = b. as_primitive :: < Int32Type > ( ) ;
2207
+ assert_eq ! ( 53 , actual. value( 0 ) ) ;
2208
+ assert_eq ! ( 53 , actual. value( 1 ) ) ;
2209
+ assert_eq ! ( 1 , actual. value( 2 ) ) ;
2210
+ }
2211
+
2212
+ #[ test]
2213
+ fn test_temporal_array_timestamp_year_iso_edge_cases ( ) {
2214
+ let a = TimestampSecondArray :: from ( vec ! [
2215
+ TIMESTAMP_SECOND_2015_12_28 ,
2216
+ TIMESTAMP_SECOND_2016_01_03 ,
2217
+ TIMESTAMP_SECOND_2016_01_03 + SECONDS_IN_DAY ,
2218
+ ] ) ;
2219
+ let b = date_part ( & a, DatePart :: YearISO ) . unwrap ( ) ;
2220
+ let actual = b. as_primitive :: < Int32Type > ( ) ;
2221
+ assert_eq ! ( 2015 , actual. value( 0 ) ) ;
2222
+ assert_eq ! ( 2015 , actual. value( 1 ) ) ;
2223
+ assert_eq ! ( 2016 , actual. value( 2 ) ) ;
2224
+ }
2075
2225
}
0 commit comments