## Coverage Report

#### Created: 2025-11-22 06:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /Users/andrewlamb/Software/datafusion/datafusion/functions/src/datetime/to_timestamp.rs

    Line
    Count
    Source
    1
    // Licensed to the Apache Software Foundation (ASF) under one
    2
    // or more contributor license agreements.  See the NOTICE file
    3
    // distributed with this work for additional information
    4
    // regarding copyright ownership.  The ASF licenses this file
    5
    // to you under the Apache License, Version 2.0 (the
    6
    // "License"); you may not use this file except in compliance
    7
    // with the License.  You may obtain a copy of the License at
    8
    //
    9
    //   http://www.apache.org/licenses/LICENSE-2.0
    10
    //
    11
    // Unless required by applicable law or agreed to in writing,
    12
    // software distributed under the License is distributed on an
    13
    // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14
    // KIND, either express or implied.  See the License for the
    15
    // specific language governing permissions and limitations
    16
    // under the License.
    17
    18
    use std::any::Any;
    19
    use std::sync::Arc;
    20
    21
    use crate::datetime::common::*;
    22
    use arrow::array::Float64Array;
    23
    use arrow::datatypes::DataType::*;
    24
    use arrow::datatypes::TimeUnit::{Microsecond, Millisecond, Nanosecond, Second};
    25
    use arrow::datatypes::{
    26
        ArrowTimestampType, DataType, TimeUnit, TimestampMicrosecondType,
    27
        TimestampMillisecondType, TimestampNanosecondType, TimestampSecondType,
    28
    };
    29
    use datafusion_common::format::DEFAULT_CAST_OPTIONS;
    30
    use datafusion_common::{exec_err, Result, ScalarType, ScalarValue};
    31
    use datafusion_expr::{
    32
        ColumnarValue, Documentation, ScalarUDFImpl, Signature, Volatility,
    33
    };
    34
    use datafusion_macros::user_doc;
    35
    36
    #[user_doc(
    37
        doc_section(label = "Time and Date Functions"),
    38
        description = r#"
    39
    Converts a value to a timestamp (`YYYY-MM-DDT00:00:00Z`). Supports strings, integer, unsigned integer, and double types as input. Strings are parsed as RFC3339 (e.g. '2023-07-20T05:44:00') if no [Chrono formats] are provided. Integers, unsigned integers, and doubles are interpreted as seconds since the unix epoch (`1970-01-01T00:00:00Z`). Returns the corresponding timestamp.
    40
    41
    Note: `to_timestamp` returns `Timestamp(ns)`. The supported range for integer input is between `-9223372037` and `9223372036`. Supported range for string input is between `1677-09-21T00:12:44.0` and `2262-04-11T23:47:16.0`. Please use `to_timestamp_seconds` for the input outside of supported bounds.
    42
    "#,
    43
        syntax_example = "to_timestamp(expression[, ..., format_n])",
    44
        sql_example = r#"```sql
    45
    > select to_timestamp('2023-01-31T09:26:56.123456789-05:00');
    46
    +-----------------------------------------------------------+
    47
    | to_timestamp(Utf8("2023-01-31T09:26:56.123456789-05:00")) |
    48
    +-----------------------------------------------------------+
    49
    | 2023-01-31T14:26:56.123456789                             |
    50
    +-----------------------------------------------------------+
    51
    > select to_timestamp('03:59:00.123456789 05-17-2023', '%c', '%+', '%H:%M:%S%.f %m-%d-%Y');
    52
    +--------------------------------------------------------------------------------------------------------+
    53
    | to_timestamp(Utf8("03:59:00.123456789 05-17-2023"),Utf8("%c"),Utf8("%+"),Utf8("%H:%M:%S%.f %m-%d-%Y")) |
    54
    +--------------------------------------------------------------------------------------------------------+
    55
    | 2023-05-17T03:59:00.123456789                                                                          |
    56
    +--------------------------------------------------------------------------------------------------------+
    57
    ```
    58
    Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/date_time_functions.rs)
    59
    "#,
    60
        argument(
    61
            name = "expression",
    62
            description = "Expression to operate on. Can be a constant, column, or function, and any combination of arithmetic operators."
    63
        ),
    64
        argument(
    65
            name = "format_n",
    66
            description = "Optional [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) strings to use to parse the expression. Formats will be tried in the order they appear with the first successful one being returned. If none of the formats successfully parse the expression an error will be returned."
    67
        )
    68
    )]
    69
    #[derive(Debug, PartialEq, Eq, Hash)]
    70
    pub struct ToTimestampFunc {
    71
        signature: Signature,
    72
    }
    73
    74
    #[user_doc(
    75
        doc_section(label = "Time and Date Functions"),
    76
        description = "Converts a value to a timestamp (`YYYY-MM-DDT00:00:00.000Z`). Supports strings, integer, and unsigned integer types as input. Strings are parsed as RFC3339 (e.g. '2023-07-20T05:44:00') if no [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html)s are provided. Integers and unsigned integers are interpreted as seconds since the unix epoch (`1970-01-01T00:00:00Z`). Returns the corresponding timestamp.",
    77
        syntax_example = "to_timestamp_seconds(expression[, ..., format_n])",
    78
        sql_example = r#"```sql
    79
    > select to_timestamp_seconds('2023-01-31T09:26:56.123456789-05:00');
    80
    +-------------------------------------------------------------------+
    81
    | to_timestamp_seconds(Utf8("2023-01-31T09:26:56.123456789-05:00")) |
    82
    +-------------------------------------------------------------------+
    83
    | 2023-01-31T14:26:56                                               |
    84
    +-------------------------------------------------------------------+
    85
    > select to_timestamp_seconds('03:59:00.123456789 05-17-2023', '%c', '%+', '%H:%M:%S%.f %m-%d-%Y');
    86
    +----------------------------------------------------------------------------------------------------------------+
    87
    | to_timestamp_seconds(Utf8("03:59:00.123456789 05-17-2023"),Utf8("%c"),Utf8("%+"),Utf8("%H:%M:%S%.f %m-%d-%Y")) |
    88
    +----------------------------------------------------------------------------------------------------------------+
    89
    | 2023-05-17T03:59:00                                                                                            |
    90
    +----------------------------------------------------------------------------------------------------------------+
    91
    ```
    92
    Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/date_time_functions.rs)
    93
    "#,
    94
        argument(
    95
            name = "expression",
    96
            description = "Expression to operate on. Can be a constant, column, or function, and any combination of arithmetic operators."
    97
        ),
    98
        argument(
    99
            name = "format_n",
    100
            description = "Optional [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) strings to use to parse the expression. Formats will be tried in the order they appear with the first successful one being returned. If none of the formats successfully parse the expression an error will be returned."
    101
        )
    102
    )]
    103
    #[derive(Debug, PartialEq, Eq, Hash)]
    104
    pub struct ToTimestampSecondsFunc {
    105
        signature: Signature,
    106
    }
    107
    108
    #[user_doc(
    109
        doc_section(label = "Time and Date Functions"),
    110
        description = "Converts a value to a timestamp (`YYYY-MM-DDT00:00:00.000Z`). Supports strings, integer, and unsigned integer types as input. Strings are parsed as RFC3339 (e.g. '2023-07-20T05:44:00') if no [Chrono formats](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) are provided. Integers and unsigned integers are interpreted as milliseconds since the unix epoch (`1970-01-01T00:00:00Z`). Returns the corresponding timestamp.",
    111
        syntax_example = "to_timestamp_millis(expression[, ..., format_n])",
    112
        sql_example = r#"```sql
    113
    > select to_timestamp_millis('2023-01-31T09:26:56.123456789-05:00');
    114
    +------------------------------------------------------------------+
    115
    | to_timestamp_millis(Utf8("2023-01-31T09:26:56.123456789-05:00")) |
    116
    +------------------------------------------------------------------+
    117
    | 2023-01-31T14:26:56.123                                          |
    118
    +------------------------------------------------------------------+
    119
    > select to_timestamp_millis('03:59:00.123456789 05-17-2023', '%c', '%+', '%H:%M:%S%.f %m-%d-%Y');
    120
    +---------------------------------------------------------------------------------------------------------------+
    121
    | to_timestamp_millis(Utf8("03:59:00.123456789 05-17-2023"),Utf8("%c"),Utf8("%+"),Utf8("%H:%M:%S%.f %m-%d-%Y")) |
    122
    +---------------------------------------------------------------------------------------------------------------+
    123
    | 2023-05-17T03:59:00.123                                                                                       |
    124
    +---------------------------------------------------------------------------------------------------------------+
    125
    ```
    126
    Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/date_time_functions.rs)
    127
    "#,
    128
        argument(
    129
            name = "expression",
    130
            description = "Expression to operate on. Can be a constant, column, or function, and any combination of arithmetic operators."
    131
        ),
    132
        argument(
    133
            name = "format_n",
    134
            description = "Optional [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) strings to use to parse the expression. Formats will be tried in the order they appear with the first successful one being returned. If none of the formats successfully parse the expression an error will be returned."
    135
        )
    136
    )]
    137
    #[derive(Debug, PartialEq, Eq, Hash)]
    138
    pub struct ToTimestampMillisFunc {
    139
        signature: Signature,
    140
    }
    141
    142
    #[user_doc(
    143
        doc_section(label = "Time and Date Functions"),
    144
        description = "Converts a value to a timestamp (`YYYY-MM-DDT00:00:00.000000Z`). Supports strings, integer, and unsigned integer types as input. Strings are parsed as RFC3339 (e.g. '2023-07-20T05:44:00') if no [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html)s are provided. Integers and unsigned integers are interpreted as microseconds since the unix epoch (`1970-01-01T00:00:00Z`) Returns the corresponding timestamp.",
    145
        syntax_example = "to_timestamp_micros(expression[, ..., format_n])",
    146
        sql_example = r#"```sql
    147
    > select to_timestamp_micros('2023-01-31T09:26:56.123456789-05:00');
    148
    +------------------------------------------------------------------+
    149
    | to_timestamp_micros(Utf8("2023-01-31T09:26:56.123456789-05:00")) |
    150
    +------------------------------------------------------------------+
    151
    | 2023-01-31T14:26:56.123456                                       |
    152
    +------------------------------------------------------------------+
    153
    > select to_timestamp_micros('03:59:00.123456789 05-17-2023', '%c', '%+', '%H:%M:%S%.f %m-%d-%Y');
    154
    +---------------------------------------------------------------------------------------------------------------+
    155
    | to_timestamp_micros(Utf8("03:59:00.123456789 05-17-2023"),Utf8("%c"),Utf8("%+"),Utf8("%H:%M:%S%.f %m-%d-%Y")) |
    156
    +---------------------------------------------------------------------------------------------------------------+
    157
    | 2023-05-17T03:59:00.123456                                                                                    |
    158
    +---------------------------------------------------------------------------------------------------------------+
    159
    ```
    160
    Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/date_time_functions.rs)
    161
    "#,
    162
        argument(
    163
            name = "expression",
    164
            description = "Expression to operate on. Can be a constant, column, or function, and any combination of arithmetic operators."
    165
        ),
    166
        argument(
    167
            name = "format_n",
    168
            description = "Optional [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) strings to use to parse the expression. Formats will be tried in the order they appear with the first successful one being returned. If none of the formats successfully parse the expression an error will be returned."
    169
        )
    170
    )]
    171
    #[derive(Debug, PartialEq, Eq, Hash)]
    172
    pub struct ToTimestampMicrosFunc {
    173
        signature: Signature,
    174
    }
    175
    176
    #[user_doc(
    177
        doc_section(label = "Time and Date Functions"),
    178
        description = "Converts a value to a timestamp (`YYYY-MM-DDT00:00:00.000000000Z`). Supports strings, integer, and unsigned integer types as input. Strings are parsed as RFC3339 (e.g. '2023-07-20T05:44:00') if no [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html)s are provided. Integers and unsigned integers are interpreted as nanoseconds since the unix epoch (`1970-01-01T00:00:00Z`). Returns the corresponding timestamp.",
    179
        syntax_example = "to_timestamp_nanos(expression[, ..., format_n])",
    180
        sql_example = r#"```sql
    181
    > select to_timestamp_nanos('2023-01-31T09:26:56.123456789-05:00');
    182
    +-----------------------------------------------------------------+
    183
    | to_timestamp_nanos(Utf8("2023-01-31T09:26:56.123456789-05:00")) |
    184
    +-----------------------------------------------------------------+
    185
    | 2023-01-31T14:26:56.123456789                                   |
    186
    +-----------------------------------------------------------------+
    187
    > select to_timestamp_nanos('03:59:00.123456789 05-17-2023', '%c', '%+', '%H:%M:%S%.f %m-%d-%Y');
    188
    +--------------------------------------------------------------------------------------------------------------+
    189
    | to_timestamp_nanos(Utf8("03:59:00.123456789 05-17-2023"),Utf8("%c"),Utf8("%+"),Utf8("%H:%M:%S%.f %m-%d-%Y")) |
    190
    +--------------------------------------------------------------------------------------------------------------+
    191
    | 2023-05-17T03:59:00.123456789                                                                                |
    192
    +---------------------------------------------------------------------------------------------------------------+
    193
    ```
    194
    Additional examples can be found [here](https://github.com/apache/datafusion/blob/main/datafusion-examples/examples/date_time_functions.rs)
    195
    "#,
    196
        argument(
    197
            name = "expression",
    198
            description = "Expression to operate on. Can be a constant, column, or function, and any combination of arithmetic operators."
    199
        ),
    200
        argument(
    201
            name = "format_n",
    202
            description = "Optional [Chrono format](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) strings to use to parse the expression. Formats will be tried in the order they appear with the first successful one being returned. If none of the formats successfully parse the expression an error will be returned."
    203
        )
    204
    )]
    205
    #[derive(Debug, PartialEq, Eq, Hash)]
    206
    pub struct ToTimestampNanosFunc {
    207
        signature: Signature,
    208
    }
    209
    210
    impl Default for ToTimestampFunc {
    211
    0
        fn default() -> Self {
    212
    0
            Self::new()
    213
    0
        }
    214
    }
    215
    216
    impl ToTimestampFunc {
    217
    0
        pub fn new() -> Self {
    218
    0
            Self {
    219
    0
                signature: Signature::variadic_any(Volatility::Immutable),
    220
    0
            }
    221
    0
        }
    222
    }
    223
    224
    impl Default for ToTimestampSecondsFunc {
    225
    0
        fn default() -> Self {
    226
    0
            Self::new()
    227
    0
        }
    228
    }
    229
    230
    impl ToTimestampSecondsFunc {
    231
    0
        pub fn new() -> Self {
    232
    0
            Self {
    233
    0
                signature: Signature::variadic_any(Volatility::Immutable),
    234
    0
            }
    235
    0
        }
    236
    }
    237
    238
    impl Default for ToTimestampMillisFunc {
    239
    0
        fn default() -> Self {
    240
    0
            Self::new()
    241
    0
        }
    242
    }
    243
    244
    impl ToTimestampMillisFunc {
    245
    0
        pub fn new() -> Self {
    246
    0
            Self {
    247
    0
                signature: Signature::variadic_any(Volatility::Immutable),
    248
    0
            }
    249
    0
        }
    250
    }
    251
    252
    impl Default for ToTimestampMicrosFunc {
    253
    0
        fn default() -> Self {
    254
    0
            Self::new()
    255
    0
        }
    256
    }
    257
    258
    impl ToTimestampMicrosFunc {
    259
    0
        pub fn new() -> Self {
    260
    0
            Self {
    261
    0
                signature: Signature::variadic_any(Volatility::Immutable),
    262
    0
            }
    263
    0
        }
    264
    }
    265
    266
    impl Default for ToTimestampNanosFunc {
    267
    0
        fn default() -> Self {
    268
    0
            Self::new()
    269
    0
        }
    270
    }
    271
    272
    impl ToTimestampNanosFunc {
    273
    0
        pub fn new() -> Self {
    274
    0
            Self {
    275
    0
                signature: Signature::variadic_any(Volatility::Immutable),
    276
    0
            }
    277
    0
        }
    278
    }
    279
    280
    /// to_timestamp SQL function
    281
    ///
    282
    /// Note: `to_timestamp` returns `Timestamp(Nanosecond)` though its arguments are interpreted as **seconds**.
    283
    /// The supported range for integer input is between `-9223372037` and `9223372036`.
    284
    /// Supported range for string input is between `1677-09-21T00:12:44.0` and `2262-04-11T23:47:16.0`.
    285
    /// Please use `to_timestamp_seconds` for the input outside of supported bounds.
    286
    impl ScalarUDFImpl for ToTimestampFunc {
    287
    0
        fn as_any(&self) -> &dyn Any {
    288
    0
            self
    289
    0
        }
    290
    291
    0
        fn name(&self) -> &str {
    292
    0
            "to_timestamp"
    293
    0
        }
    294
    295
    0
        fn signature(&self) -> &Signature {
    296
    0
            &self.signature
    297
    0
        }
    298
    299
    0
        fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
    300
    0
            Ok(return_type_for(&arg_types[0], Nanosecond))
    301
    0
        }
    302
    303
    0
        fn invoke_with_args(
    304
    0
            &self,
    305
    0
            args: datafusion_expr::ScalarFunctionArgs,
    306
    0
        ) -> Result<ColumnarValue> {
    307
    0
            let args = args.args;
    308
    0
            if args.is_empty() {
    309
    0
                return exec_err!(
    310
    0
                    "to_timestamp function requires 1 or more arguments, got {}",
    311
    0
                    args.len()
    312
                );
    313
    0
            }
    314
    315
            // validate that any args after the first one are Utf8
    316
    0
            if args.len() > 1 {
    317
    0
                validate_data_types(&args, "to_timestamp")?;
    318
    0
            }
    319
    320
    0
            match args[0].data_type() {
    321
    0
                Int32 | Int64 => args[0]
    322
    0
                    .cast_to(&Timestamp(Second, None), None)?
    323
    0
                    .cast_to(&Timestamp(Nanosecond, None), None),
    324
                Null | Timestamp(_, None) => {
    325
    0
                    args[0].cast_to(&Timestamp(Nanosecond, None), None)
    326
                }
    327
                Float64 => {
    328
    0
                    let rescaled = arrow::compute::kernels::numeric::mul(
    329
    0
                        &args[0].to_array(1)?,
    330
    0
                        &arrow::array::Scalar::new(Float64Array::from(vec![
    331
    0
                            1_000_000_000f64,
    332
    0
                        ])),
    333
    0
                    )?;
    334
    0
                    Ok(ColumnarValue::Array(arrow::compute::cast_with_options(
    335
    0
                        &rescaled,
    336
    0
                        &Timestamp(Nanosecond, None),
    337
    0
                        &DEFAULT_CAST_OPTIONS,
    338
    0
                    )?))
    339
                }
    340
    0
                Timestamp(_, Some(tz)) => {
    341
    0
                    args[0].cast_to(&Timestamp(Nanosecond, Some(tz)), None)
    342
                }
    343
                Utf8View | LargeUtf8 | Utf8 => {
    344
    0
                    to_timestamp_impl::<TimestampNanosecondType>(&args, "to_timestamp")
    345
                }
    346
                Decimal128(_, _) => {
    347
    0
                    match &args[0] {
    348
                        ColumnarValue::Scalar(ScalarValue::Decimal128(
    349
    0
                            Some(value),
    350
                            _,
    351
    0
                            scale,
    352
                        )) => {
    353
                            // Convert decimal to seconds and nanoseconds
    354
    0
                            let scale_factor = 10_i128.pow(*scale as u32);
    355
    0
                            let seconds = value / scale_factor;
    356
    0
                            let fraction = value % scale_factor;
    357
    358
    0
                            let nanos = (fraction * 1_000_000_000) / scale_factor;
    359
    360
    0
                            let timestamp_nanos = seconds * 1_000_000_000 + nanos;
    361
    362
    0
                            Ok(ColumnarValue::Scalar(ScalarValue::TimestampNanosecond(
    363
    0
                                Some(timestamp_nanos as i64),
    364
    0
                                None,
    365
    0
                            )))
    366
                        }
    367
    0
                        _ => exec_err!("Invalid decimal value"),
    368
                    }
    369
                }
    370
    0
                other => {
    371
    0
                    exec_err!("Unsupported data type {other} for function to_timestamp")
    372
                }
    373
            }
    374
    0
        }
    375
    0
        fn documentation(&self) -> Option<&Documentation> {
    376
    0
            self.doc()
    377
    0
        }
    378
    }
    379
    380
    impl ScalarUDFImpl for ToTimestampSecondsFunc {
    381
    0
        fn as_any(&self) -> &dyn Any {
    382
    0
            self
    383
    0
        }
    384
    385
    0
        fn name(&self) -> &str {
    386
    0
            "to_timestamp_seconds"
    387
    0
        }
    388
    389
    0
        fn signature(&self) -> &Signature {
    390
    0
            &self.signature
    391
    0
        }
    392
    393
    0
        fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
    394
    0
            Ok(return_type_for(&arg_types[0], Second))
    395
    0
        }
    396
    397
    0
        fn invoke_with_args(
    398
    0
            &self,
    399
    0
            args: datafusion_expr::ScalarFunctionArgs,
    400
    0
        ) -> Result<ColumnarValue> {
    401
    0
            let args = args.args;
    402
    0
            if args.is_empty() {
    403
    0
                return exec_err!(
    404
    0
                    "to_timestamp_seconds function requires 1 or more arguments, got {}",
    405
    0
                    args.len()
    406
                );
    407
    0
            }
    408
    409
            // validate that any args after the first one are Utf8
    410
    0
            if args.len() > 1 {
    411
    0
                validate_data_types(&args, "to_timestamp")?;
    412
    0
            }
    413
    414
    0
            match args[0].data_type() {
    415
                Null | Int32 | Int64 | Timestamp(_, None) | Decimal128(_, _) => {
    416
    0
                    args[0].cast_to(&Timestamp(Second, None), None)
    417
                }
    418
    0
                Timestamp(_, Some(tz)) => args[0].cast_to(&Timestamp(Second, Some(tz)), None),
    419
                Utf8View | LargeUtf8 | Utf8 => {
    420
    0
                    to_timestamp_impl::<TimestampSecondType>(&args, "to_timestamp_seconds")
    421
                }
    422
    0
                other => {
    423
    0
                    exec_err!(
    424
    0
                        "Unsupported data type {} for function to_timestamp_seconds",
    425
                        other
    426
                    )
    427
                }
    428
            }
    429
    0
        }
    430
    0
        fn documentation(&self) -> Option<&Documentation> {
    431
    0
            self.doc()
    432
    0
        }
    433
    }
    434
    435
    impl ScalarUDFImpl for ToTimestampMillisFunc {
    436
    0
        fn as_any(&self) -> &dyn Any {
    437
    0
            self
    438
    0
        }
    439
    440
    0
        fn name(&self) -> &str {
    441
    0
            "to_timestamp_millis"
    442
    0
        }
    443
    444
    0
        fn signature(&self) -> &Signature {
    445
    0
            &self.signature
    446
    0
        }
    447
    448
    0
        fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
    449
    0
            Ok(return_type_for(&arg_types[0], Millisecond))
    450
    0
        }
    451
    452
    0
        fn invoke_with_args(
    453
    0
            &self,
    454
    0
            args: datafusion_expr::ScalarFunctionArgs,
    455
    0
        ) -> Result<ColumnarValue> {
    456
    0
            let args = args.args;
    457
    0
            if args.is_empty() {
    458
    0
                return exec_err!(
    459
    0
                    "to_timestamp_millis function requires 1 or more arguments, got {}",
    460
    0
                    args.len()
    461
                );
    462
    0
            }
    463
    464
            // validate that any args after the first one are Utf8
    465
    0
            if args.len() > 1 {
    466
    0
                validate_data_types(&args, "to_timestamp")?;
    467
    0
            }
    468
    469
    0
            match args[0].data_type() {
    470
                Null | Int32 | Int64 | Timestamp(_, None) => {
    471
    0
                    args[0].cast_to(&Timestamp(Millisecond, None), None)
    472
                }
    473
    0
                Timestamp(_, Some(tz)) => {
    474
    0
                    args[0].cast_to(&Timestamp(Millisecond, Some(tz)), None)
    475
                }
    476
    0
                Utf8View | LargeUtf8 | Utf8 => to_timestamp_impl::<TimestampMillisecondType>(
    477
    0
                    &args,
    478
    0
                    "to_timestamp_millis",
    479
                ),
    480
    0
                other => {
    481
    0
                    exec_err!(
    482
    0
                        "Unsupported data type {} for function to_timestamp_millis",
    483
                        other
    484
                    )
    485
                }
    486
            }
    487
    0
        }
    488
    0
        fn documentation(&self) -> Option<&Documentation> {
    489
    0
            self.doc()
    490
    0
        }
    491
    }
    492
    493
    impl ScalarUDFImpl for ToTimestampMicrosFunc {
    494
    0
        fn as_any(&self) -> &dyn Any {
    495
    0
            self
    496
    0
        }
    497
    498
    0
        fn name(&self) -> &str {
    499
    0
            "to_timestamp_micros"
    500
    0
        }
    501
    502
    0
        fn signature(&self) -> &Signature {
    503
    0
            &self.signature
    504
    0
        }
    505
    506
    0
        fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
    507
    0
            Ok(return_type_for(&arg_types[0], Microsecond))
    508
    0
        }
    509
    510
    0
        fn invoke_with_args(
    511
    0
            &self,
    512
    0
            args: datafusion_expr::ScalarFunctionArgs,
    513
    0
        ) -> Result<ColumnarValue> {
    514
    0
            let args = args.args;
    515
    0
            if args.is_empty() {
    516
    0
                return exec_err!(
    517
    0
                    "to_timestamp_micros function requires 1 or more arguments, got {}",
    518
    0
                    args.len()
    519
                );
    520
    0
            }
    521
    522
            // validate that any args after the first one are Utf8
    523
    0
            if args.len() > 1 {
    524
    0
                validate_data_types(&args, "to_timestamp")?;
    525
    0
            }
    526
    527
    0
            match args[0].data_type() {
    528
                Null | Int32 | Int64 | Timestamp(_, None) => {
    529
    0
                    args[0].cast_to(&Timestamp(Microsecond, None), None)
    530
                }
    531
    0
                Timestamp(_, Some(tz)) => {
    532
    0
                    args[0].cast_to(&Timestamp(Microsecond, Some(tz)), None)
    533
                }
    534
    0
                Utf8View | LargeUtf8 | Utf8 => to_timestamp_impl::<TimestampMicrosecondType>(
    535
    0
                    &args,
    536
    0
                    "to_timestamp_micros",
    537
                ),
    538
    0
                other => {
    539
    0
                    exec_err!(
    540
    0
                        "Unsupported data type {} for function to_timestamp_micros",
    541
                        other
    542
                    )
    543
                }
    544
            }
    545
    0
        }
    546
    0
        fn documentation(&self) -> Option<&Documentation> {
    547
    0
            self.doc()
    548
    0
        }
    549
    }
    550
    551
    impl ScalarUDFImpl for ToTimestampNanosFunc {
    552
    0
        fn as_any(&self) -> &dyn Any {
    553
    0
            self
    554
    0
        }
    555
    556
    0
        fn name(&self) -> &str {
    557
    0
            "to_timestamp_nanos"
    558
    0
        }
    559
    560
    0
        fn signature(&self) -> &Signature {
    561
    0
            &self.signature
    562
    0
        }
    563
    564
    0
        fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
    565
    0
            Ok(return_type_for(&arg_types[0], Nanosecond))
    566
    0
        }
    567
    568
    0
        fn invoke_with_args(
    569
    0
            &self,
    570
    0
            args: datafusion_expr::ScalarFunctionArgs,
    571
    0
        ) -> Result<ColumnarValue> {
    572
    0
            let args = args.args;
    573
    0
            if args.is_empty() {
    574
    0
                return exec_err!(
    575
    0
                    "to_timestamp_nanos function requires 1 or more arguments, got {}",
    576
    0
                    args.len()
    577
                );
    578
    0
            }
    579
    580
            // validate that any args after the first one are Utf8
    581
    0
            if args.len() > 1 {
    582
    0
                validate_data_types(&args, "to_timestamp")?;
    583
    0
            }
    584
    585
    0
            match args[0].data_type() {
    586
                Null | Int32 | Int64 | Timestamp(_, None) => {
    587
    0
                    args[0].cast_to(&Timestamp(Nanosecond, None), None)
    588
                }
    589
    0
                Timestamp(_, Some(tz)) => {
    590
    0
                    args[0].cast_to(&Timestamp(Nanosecond, Some(tz)), None)
    591
                }
    592
                Utf8View | LargeUtf8 | Utf8 => {
    593
    0
                    to_timestamp_impl::<TimestampNanosecondType>(&args, "to_timestamp_nanos")
    594
                }
    595
    0
                other => {
    596
    0
                    exec_err!(
    597
    0
                        "Unsupported data type {} for function to_timestamp_nanos",
    598
                        other
    599
                    )
    600
                }
    601
            }
    602
    0
        }
    603
    0
        fn documentation(&self) -> Option<&Documentation> {
    604
    0
            self.doc()
    605
    0
        }
    606
    }
    607
    608
    /// Returns the return type for the to_timestamp_* function, preserving
    609
    /// the timezone if it exists.
    610
    0
    fn return_type_for(arg: &DataType, unit: TimeUnit) -> DataType {
    611
    0
        match arg {
    612
    0
            Timestamp(_, Some(tz)) => Timestamp(unit, Some(Arc::clone(tz))),
    613
    0
            _ => Timestamp(unit, None),
    614
        }
    615
    0
    }
    616
    617
    0
    fn to_timestamp_impl<T: ArrowTimestampType + ScalarType<i64>>(
    618
    0
        args: &[ColumnarValue],
    619
    0
        name: &str,
    620
    0
    ) -> Result<ColumnarValue> {
    621
    0
        let factor = match T::UNIT {
    622
    0
            Second => 1_000_000_000,
    623
    0
            Millisecond => 1_000_000,
    624
    0
            Microsecond => 1_000,
    625
    0
            Nanosecond => 1,
    626
        };
    627
    628
    0
        match args.len() {
    629
    0
            1 => handle::<T, _, T>(
    630
    0
                args,
    631
    0
                |s| string_to_timestamp_nanos_shim(s).map(|n| n / factor),
    632
    0
                name,
    633
            ),
    634
    0
            n if n >= 2 => handle_multiple::<T, _, T, _>(
    635
    0
                args,
    636
                string_to_timestamp_nanos_formatted,
    637
    0
                |n| n / factor,
    638
    0
                name,
    639
            ),
    640
    0
            _ => exec_err!("Unsupported 0 argument count for function {name}"),
    641
        }
    642
    0
    }
    643
    644
    #[cfg(test)]
    645
    mod tests {
    646
        use std::sync::Arc;
    647
    648
        use arrow::array::types::Int64Type;
    649
        use arrow::array::{
    650
            Array, PrimitiveArray, TimestampMicrosecondArray, TimestampMillisecondArray,
    651
            TimestampNanosecondArray, TimestampSecondArray,
    652
        };
    653
        use arrow::array::{ArrayRef, Int64Array, StringBuilder};
    654
        use arrow::datatypes::{Field, TimeUnit};
    655
        use chrono::Utc;
    656
        use datafusion_common::config::ConfigOptions;
    657
        use datafusion_common::{assert_contains, DataFusionError, ScalarValue};
    658
        use datafusion_expr::ScalarFunctionImplementation;
    659
    660
        use super::*;
    661
    662
        fn to_timestamp(args: &[ColumnarValue]) -> Result<ColumnarValue> {
    663
            to_timestamp_impl::<TimestampNanosecondType>(args, "to_timestamp")
    664
        }
    665
    666
        /// to_timestamp_millis SQL function
    667
        fn to_timestamp_millis(args: &[ColumnarValue]) -> Result<ColumnarValue> {
    668
            to_timestamp_impl::<TimestampMillisecondType>(args, "to_timestamp_millis")
    669
        }
    670
    671
        /// to_timestamp_micros SQL function
    672
        fn to_timestamp_micros(args: &[ColumnarValue]) -> Result<ColumnarValue> {
    673
            to_timestamp_impl::<TimestampMicrosecondType>(args, "to_timestamp_micros")
    674
        }
    675
    676
        /// to_timestamp_nanos SQL function
    677
        fn to_timestamp_nanos(args: &[ColumnarValue]) -> Result<ColumnarValue> {
    678
            to_timestamp_impl::<TimestampNanosecondType>(args, "to_timestamp_nanos")
    679
        }
    680
    681
        /// to_timestamp_seconds SQL function
    682
        fn to_timestamp_seconds(args: &[ColumnarValue]) -> Result<ColumnarValue> {
    683
            to_timestamp_impl::<TimestampSecondType>(args, "to_timestamp_seconds")
    684
        }
    685
    686
        #[test]
    687
        fn to_timestamp_arrays_and_nulls() -> Result<()> {
    688
            // ensure that arrow array implementation is wired up and handles nulls correctly
    689
    690
            let mut string_builder = StringBuilder::with_capacity(2, 1024);
    691
            let mut ts_builder = TimestampNanosecondArray::builder(2);
    692
    693
            string_builder.append_value("2020-09-08T13:42:29.190855");
    694
            ts_builder.append_value(1599572549190855000);
    695
    696
            string_builder.append_null();
    697
            ts_builder.append_null();
    698
            let expected_timestamps = &ts_builder.finish() as &dyn Array;
    699
    700
            let string_array =
    701
                ColumnarValue::Array(Arc::new(string_builder.finish()) as ArrayRef);
    702
            let parsed_timestamps = to_timestamp(&[string_array])
    703
                .expect("that to_timestamp parsed values without error");
    704
            if let ColumnarValue::Array(parsed_array) = parsed_timestamps {
    705
                assert_eq!(parsed_array.len(), 2);
    706
                assert_eq!(expected_timestamps, parsed_array.as_ref());
    707
            } else {
    708
                panic!("Expected a columnar array")
    709
            }
    710
            Ok(())
    711
        }
    712
    713
        #[test]
    714
        fn to_timestamp_with_formats_arrays_and_nulls() -> Result<()> {
    715
            // ensure that arrow array implementation is wired up and handles nulls correctly
    716
    717
            let mut date_string_builder = StringBuilder::with_capacity(2, 1024);
    718
            let mut format1_builder = StringBuilder::with_capacity(2, 1024);
    719
            let mut format2_builder = StringBuilder::with_capacity(2, 1024);
    720
            let mut format3_builder = StringBuilder::with_capacity(2, 1024);
    721
            let mut ts_builder = TimestampNanosecondArray::builder(2);
    722
    723
            date_string_builder.append_null();
    724
            format1_builder.append_null();
    725
            format2_builder.append_null();
    726
            format3_builder.append_null();
    727
            ts_builder.append_null();
    728
    729
            date_string_builder.append_value("2020-09-08T13:42:29.19085Z");
    730
            format1_builder.append_value("%s");
    731
            format2_builder.append_value("%c");
    732
            format3_builder.append_value("%+");
    733
            ts_builder.append_value(1599572549190850000);
    734
    735
            let expected_timestamps = &ts_builder.finish() as &dyn Array;
    736
    737
            let string_array = [
    738
                ColumnarValue::Array(Arc::new(date_string_builder.finish()) as ArrayRef),
    739
                ColumnarValue::Array(Arc::new(format1_builder.finish()) as ArrayRef),
    740
                ColumnarValue::Array(Arc::new(format2_builder.finish()) as ArrayRef),
    741
                ColumnarValue::Array(Arc::new(format3_builder.finish()) as ArrayRef),
    742
            ];
    743
            let parsed_timestamps = to_timestamp(&string_array)
    744
                .expect("that to_timestamp with format args parsed values without error");
    745
            if let ColumnarValue::Array(parsed_array) = parsed_timestamps {
    746
                assert_eq!(parsed_array.len(), 2);
    747
                assert_eq!(expected_timestamps, parsed_array.as_ref());
    748
            } else {
    749
                panic!("Expected a columnar array")
    750
            }
    751
            Ok(())
    752
        }
    753
    754
        #[test]
    755
        fn to_timestamp_invalid_input_type() -> Result<()> {
    756
            // pass the wrong type of input array to to_timestamp and test
    757
            // that we get an error.
    758
    759
            let mut builder = Int64Array::builder(1);
    760
            builder.append_value(1);
    761
            let int64array = ColumnarValue::Array(Arc::new(builder.finish()));
    762
    763
            let expected_err =
    764
                "Execution error: Unsupported data type Int64 for function to_timestamp";
    765
            match to_timestamp(&[int64array]) {
    766
                Ok(_) => panic!("Expected error but got success"),
    767
                Err(e) => {
    768
                    assert!(
    769
                        e.to_string().contains(expected_err),
    770
                        "Can not find expected error '{expected_err}'. Actual error '{e}'"
    771
                    );
    772
                }
    773
            }
    774
            Ok(())
    775
        }
    776
    777
        #[test]
    778
        fn to_timestamp_with_formats_invalid_input_type() -> Result<()> {
    779
            // pass the wrong type of input array to to_timestamp and test
    780
            // that we get an error.
    781
    782
            let mut builder = Int64Array::builder(1);
    783
            builder.append_value(1);
    784
            let int64array = [
    785
                ColumnarValue::Array(Arc::new(builder.finish())),
    786
                ColumnarValue::Array(Arc::new(builder.finish())),
    787
            ];
    788
    789
            let expected_err =
    790
                "Execution error: Unsupported data type Int64 for function to_timestamp";
    791
            match to_timestamp(&int64array) {
    792
                Ok(_) => panic!("Expected error but got success"),
    793
                Err(e) => {
    794
                    assert!(
    795
                        e.to_string().contains(expected_err),
    796
                        "Can not find expected error '{expected_err}'. Actual error '{e}'"
    797
                    );
    798
                }
    799
            }
    800
            Ok(())
    801
        }
    802
    803
        #[test]
    804
        fn to_timestamp_with_unparsable_data() -> Result<()> {
    805
            let mut date_string_builder = StringBuilder::with_capacity(2, 1024);
    806
    807
            date_string_builder.append_null();
    808
    809
            date_string_builder.append_value("2020-09-08 - 13:42:29.19085Z");
    810
    811
            let string_array =
    812
                ColumnarValue::Array(Arc::new(date_string_builder.finish()) as ArrayRef);
    813
    814
            let expected_err =
    815
                "Arrow error: Parser error: Error parsing timestamp from '2020-09-08 - 13:42:29.19085Z': error parsing time";
    816
            match to_timestamp(&[string_array]) {
    817
                Ok(_) => panic!("Expected error but got success"),
    818
                Err(e) => {
    819
                    assert!(
    820
                        e.to_string().contains(expected_err),
    821
                        "Can not find expected error '{expected_err}'. Actual error '{e}'"
    822
                    );
    823
                }
    824
            }
    825
            Ok(())
    826
        }
    827
    828
        #[test]
    829
        fn to_timestamp_with_invalid_tz() -> Result<()> {
    830
            let mut date_string_builder = StringBuilder::with_capacity(2, 1024);
    831
    832
            date_string_builder.append_null();
    833
    834
            date_string_builder.append_value("2020-09-08T13:42:29ZZ");
    835
    836
            let string_array =
    837
                ColumnarValue::Array(Arc::new(date_string_builder.finish()) as ArrayRef);
    838
    839
            let expected_err =
    840
                "Arrow error: Parser error: Invalid timezone \"ZZ\": failed to parse timezone";
    841
            match to_timestamp(&[string_array]) {
    842
                Ok(_) => panic!("Expected error but got success"),
    843
                Err(e) => {
    844
                    assert!(
    845
                        e.to_string().contains(expected_err),
    846
                        "Can not find expected error '{expected_err}'. Actual error '{e}'"
    847
                    );
    848
                }
    849
            }
    850
            Ok(())
    851
        }
    852
    853
        #[test]
    854
        fn to_timestamp_with_no_matching_formats() -> Result<()> {
    855
            let mut date_string_builder = StringBuilder::with_capacity(2, 1024);
    856
            let mut format1_builder = StringBuilder::with_capacity(2, 1024);
    857
            let mut format2_builder = StringBuilder::with_capacity(2, 1024);
    858
            let mut format3_builder = StringBuilder::with_capacity(2, 1024);
    859
    860
            date_string_builder.append_null();
    861
            format1_builder.append_null();
    862
            format2_builder.append_null();
    863
            format3_builder.append_null();
    864
    865
            date_string_builder.append_value("2020-09-08T13:42:29.19085Z");
    866
            format1_builder.append_value("%s");
    867
            format2_builder.append_value("%c");
    868
            format3_builder.append_value("%H:%M:%S");
    869
    870
            let string_array = [
    871
                ColumnarValue::Array(Arc::new(date_string_builder.finish()) as ArrayRef),
    872
                ColumnarValue::Array(Arc::new(format1_builder.finish()) as ArrayRef),
    873
                ColumnarValue::Array(Arc::new(format2_builder.finish()) as ArrayRef),
    874
                ColumnarValue::Array(Arc::new(format3_builder.finish()) as ArrayRef),
    875
            ];
    876
    877
            let expected_err =
    878
                "Execution error: Error parsing timestamp from '2020-09-08T13:42:29.19085Z' using format '%H:%M:%S': input contains invalid characters";
    879
            match to_timestamp(&string_array) {
    880
                Ok(_) => panic!("Expected error but got success"),
    881
                Err(e) => {
    882
                    assert!(
    883
                        e.to_string().contains(expected_err),
    884
                        "Can not find expected error '{expected_err}'. Actual error '{e}'"
    885
                    );
    886
                }
    887
            }
    888
            Ok(())
    889
        }
    890
    891
        #[test]
    892
        fn string_to_timestamp_formatted() {
    893
            // Explicit timezone
    894
            assert_eq!(
    895
                1599572549190855000,
    896
                parse_timestamp_formatted("2020-09-08T13:42:29.190855+00:00", "%+").unwrap()
    897
            );
    898
            assert_eq!(
    899
                1599572549190855000,
    900
                parse_timestamp_formatted("2020-09-08T13:42:29.190855Z", "%+").unwrap()
    901
            );
    902
            assert_eq!(
    903
                1599572549000000000,
    904
                parse_timestamp_formatted("2020-09-08T13:42:29Z", "%+").unwrap()
    905
            ); // no fractional part
    906
            assert_eq!(
    907
                1599590549190855000,
    908
                parse_timestamp_formatted("2020-09-08T13:42:29.190855-05:00", "%+").unwrap()
    909
            );
    910
            assert_eq!(
    911
                1599590549000000000,
    912
                parse_timestamp_formatted("1599590549", "%s").unwrap()
    913
            );
    914
            assert_eq!(
    915
                1599572549000000000,
    916
                parse_timestamp_formatted("09-08-2020 13/42/29", "%m-%d-%Y %H/%M/%S")
    917
                    .unwrap()
    918
            );
    919
            assert_eq!(
    920
                1642896000000000000,
    921
                parse_timestamp_formatted("2022-01-23", "%Y-%m-%d").unwrap()
    922
            );
    923
        }
    924
    925
        fn parse_timestamp_formatted(s: &str, format: &str) -> Result<i64, DataFusionError> {
    926
            let result = string_to_timestamp_nanos_formatted(s, format);
    927
            if let Err(e) = &result {
    928
                eprintln!("Error parsing timestamp '{s}' using format '{format}': {e:?}");
    929
            }
    930
            result
    931
        }
    932
    933
        #[test]
    934
        fn string_to_timestamp_formatted_invalid() {
    935
            // Test parsing invalid formats
    936
            let cases = [
    937
                ("", "%Y%m%d %H%M%S", "premature end of input"),
    938
                ("SS", "%c", "premature end of input"),
    939
                ("Wed, 18 Feb 2015 23:16:09 GMT", "", "trailing input"),
    940
                (
    941
                    "Wed, 18 Feb 2015 23:16:09 GMT",
    942
                    "%XX",
    943
                    "input contains invalid characters",
    944
                ),
    945
                (
    946
                    "Wed, 18 Feb 2015 23:16:09 GMT",
    947
                    "%Y%m%d %H%M%S",
    948
                    "input contains invalid characters",
    949
                ),
    950
            ];
    951
    952
            for (s, f, ctx) in cases {
    953
                let expected = format!("Execution error: Error parsing timestamp from '{s}' using format '{f}': {ctx}");
    954
                let actual = string_to_datetime_formatted(&Utc, s, f)
    955
                    .unwrap_err()
    956
                    .strip_backtrace();
    957
                assert_eq!(actual, expected)
    958
            }
    959
        }
    960
    961
        #[test]
    962
        fn string_to_timestamp_invalid_arguments() {
    963
            // Test parsing invalid formats
    964
            let cases = [
    965
                ("", "%Y%m%d %H%M%S", "premature end of input"),
    966
                ("SS", "%c", "premature end of input"),
    967
                ("Wed, 18 Feb 2015 23:16:09 GMT", "", "trailing input"),
    968
                (
    969
                    "Wed, 18 Feb 2015 23:16:09 GMT",
    970
                    "%XX",
    971
                    "input contains invalid characters",
    972
                ),
    973
                (
    974
                    "Wed, 18 Feb 2015 23:16:09 GMT",
    975
                    "%Y%m%d %H%M%S",
    976
                    "input contains invalid characters",
    977
                ),
    978
            ];
    979
    980
            for (s, f, ctx) in cases {
    981
                let expected = format!("Execution error: Error parsing timestamp from '{s}' using format '{f}': {ctx}");
    982
                let actual = string_to_datetime_formatted(&Utc, s, f)
    983
                    .unwrap_err()
    984
                    .strip_backtrace();
    985
                assert_eq!(actual, expected)
    986
            }
    987
        }
    988
    989
        #[test]
    990
        fn test_tz() {
    991
            let udfs: Vec<Box<dyn ScalarUDFImpl>> = vec![
    992
                Box::new(ToTimestampFunc::new()),
    993
                Box::new(ToTimestampSecondsFunc::new()),
    994
                Box::new(ToTimestampMillisFunc::new()),
    995
                Box::new(ToTimestampNanosFunc::new()),
    996
                Box::new(ToTimestampSecondsFunc::new()),
    997
            ];
    998
    999
            let mut nanos_builder = TimestampNanosecondArray::builder(2);
    1000
            let mut millis_builder = TimestampMillisecondArray::builder(2);
    1001
            let mut micros_builder = TimestampMicrosecondArray::builder(2);
    1002
            let mut sec_builder = TimestampSecondArray::builder(2);
    1003
    1004
            nanos_builder.append_value(1599572549190850000);
    1005
            millis_builder.append_value(1599572549190);
    1006
            micros_builder.append_value(1599572549190850);
    1007
            sec_builder.append_value(1599572549);
    1008
    1009
            let nanos_timestamps =
    1010
                Arc::new(nanos_builder.finish().with_timezone("UTC")) as ArrayRef;
    1011
            let millis_timestamps =
    1012
                Arc::new(millis_builder.finish().with_timezone("UTC")) as ArrayRef;
    1013
            let micros_timestamps =
    1014
                Arc::new(micros_builder.finish().with_timezone("UTC")) as ArrayRef;
    1015
            let sec_timestamps =
    1016
                Arc::new(sec_builder.finish().with_timezone("UTC")) as ArrayRef;
    1017
    1018
            let arrays = &[
    1019
                ColumnarValue::Array(Arc::clone(&nanos_timestamps)),
    1020
                ColumnarValue::Array(Arc::clone(&millis_timestamps)),
    1021
                ColumnarValue::Array(Arc::clone(&micros_timestamps)),
    1022
                ColumnarValue::Array(Arc::clone(&sec_timestamps)),
    1023
            ];
    1024
    1025
            for udf in &udfs {
    1026
                for array in arrays {
    1027
                    let rt = udf.return_type(&[array.data_type()]).unwrap();
    1028
                    let arg_field = Field::new("arg", array.data_type().clone(), true).into();
    1029
                    assert!(matches!(rt, Timestamp(_, Some(_))));
    1030
                    let args = datafusion_expr::ScalarFunctionArgs {
    1031
                        args: vec![array.clone()],
    1032
                        arg_fields: vec![arg_field],
    1033
                        number_rows: 4,
    1034
                        return_field: Field::new("f", rt, true).into(),
    1035
                        config_options: Arc::new(ConfigOptions::default()),
    1036
                    };
    1037
                    let res = udf
    1038
                        .invoke_with_args(args)
    1039
                        .expect("that to_timestamp parsed values without error");
    1040
                    let array = match res {
    1041
                        ColumnarValue::Array(res) => res,
    1042
                        _ => panic!("Expected a columnar array"),
    1043
                    };
    1044
                    let ty = array.data_type();
    1045
                    assert!(matches!(ty, Timestamp(_, Some(_))));
    1046
                }
    1047
            }
    1048
    1049
            let mut nanos_builder = TimestampNanosecondArray::builder(2);
    1050
            let mut millis_builder = TimestampMillisecondArray::builder(2);
    1051
            let mut micros_builder = TimestampMicrosecondArray::builder(2);
    1052
            let mut sec_builder = TimestampSecondArray::builder(2);
    1053
            let mut i64_builder = Int64Array::builder(2);
    1054
    1055
            nanos_builder.append_value(1599572549190850000);
    1056
            millis_builder.append_value(1599572549190);
    1057
            micros_builder.append_value(1599572549190850);
    1058
            sec_builder.append_value(1599572549);
    1059
            i64_builder.append_value(1599572549);
    1060
    1061
            let nanos_timestamps = Arc::new(nanos_builder.finish()) as ArrayRef;
    1062
            let millis_timestamps = Arc::new(millis_builder.finish()) as ArrayRef;
    1063
            let micros_timestamps = Arc::new(micros_builder.finish()) as ArrayRef;
    1064
            let sec_timestamps = Arc::new(sec_builder.finish()) as ArrayRef;
    1065
            let i64_timestamps = Arc::new(i64_builder.finish()) as ArrayRef;
    1066
    1067
            let arrays = &[
    1068
                ColumnarValue::Array(Arc::clone(&nanos_timestamps)),
    1069
                ColumnarValue::Array(Arc::clone(&millis_timestamps)),
    1070
                ColumnarValue::Array(Arc::clone(&micros_timestamps)),
    1071
                ColumnarValue::Array(Arc::clone(&sec_timestamps)),
    1072
                ColumnarValue::Array(Arc::clone(&i64_timestamps)),
    1073
            ];
    1074
    1075
            for udf in &udfs {
    1076
                for array in arrays {
    1077
                    let rt = udf.return_type(&[array.data_type()]).unwrap();
    1078
                    assert!(matches!(rt, Timestamp(_, None)));
    1079
                    let arg_field = Field::new("arg", array.data_type().clone(), true).into();
    1080
                    let args = datafusion_expr::ScalarFunctionArgs {
    1081
                        args: vec![array.clone()],
    1082
                        arg_fields: vec![arg_field],
    1083
                        number_rows: 5,
    1084
                        return_field: Field::new("f", rt, true).into(),
    1085
                        config_options: Arc::new(ConfigOptions::default()),
    1086
                    };
    1087
                    let res = udf
    1088
                        .invoke_with_args(args)
    1089
                        .expect("that to_timestamp parsed values without error");
    1090
                    let array = match res {
    1091
                        ColumnarValue::Array(res) => res,
    1092
                        _ => panic!("Expected a columnar array"),
    1093
                    };
    1094
                    let ty = array.data_type();
    1095
                    assert!(matches!(ty, Timestamp(_, None)));
    1096
                }
    1097
            }
    1098
        }
    1099
    1100
        #[test]
    1101
        fn test_to_timestamp_arg_validation() {
    1102
            let mut date_string_builder = StringBuilder::with_capacity(2, 1024);
    1103
            date_string_builder.append_value("2020-09-08T13:42:29.19085Z");
    1104
    1105
            let data = date_string_builder.finish();
    1106
    1107
            let funcs: Vec<(ScalarFunctionImplementation, TimeUnit)> = vec![
    1108
                (Arc::new(to_timestamp), Nanosecond),
    1109
                (Arc::new(to_timestamp_micros), Microsecond),
    1110
                (Arc::new(to_timestamp_millis), Millisecond),
    1111
                (Arc::new(to_timestamp_nanos), Nanosecond),
    1112
                (Arc::new(to_timestamp_seconds), Second),
    1113
            ];
    1114
    1115
            let mut nanos_builder = TimestampNanosecondArray::builder(2);
    1116
            let mut millis_builder = TimestampMillisecondArray::builder(2);
    1117
            let mut micros_builder = TimestampMicrosecondArray::builder(2);
    1118
            let mut sec_builder = TimestampSecondArray::builder(2);
    1119
    1120
            nanos_builder.append_value(1599572549190850000);
    1121
            millis_builder.append_value(1599572549190);
    1122
            micros_builder.append_value(1599572549190850);
    1123
            sec_builder.append_value(1599572549);
    1124
    1125
            let nanos_expected_timestamps = &nanos_builder.finish() as &dyn Array;
    1126
            let millis_expected_timestamps = &millis_builder.finish() as &dyn Array;
    1127
            let micros_expected_timestamps = &micros_builder.finish() as &dyn Array;
    1128
            let sec_expected_timestamps = &sec_builder.finish() as &dyn Array;
    1129
    1130
            for (func, time_unit) in funcs {
    1131
                // test UTF8
    1132
                let string_array = [
    1133
                    ColumnarValue::Array(Arc::new(data.clone()) as ArrayRef),
    1134
                    ColumnarValue::Scalar(ScalarValue::Utf8(Some("%s".to_string()))),
    1135
                    ColumnarValue::Scalar(ScalarValue::Utf8(Some("%c".to_string()))),
    1136
                    ColumnarValue::Scalar(ScalarValue::Utf8(Some("%+".to_string()))),
    1137
                ];
    1138
                let parsed_timestamps = func(&string_array)
    1139
                    .expect("that to_timestamp with format args parsed values without error");
    1140
                if let ColumnarValue::Array(parsed_array) = parsed_timestamps {
    1141
                    assert_eq!(parsed_array.len(), 1);
    1142
                    match time_unit {
    1143
                        Nanosecond => {
    1144
                            assert_eq!(nanos_expected_timestamps, parsed_array.as_ref())
    1145
                        }
    1146
                        Millisecond => {
    1147
                            assert_eq!(millis_expected_timestamps, parsed_array.as_ref())
    1148
                        }
    1149
                        Microsecond => {
    1150
                            assert_eq!(micros_expected_timestamps, parsed_array.as_ref())
    1151
                        }
    1152
                        Second => {
    1153
                            assert_eq!(sec_expected_timestamps, parsed_array.as_ref())
    1154
                        }
    1155
                    };
    1156
                } else {
    1157
                    panic!("Expected a columnar array")
    1158
                }
    1159
    1160
                // test LargeUTF8
    1161
                let string_array = [
    1162
                    ColumnarValue::Array(Arc::new(data.clone()) as ArrayRef),
    1163
                    ColumnarValue::Scalar(ScalarValue::LargeUtf8(Some("%s".to_string()))),
    1164
                    ColumnarValue::Scalar(ScalarValue::LargeUtf8(Some("%c".to_string()))),
    1165
                    ColumnarValue::Scalar(ScalarValue::LargeUtf8(Some("%+".to_string()))),
    1166
                ];
    1167
                let parsed_timestamps = func(&string_array)
    1168
                    .expect("that to_timestamp with format args parsed values without error");
    1169
                if let ColumnarValue::Array(parsed_array) = parsed_timestamps {
    1170
                    assert_eq!(parsed_array.len(), 1);
    1171
                    assert!(matches!(parsed_array.data_type(), Timestamp(_, None)));
    1172
    1173
                    match time_unit {
    1174
                        Nanosecond => {
    1175
                            assert_eq!(nanos_expected_timestamps, parsed_array.as_ref())
    1176
                        }
    1177
                        Millisecond => {
    1178
                            assert_eq!(millis_expected_timestamps, parsed_array.as_ref())
    1179
                        }
    1180
                        Microsecond => {
    1181
                            assert_eq!(micros_expected_timestamps, parsed_array.as_ref())
    1182
                        }
    1183
                        Second => {
    1184
                            assert_eq!(sec_expected_timestamps, parsed_array.as_ref())
    1185
                        }
    1186
                    };
    1187
                } else {
    1188
                    panic!("Expected a columnar array")
    1189
                }
    1190
    1191
                // test other types
    1192
                let string_array = [
    1193
                    ColumnarValue::Array(Arc::new(data.clone()) as ArrayRef),
    1194
                    ColumnarValue::Scalar(ScalarValue::Int32(Some(1))),
    1195
                    ColumnarValue::Scalar(ScalarValue::Int32(Some(2))),
    1196
                    ColumnarValue::Scalar(ScalarValue::Int32(Some(3))),
    1197
                ];
    1198
    1199
                let expected = "Unsupported data type Int32 for function".to_string();
    1200
                let actual = func(&string_array).unwrap_err().to_string();
    1201
                assert_contains!(actual, expected);
    1202
    1203
                // test other types
    1204
                let string_array = [
    1205
                    ColumnarValue::Array(Arc::new(data.clone()) as ArrayRef),
    1206
                    ColumnarValue::Array(Arc::new(PrimitiveArray::<Int64Type>::new(
    1207
                        vec![1i64].into(),
    1208
                        None,
    1209
                    )) as ArrayRef),
    1210
                ];
    1211
    1212
                let expected = "Unsupported data type".to_string();
    1213
                let actual = func(&string_array).unwrap_err().to_string();
    1214
                assert_contains!(actual, expected);
    1215
            }
    1216
        }
    1217
    }