// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

//! Zero-sized types used to parameterize generic array implementations

use crate::delta::{
    add_days_datetime, add_months_date, add_months_datetime, sub_days_datetime, sub_months_datetime,
};
use crate::temporal_conversions::as_datetime_with_timezone;
use crate::timezone::Tz;
use crate::{ArrowNativeTypeOp, OffsetSizeTrait};
use arrow_buffer::{Buffer, OffsetBuffer, i256};
use arrow_data::decimal::{
    format_decimal_str, is_validate_decimal_precision, is_validate_decimal32_precision,
    is_validate_decimal64_precision, is_validate_decimal256_precision, validate_decimal_precision,
    validate_decimal32_precision, validate_decimal64_precision, validate_decimal256_precision,
};
use arrow_data::{validate_binary_view, validate_string_view};
use arrow_schema::{
    ArrowError, DECIMAL_DEFAULT_SCALE, DECIMAL32_DEFAULT_SCALE, DECIMAL32_MAX_PRECISION,
    DECIMAL32_MAX_SCALE, DECIMAL64_DEFAULT_SCALE, DECIMAL64_MAX_PRECISION, DECIMAL64_MAX_SCALE,
    DECIMAL128_MAX_PRECISION, DECIMAL128_MAX_SCALE, DECIMAL256_MAX_PRECISION, DECIMAL256_MAX_SCALE,
    DataType, IntervalUnit, TimeUnit,
};
use chrono::{DateTime, Duration, NaiveDate, NaiveDateTime, TimeZone};
use half::f16;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::ops::Sub;

// re-export types so that they can be used without importing arrow_buffer explicitly
pub use arrow_buffer::{IntervalDayTime, IntervalMonthDayNano};

// BooleanType is special: its bit-width is not the size of the primitive type, and its `index`
// operation assumes bit-packing.
/// A boolean datatype
#[derive(Debug)]
pub struct BooleanType {}

impl BooleanType {
    /// The corresponding Arrow data type
    pub const DATA_TYPE: DataType = DataType::Boolean;
}

/// Trait for [primitive values].
///
/// This trait bridges the dynamic-typed nature of Arrow
/// (via [`DataType`]) with the static-typed nature of rust types
/// ([`ArrowNativeType`]) for all types that implement [`ArrowNativeType`].
///
/// [primitive values]: https://arrow.apache.org/docs/format/Columnar.html#fixed-size-primitive-layout
/// [`ArrowNativeType`]: arrow_buffer::ArrowNativeType
pub trait ArrowPrimitiveType: primitive::PrimitiveTypeSealed + 'static {
    /// Corresponding Rust native type for the primitive type.
    type Native: ArrowNativeTypeOp;

    /// the corresponding Arrow data type of this primitive type.
    const DATA_TYPE: DataType;

    /// Returns a default value of this primitive type.
    ///
    /// This is useful for aggregate array ops like `sum()`, `mean()`.
    fn default_value() -> Self::Native {
        Default::default()
    }
}

mod primitive {
    pub trait PrimitiveTypeSealed {}
}

macro_rules! make_type {
    ($name:ident, $native_ty:ty, $data_ty:expr, $doc_string: literal) => {
        #[derive(Debug)]
        #[doc = $doc_string]
        pub struct $name {}

        impl ArrowPrimitiveType for $name {
            type Native = $native_ty;
            const DATA_TYPE: DataType = $data_ty;
        }

        impl primitive::PrimitiveTypeSealed for $name {}
    };
}

make_type!(Int8Type, i8, DataType::Int8, "A signed 8-bit integer type.");
make_type!(
    Int16Type,
    i16,
    DataType::Int16,
    "Signed 16-bit integer type."
);
make_type!(
    Int32Type,
    i32,
    DataType::Int32,
    "Signed 32-bit integer type."
);
make_type!(
    Int64Type,
    i64,
    DataType::Int64,
    "Signed 64-bit integer type."
);
make_type!(
    UInt8Type,
    u8,
    DataType::UInt8,
    "Unsigned 8-bit integer type."
);
make_type!(
    UInt16Type,
    u16,
    DataType::UInt16,
    "Unsigned 16-bit integer type."
);
make_type!(
    UInt32Type,
    u32,
    DataType::UInt32,
    "Unsigned 32-bit integer type."
);
make_type!(
    UInt64Type,
    u64,
    DataType::UInt64,
    "Unsigned 64-bit integer type."
);
make_type!(
    Float16Type,
    f16,
    DataType::Float16,
    "16-bit floating point number type."
);
make_type!(
    Float32Type,
    f32,
    DataType::Float32,
    "32-bit floating point number type."
);
make_type!(
    Float64Type,
    f64,
    DataType::Float64,
    "64-bit floating point number type."
);
make_type!(
    TimestampSecondType,
    i64,
    DataType::Timestamp(TimeUnit::Second, None),
    "Timestamp second type with an optional timezone."
);
make_type!(
    TimestampMillisecondType,
    i64,
    DataType::Timestamp(TimeUnit::Millisecond, None),
    "Timestamp millisecond type with an optional timezone."
);
make_type!(
    TimestampMicrosecondType,
    i64,
    DataType::Timestamp(TimeUnit::Microsecond, None),
    "Timestamp microsecond type with an optional timezone."
);
make_type!(
    TimestampNanosecondType,
    i64,
    DataType::Timestamp(TimeUnit::Nanosecond, None),
    "Timestamp nanosecond type with an optional timezone."
);
make_type!(
    Date32Type,
    i32,
    DataType::Date32,
    "32-bit date type: the elapsed time since UNIX epoch in days (32 bits)."
);
make_type!(
    Date64Type,
    i64,
    DataType::Date64,
    "64-bit date type: the elapsed time since UNIX epoch in milliseconds (64 bits). \
    Values must be divisible by `86_400_000`. \
    See [`DataType::Date64`] for more details."
);
make_type!(
    Time32SecondType,
    i32,
    DataType::Time32(TimeUnit::Second),
    "32-bit time type: the elapsed time since midnight in seconds."
);
make_type!(
    Time32MillisecondType,
    i32,
    DataType::Time32(TimeUnit::Millisecond),
    "32-bit time type: the elapsed time since midnight in milliseconds."
);
make_type!(
    Time64MicrosecondType,
    i64,
    DataType::Time64(TimeUnit::Microsecond),
    "64-bit time type: the elapsed time since midnight in microseconds."
);
make_type!(
    Time64NanosecondType,
    i64,
    DataType::Time64(TimeUnit::Nanosecond),
    "64-bit time type: the elapsed time since midnight in nanoseconds."
);
make_type!(
    IntervalYearMonthType,
    i32,
    DataType::Interval(IntervalUnit::YearMonth),
    "32-bit “calendar” interval type: the number of whole months."
);
make_type!(
    IntervalDayTimeType,
    IntervalDayTime,
    DataType::Interval(IntervalUnit::DayTime),
    "“Calendar” interval type: days and milliseconds. See [`IntervalDayTime`] for more details."
);
make_type!(
    IntervalMonthDayNanoType,
    IntervalMonthDayNano,
    DataType::Interval(IntervalUnit::MonthDayNano),
    r"“Calendar” interval type: months, days, and nanoseconds. See [`IntervalMonthDayNano`] for more details."
);
make_type!(
    DurationSecondType,
    i64,
    DataType::Duration(TimeUnit::Second),
    "Elapsed time type: seconds."
);
make_type!(
    DurationMillisecondType,
    i64,
    DataType::Duration(TimeUnit::Millisecond),
    "Elapsed time type: milliseconds."
);
make_type!(
    DurationMicrosecondType,
    i64,
    DataType::Duration(TimeUnit::Microsecond),
    "Elapsed time type: microseconds."
);
make_type!(
    DurationNanosecondType,
    i64,
    DataType::Duration(TimeUnit::Nanosecond),
    "Elapsed time type: nanoseconds."
);

/// A subtype of primitive type that represents legal dictionary keys.
/// See <https://arrow.apache.org/docs/format/Columnar.html>
pub trait ArrowDictionaryKeyType: ArrowPrimitiveType {}

impl ArrowDictionaryKeyType for Int8Type {}

impl ArrowDictionaryKeyType for Int16Type {}

impl ArrowDictionaryKeyType for Int32Type {}

impl ArrowDictionaryKeyType for Int64Type {}

impl ArrowDictionaryKeyType for UInt8Type {}

impl ArrowDictionaryKeyType for UInt16Type {}

impl ArrowDictionaryKeyType for UInt32Type {}

impl ArrowDictionaryKeyType for UInt64Type {}

/// A subtype of primitive type that is used as run-ends index
/// in `RunArray`.
/// See <https://arrow.apache.org/docs/format/Columnar.html>
pub trait RunEndIndexType: ArrowPrimitiveType {}

impl RunEndIndexType for Int16Type {}

impl RunEndIndexType for Int32Type {}

impl RunEndIndexType for Int64Type {}

/// A subtype of primitive type that represents temporal values.
pub trait ArrowTemporalType: ArrowPrimitiveType {}

impl ArrowTemporalType for TimestampSecondType {}
impl ArrowTemporalType for TimestampMillisecondType {}
impl ArrowTemporalType for TimestampMicrosecondType {}
impl ArrowTemporalType for TimestampNanosecondType {}
impl ArrowTemporalType for Date32Type {}
impl ArrowTemporalType for Date64Type {}
impl ArrowTemporalType for Time32SecondType {}
impl ArrowTemporalType for Time32MillisecondType {}
impl ArrowTemporalType for Time64MicrosecondType {}
impl ArrowTemporalType for Time64NanosecondType {}
// impl ArrowTemporalType for IntervalYearMonthType {}
// impl ArrowTemporalType for IntervalDayTimeType {}
// impl ArrowTemporalType for IntervalMonthDayNanoType {}
impl ArrowTemporalType for DurationSecondType {}
impl ArrowTemporalType for DurationMillisecondType {}
impl ArrowTemporalType for DurationMicrosecondType {}
impl ArrowTemporalType for DurationNanosecondType {}

/// A timestamp type allows us to create array builders that take a timestamp.
pub trait ArrowTimestampType: ArrowTemporalType<Native = i64> {
    /// The [`TimeUnit`] of this timestamp.
    const UNIT: TimeUnit;

    /// Creates a ArrowTimestampType::Native from the provided [`NaiveDateTime`]
    ///
    /// See [`DataType::Timestamp`] for more information on timezone handling
    #[deprecated(since = "58.1.0", note = "Use from_naive_datetime instead")]
    fn make_value(naive: NaiveDateTime) -> Option<i64>;

    /// Creates a timestamp value from a [`DateTime`] in any timezone.
    ///
    /// Returns `None` if the timestamp value would overflow the i64 range
    /// (e.g., for nanosecond precision with extreme datetime values).
    ///
    /// # Arguments
    ///
    /// * `datetime` - The datetime to convert
    fn from_datetime<Tz: TimeZone>(datetime: DateTime<Tz>) -> Option<i64>;

    /// Creates a timestamp value from a [`NaiveDateTime`] interpreted in the given timezone.
    ///
    /// # Arguments
    ///
    /// * `naive` - The local datetime to convert
    /// * `tz` - Optional timezone. If `None`, interprets as UTC
    ///   (equivalent to calling [`Self::make_value`]).
    fn from_naive_datetime(naive: NaiveDateTime, tz: Option<&Tz>) -> Option<i64> {
        match tz {
            Some(tz) => match tz.from_local_datetime(&naive) {
                chrono::offset::LocalResult::Single(dt) => Self::from_datetime(dt),
                chrono::offset::LocalResult::Ambiguous(dt1, _) => Self::from_datetime(dt1),
                chrono::offset::LocalResult::None => None,
            },
            None => Self::from_datetime(naive.and_utc()),
        }
    }
}

impl ArrowTimestampType for TimestampSecondType {
    const UNIT: TimeUnit = TimeUnit::Second;

    fn make_value(naive: NaiveDateTime) -> Option<i64> {
        Some(naive.and_utc().timestamp())
    }

    fn from_datetime<Tz: TimeZone>(datetime: DateTime<Tz>) -> Option<i64> {
        Some(datetime.timestamp())
    }
}
impl ArrowTimestampType for TimestampMillisecondType {
    const UNIT: TimeUnit = TimeUnit::Millisecond;

    fn make_value(naive: NaiveDateTime) -> Option<i64> {
        let utc = naive.and_utc();
        let millis = utc.timestamp().checked_mul(1_000)?;
        millis.checked_add(utc.timestamp_subsec_millis() as i64)
    }

    fn from_datetime<Tz: TimeZone>(datetime: DateTime<Tz>) -> Option<i64> {
        let millis = datetime.timestamp().checked_mul(1_000)?;
        millis.checked_add(datetime.timestamp_subsec_millis() as i64)
    }
}
impl ArrowTimestampType for TimestampMicrosecondType {
    const UNIT: TimeUnit = TimeUnit::Microsecond;

    fn make_value(naive: NaiveDateTime) -> Option<i64> {
        let utc = naive.and_utc();
        let micros = utc.timestamp().checked_mul(1_000_000)?;
        micros.checked_add(utc.timestamp_subsec_micros() as i64)
    }

    fn from_datetime<Tz: TimeZone>(datetime: DateTime<Tz>) -> Option<i64> {
        let micros = datetime.timestamp().checked_mul(1_000_000)?;
        micros.checked_add(datetime.timestamp_subsec_micros() as i64)
    }
}
impl ArrowTimestampType for TimestampNanosecondType {
    const UNIT: TimeUnit = TimeUnit::Nanosecond;

    fn make_value(naive: NaiveDateTime) -> Option<i64> {
        let utc = naive.and_utc();
        let nanos = utc.timestamp().checked_mul(1_000_000_000)?;
        nanos.checked_add(utc.timestamp_subsec_nanos() as i64)
    }

    fn from_datetime<Tz: TimeZone>(datetime: DateTime<Tz>) -> Option<i64> {
        datetime.timestamp_nanos_opt()
    }
}

fn add_year_months<T: ArrowTimestampType>(
    timestamp: <T as ArrowPrimitiveType>::Native,
    delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
    tz: Tz,
) -> Option<<T as ArrowPrimitiveType>::Native> {
    let months = IntervalYearMonthType::to_months(delta);
    let res = as_datetime_with_timezone::<T>(timestamp, tz)?;
    let res = add_months_datetime(res, months)?;
    T::from_naive_datetime(res.naive_utc(), None)
}

fn add_day_time<T: ArrowTimestampType>(
    timestamp: <T as ArrowPrimitiveType>::Native,
    delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
    tz: Tz,
) -> Option<<T as ArrowPrimitiveType>::Native> {
    let (days, ms) = IntervalDayTimeType::to_parts(delta);
    let res = as_datetime_with_timezone::<T>(timestamp, tz)?;
    let res = add_days_datetime(res, days)?;
    let res = res.checked_add_signed(Duration::try_milliseconds(ms as i64)?)?;
    T::from_naive_datetime(res.naive_utc(), None)
}

fn add_month_day_nano<T: ArrowTimestampType>(
    timestamp: <T as ArrowPrimitiveType>::Native,
    delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
    tz: Tz,
) -> Option<<T as ArrowPrimitiveType>::Native> {
    let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
    let res = as_datetime_with_timezone::<T>(timestamp, tz)?;
    let res = add_months_datetime(res, months)?;
    let res = add_days_datetime(res, days)?;
    let res = res.checked_add_signed(Duration::nanoseconds(nanos))?;
    T::from_naive_datetime(res.naive_utc(), None)
}

fn subtract_year_months<T: ArrowTimestampType>(
    timestamp: <T as ArrowPrimitiveType>::Native,
    delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
    tz: Tz,
) -> Option<<T as ArrowPrimitiveType>::Native> {
    let months = IntervalYearMonthType::to_months(delta);
    let res = as_datetime_with_timezone::<T>(timestamp, tz)?;
    let res = sub_months_datetime(res, months)?;
    T::from_naive_datetime(res.naive_utc(), None)
}

fn subtract_day_time<T: ArrowTimestampType>(
    timestamp: <T as ArrowPrimitiveType>::Native,
    delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
    tz: Tz,
) -> Option<<T as ArrowPrimitiveType>::Native> {
    let (days, ms) = IntervalDayTimeType::to_parts(delta);
    let res = as_datetime_with_timezone::<T>(timestamp, tz)?;
    let res = sub_days_datetime(res, days)?;
    let res = res.checked_sub_signed(Duration::try_milliseconds(ms as i64)?)?;
    T::from_naive_datetime(res.naive_utc(), None)
}

fn subtract_month_day_nano<T: ArrowTimestampType>(
    timestamp: <T as ArrowPrimitiveType>::Native,
    delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
    tz: Tz,
) -> Option<<T as ArrowPrimitiveType>::Native> {
    let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
    let res = as_datetime_with_timezone::<T>(timestamp, tz)?;
    let res = sub_months_datetime(res, months)?;
    let res = sub_days_datetime(res, days)?;
    let res = res.checked_sub_signed(Duration::nanoseconds(nanos))?;
    T::from_naive_datetime(res.naive_utc(), None)
}

impl TimestampSecondType {
    /// Adds the given IntervalYearMonthType to an arrow TimestampSecondType.
    ///
    /// Returns `None` when it will result in overflow.
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn add_year_months(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        add_year_months::<Self>(timestamp, delta, tz)
    }

    /// Adds the given IntervalDayTimeType to an arrow TimestampSecondType.
    ///
    /// Returns `None` when it will result in overflow.
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn add_day_time(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        add_day_time::<Self>(timestamp, delta, tz)
    }

    /// Adds the given IntervalMonthDayNanoType to an arrow TimestampSecondType
    ///
    /// Returns `None` when it will result in overflow.
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn add_month_day_nano(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        add_month_day_nano::<Self>(timestamp, delta, tz)
    }

    /// Subtracts the given IntervalYearMonthType to an arrow TimestampSecondType
    ///
    /// Returns `None` when it will result in overflow.
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn subtract_year_months(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        subtract_year_months::<Self>(timestamp, delta, tz)
    }

    /// Subtracts the given IntervalDayTimeType to an arrow TimestampSecondType
    ///
    /// Returns `None` when it will result in overflow.
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn subtract_day_time(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        subtract_day_time::<Self>(timestamp, delta, tz)
    }

    /// Subtracts the given IntervalMonthDayNanoType to an arrow TimestampSecondType
    ///
    /// Returns `None` when it will result in overflow.
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn subtract_month_day_nano(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        subtract_month_day_nano::<Self>(timestamp, delta, tz)
    }
}

impl TimestampMicrosecondType {
    /// Adds the given IntervalYearMonthType to an arrow TimestampMicrosecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn add_year_months(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        add_year_months::<Self>(timestamp, delta, tz)
    }

    /// Adds the given IntervalDayTimeType to an arrow TimestampMicrosecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn add_day_time(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        add_day_time::<Self>(timestamp, delta, tz)
    }

    /// Adds the given IntervalMonthDayNanoType to an arrow TimestampMicrosecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn add_month_day_nano(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        add_month_day_nano::<Self>(timestamp, delta, tz)
    }

    /// Subtracts the given IntervalYearMonthType to an arrow TimestampMicrosecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn subtract_year_months(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        subtract_year_months::<Self>(timestamp, delta, tz)
    }

    /// Subtracts the given IntervalDayTimeType to an arrow TimestampMicrosecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn subtract_day_time(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        subtract_day_time::<Self>(timestamp, delta, tz)
    }

    /// Subtracts the given IntervalMonthDayNanoType to an arrow TimestampMicrosecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn subtract_month_day_nano(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        subtract_month_day_nano::<Self>(timestamp, delta, tz)
    }
}

impl TimestampMillisecondType {
    /// Adds the given IntervalYearMonthType to an arrow TimestampMillisecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn add_year_months(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        add_year_months::<Self>(timestamp, delta, tz)
    }

    /// Adds the given IntervalDayTimeType to an arrow TimestampMillisecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn add_day_time(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        add_day_time::<Self>(timestamp, delta, tz)
    }

    /// Adds the given IntervalMonthDayNanoType to an arrow TimestampMillisecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn add_month_day_nano(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        add_month_day_nano::<Self>(timestamp, delta, tz)
    }

    /// Subtracts the given IntervalYearMonthType to an arrow TimestampMillisecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn subtract_year_months(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        subtract_year_months::<Self>(timestamp, delta, tz)
    }

    /// Subtracts the given IntervalDayTimeType to an arrow TimestampMillisecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn subtract_day_time(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        subtract_day_time::<Self>(timestamp, delta, tz)
    }

    /// Subtracts the given IntervalMonthDayNanoType to an arrow TimestampMillisecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn subtract_month_day_nano(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        subtract_month_day_nano::<Self>(timestamp, delta, tz)
    }
}

impl TimestampNanosecondType {
    /// Adds the given IntervalYearMonthType to an arrow TimestampNanosecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn add_year_months(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        add_year_months::<Self>(timestamp, delta, tz)
    }

    /// Adds the given IntervalDayTimeType to an arrow TimestampNanosecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn add_day_time(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        add_day_time::<Self>(timestamp, delta, tz)
    }

    /// Adds the given IntervalMonthDayNanoType to an arrow TimestampNanosecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn add_month_day_nano(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        add_month_day_nano::<Self>(timestamp, delta, tz)
    }

    /// Subtracts the given IntervalYearMonthType to an arrow TimestampNanosecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn subtract_year_months(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        subtract_year_months::<Self>(timestamp, delta, tz)
    }

    /// Subtracts the given IntervalDayTimeType to an arrow TimestampNanosecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn subtract_day_time(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        subtract_day_time::<Self>(timestamp, delta, tz)
    }

    /// Subtracts the given IntervalMonthDayNanoType to an arrow TimestampNanosecondType
    ///
    /// # Arguments
    ///
    /// * `timestamp` - The date on which to perform the operation
    /// * `delta` - The interval to add
    /// * `tz` - The timezone in which to interpret `timestamp`
    pub fn subtract_month_day_nano(
        timestamp: <Self as ArrowPrimitiveType>::Native,
        delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
        tz: Tz,
    ) -> Option<<Self as ArrowPrimitiveType>::Native> {
        subtract_month_day_nano::<Self>(timestamp, delta, tz)
    }
}

impl IntervalYearMonthType {
    /// Creates a IntervalYearMonthType::Native
    ///
    /// # Arguments
    ///
    /// * `years` - The number of years (+/-) represented in this interval
    /// * `months` - The number of months (+/-) represented in this interval
    #[inline]
    pub fn make_value(
        years: i32,
        months: i32,
    ) -> <IntervalYearMonthType as ArrowPrimitiveType>::Native {
        years * 12 + months
    }

    /// Turns a IntervalYearMonthType type into an i32 of months.
    ///
    /// This operation is technically a no-op, it is included for comprehensiveness.
    ///
    /// # Arguments
    ///
    /// * `i` - The IntervalYearMonthType::Native to convert
    #[inline]
    pub fn to_months(i: <IntervalYearMonthType as ArrowPrimitiveType>::Native) -> i32 {
        i
    }
}

impl IntervalDayTimeType {
    /// Creates a IntervalDayTimeType::Native
    ///
    /// # Arguments
    ///
    /// * `days` - The number of days (+/-) represented in this interval
    /// * `millis` - The number of milliseconds (+/-) represented in this interval
    #[inline]
    pub fn make_value(days: i32, milliseconds: i32) -> IntervalDayTime {
        IntervalDayTime { days, milliseconds }
    }

    /// Turns a IntervalDayTimeType into a tuple of (days, milliseconds)
    ///
    /// # Arguments
    ///
    /// * `i` - The IntervalDayTimeType to convert
    #[inline]
    pub fn to_parts(i: IntervalDayTime) -> (i32, i32) {
        (i.days, i.milliseconds)
    }
}

impl IntervalMonthDayNanoType {
    /// Creates a IntervalMonthDayNanoType::Native
    ///
    /// # Arguments
    ///
    /// * `months` - The number of months (+/-) represented in this interval
    /// * `days` - The number of days (+/-) represented in this interval
    /// * `nanos` - The number of nanoseconds (+/-) represented in this interval
    #[inline]
    pub fn make_value(months: i32, days: i32, nanoseconds: i64) -> IntervalMonthDayNano {
        IntervalMonthDayNano {
            months,
            days,
            nanoseconds,
        }
    }

    /// Turns a IntervalMonthDayNanoType into a tuple of (months, days, nanos)
    ///
    /// # Arguments
    ///
    /// * `i` - The IntervalMonthDayNanoType to convert
    #[inline]
    pub fn to_parts(i: IntervalMonthDayNano) -> (i32, i32, i64) {
        (i.months, i.days, i.nanoseconds)
    }
}

impl Date32Type {
    /// Converts an arrow Date32Type into a chrono::NaiveDate
    ///
    /// # Arguments
    ///
    /// * `i` - The Date32Type to convert
    #[deprecated(since = "58.0.0", note = "Use to_naive_date_opt instead.")]
    pub fn to_naive_date(i: <Date32Type as ArrowPrimitiveType>::Native) -> NaiveDate {
        Self::to_naive_date_opt(i)
            .unwrap_or_else(|| panic!("Date32Type::to_naive_date overflowed for date: {i}",))
    }

    /// Converts an arrow Date32Type into a chrono::NaiveDate
    ///
    /// # Arguments
    ///
    /// * `i` - The Date32Type to convert
    ///
    /// Returns `Some(NaiveDate)` if it fits, `None` otherwise.
    pub fn to_naive_date_opt(i: <Date32Type as ArrowPrimitiveType>::Native) -> Option<NaiveDate> {
        let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
        Duration::try_days(i as i64).and_then(|d| epoch.checked_add_signed(d))
    }

    /// Converts a chrono::NaiveDate into an arrow Date32Type
    ///
    /// # Arguments
    ///
    /// * `d` - The NaiveDate to convert
    pub fn from_naive_date(d: NaiveDate) -> <Date32Type as ArrowPrimitiveType>::Native {
        let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
        d.sub(epoch).num_days() as <Date32Type as ArrowPrimitiveType>::Native
    }

    /// Adds the given IntervalYearMonthType to an arrow Date32Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to add
    #[deprecated(
        since = "58.0.0",
        note = "Use `add_year_months_opt` instead, which returns an Option to handle overflow."
    )]
    pub fn add_year_months(
        date: <Date32Type as ArrowPrimitiveType>::Native,
        delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
    ) -> <Date32Type as ArrowPrimitiveType>::Native {
        Self::add_year_months_opt(date, delta).unwrap_or_else(|| {
            panic!("Date32Type::add_year_months overflowed for date: {date}, delta: {delta}",)
        })
    }

    /// Adds the given IntervalYearMonthType to an arrow Date32Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to add
    ///
    /// Returns `Some(Date32Type)` if it fits, `None` otherwise.
    pub fn add_year_months_opt(
        date: <Date32Type as ArrowPrimitiveType>::Native,
        delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
    ) -> Option<<Date32Type as ArrowPrimitiveType>::Native> {
        let prior = Date32Type::to_naive_date_opt(date)?;
        let months = IntervalYearMonthType::to_months(delta);
        let posterior = add_months_date(prior, months)?;
        Some(Date32Type::from_naive_date(posterior))
    }

    /// Adds the given IntervalDayTimeType to an arrow Date32Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to add
    #[deprecated(
        since = "58.0.0",
        note = "Use `add_day_time_opt` instead, which returns an Option to handle overflow."
    )]
    pub fn add_day_time(
        date: <Date32Type as ArrowPrimitiveType>::Native,
        delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
    ) -> <Date32Type as ArrowPrimitiveType>::Native {
        Self::add_day_time_opt(date, delta).unwrap_or_else(|| {
            panic!("Date32Type::add_day_time overflowed for date: {date}, delta: {delta:?}",)
        })
    }

    /// Adds the given IntervalDayTimeType to an arrow Date32Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to add
    ///
    /// Returns `Some(Date32Type)` if it fits, `None` otherwise.
    pub fn add_day_time_opt(
        date: <Date32Type as ArrowPrimitiveType>::Native,
        delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
    ) -> Option<<Date32Type as ArrowPrimitiveType>::Native> {
        let (days, ms) = IntervalDayTimeType::to_parts(delta);
        let res = Date32Type::to_naive_date_opt(date)?;
        let res = res.checked_add_signed(Duration::try_days(days as i64)?)?;
        let res = res.checked_add_signed(Duration::try_milliseconds(ms as i64)?)?;
        Some(Date32Type::from_naive_date(res))
    }

    /// Adds the given IntervalMonthDayNanoType to an arrow Date32Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to add
    #[deprecated(
        since = "58.0.0",
        note = "Use `add_month_day_nano_opt` instead, which returns an Option to handle overflow."
    )]
    pub fn add_month_day_nano(
        date: <Date32Type as ArrowPrimitiveType>::Native,
        delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
    ) -> <Date32Type as ArrowPrimitiveType>::Native {
        Self::add_month_day_nano_opt(date, delta).unwrap_or_else(|| {
            panic!("Date32Type::add_month_day_nano overflowed for date: {date}, delta: {delta:?}",)
        })
    }

    /// Adds the given IntervalMonthDayNanoType to an arrow Date32Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to add
    ///
    /// Returns `Some(Date32Type)` if it fits, `None` otherwise.
    pub fn add_month_day_nano_opt(
        date: <Date32Type as ArrowPrimitiveType>::Native,
        delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
    ) -> Option<<Date32Type as ArrowPrimitiveType>::Native> {
        let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
        let res = Date32Type::to_naive_date_opt(date)?;
        let res = add_months_date(res, months)?;
        let res = res.checked_add_signed(Duration::try_days(days as i64)?)?;
        let res = res.checked_add_signed(Duration::nanoseconds(nanos))?;
        Some(Date32Type::from_naive_date(res))
    }

    /// Subtract the given IntervalYearMonthType to an arrow Date32Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to subtract
    #[deprecated(
        since = "58.0.0",
        note = "Use `subtract_year_months_opt` instead, which returns an Option to handle overflow."
    )]
    pub fn subtract_year_months(
        date: <Date32Type as ArrowPrimitiveType>::Native,
        delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
    ) -> <Date32Type as ArrowPrimitiveType>::Native {
        Self::subtract_year_months_opt(date, delta).unwrap_or_else(|| {
            panic!("Date32Type::subtract_year_months overflowed for date: {date}, delta: {delta}",)
        })
    }

    /// Subtract the given IntervalYearMonthType to an arrow Date32Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to subtract
    ///
    /// Returns `Some(Date32Type)` if it fits, `None` otherwise.
    pub fn subtract_year_months_opt(
        date: <Date32Type as ArrowPrimitiveType>::Native,
        delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
    ) -> Option<<Date32Type as ArrowPrimitiveType>::Native> {
        let prior = Date32Type::to_naive_date_opt(date)?;
        let months = IntervalYearMonthType::to_months(-delta);
        let posterior = add_months_date(prior, months)?;
        Some(Date32Type::from_naive_date(posterior))
    }

    /// Subtract the given IntervalDayTimeType to an arrow Date32Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to subtract
    #[deprecated(
        since = "58.0.0",
        note = "Use `subtract_day_time_opt` instead, which returns an Option to handle overflow."
    )]
    pub fn subtract_day_time(
        date: <Date32Type as ArrowPrimitiveType>::Native,
        delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
    ) -> <Date32Type as ArrowPrimitiveType>::Native {
        Self::subtract_day_time_opt(date, delta).unwrap_or_else(|| {
            panic!("Date32Type::subtract_day_time overflowed for date: {date}, delta: {delta:?}",)
        })
    }

    /// Subtract the given IntervalDayTimeType to an arrow Date32Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to subtract
    ///
    /// Returns `Some(Date32Type)` if it fits, `None` otherwise.
    pub fn subtract_day_time_opt(
        date: <Date32Type as ArrowPrimitiveType>::Native,
        delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
    ) -> Option<<Date32Type as ArrowPrimitiveType>::Native> {
        let (days, ms) = IntervalDayTimeType::to_parts(delta);
        let res = Date32Type::to_naive_date_opt(date)?;
        let res = res.checked_sub_signed(Duration::try_days(days as i64)?)?;
        let res = res.checked_sub_signed(Duration::try_milliseconds(ms as i64)?)?;
        Some(Date32Type::from_naive_date(res))
    }

    /// Subtract the given IntervalMonthDayNanoType to an arrow Date32Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to subtract
    #[deprecated(
        since = "58.0.0",
        note = "Use `subtract_month_day_nano_opt` instead, which returns an Option to handle overflow."
    )]
    pub fn subtract_month_day_nano(
        date: <Date32Type as ArrowPrimitiveType>::Native,
        delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
    ) -> <Date32Type as ArrowPrimitiveType>::Native {
        Self::subtract_month_day_nano_opt(date, delta).unwrap_or_else(|| {
            panic!(
                "Date32Type::subtract_month_day_nano overflowed for date: {date}, delta: {delta:?}",
            )
        })
    }

    /// Subtract the given IntervalMonthDayNanoType to an arrow Date32Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to subtract
    ///
    /// Returns `Some(Date32Type)` if it fits, `None` otherwise.
    pub fn subtract_month_day_nano_opt(
        date: <Date32Type as ArrowPrimitiveType>::Native,
        delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
    ) -> Option<<Date32Type as ArrowPrimitiveType>::Native> {
        let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
        let res = Date32Type::to_naive_date_opt(date)?;
        let res = add_months_date(res, -months)?;
        let res = res.checked_sub_signed(Duration::try_days(days as i64)?)?;
        let res = res.checked_sub_signed(Duration::nanoseconds(nanos))?;
        Some(Date32Type::from_naive_date(res))
    }
}

impl Date64Type {
    /// Converts an arrow Date64Type into a chrono::NaiveDateTime if it fits in the range that chrono::NaiveDateTime can represent.
    /// Returns `None` if the calculation would overflow or underflow.
    ///
    /// This function is able to handle dates ranging between 1677-09-21 (-9,223,372,800,000) and 2262-04-11 (9,223,286,400,000).
    ///
    /// # Arguments
    ///
    /// * `i` - The Date64Type to convert
    ///
    /// Returns `Some(NaiveDateTime)` if it fits, `None` otherwise.
    pub fn to_naive_date_opt(i: <Date64Type as ArrowPrimitiveType>::Native) -> Option<NaiveDate> {
        let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
        Duration::try_milliseconds(i).and_then(|d| epoch.checked_add_signed(d))
    }

    /// Converts a chrono::NaiveDate into an arrow Date64Type
    ///
    /// # Arguments
    ///
    /// * `d` - The NaiveDate to convert
    pub fn from_naive_date(d: NaiveDate) -> <Date64Type as ArrowPrimitiveType>::Native {
        let epoch = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap();
        d.sub(epoch).num_milliseconds() as <Date64Type as ArrowPrimitiveType>::Native
    }

    /// Adds the given IntervalYearMonthType to an arrow Date64Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to add
    ///
    /// Returns `Some(Date64Type)` if it fits, `None` otherwise.
    pub fn add_year_months_opt(
        date: <Date64Type as ArrowPrimitiveType>::Native,
        delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
    ) -> Option<<Date64Type as ArrowPrimitiveType>::Native> {
        let prior = Date64Type::to_naive_date_opt(date)?;
        let months = IntervalYearMonthType::to_months(delta);
        let posterior = add_months_date(prior, months)?;
        Some(Date64Type::from_naive_date(posterior))
    }

    /// Adds the given IntervalDayTimeType to an arrow Date64Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to add
    ///
    /// Returns `Some(Date64Type)` if it fits, `None` otherwise.
    pub fn add_day_time_opt(
        date: <Date64Type as ArrowPrimitiveType>::Native,
        delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
    ) -> Option<<Date64Type as ArrowPrimitiveType>::Native> {
        let (days, ms) = IntervalDayTimeType::to_parts(delta);
        let res = Date64Type::to_naive_date_opt(date)?;
        let res = res.checked_add_signed(Duration::try_days(days as i64)?)?;
        let res = res.checked_add_signed(Duration::try_milliseconds(ms as i64)?)?;
        Some(Date64Type::from_naive_date(res))
    }

    /// Adds the given IntervalMonthDayNanoType to an arrow Date64Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to add
    ///
    /// Returns `Some(Date64Type)` if it fits, `None` otherwise.
    pub fn add_month_day_nano_opt(
        date: <Date64Type as ArrowPrimitiveType>::Native,
        delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
    ) -> Option<<Date64Type as ArrowPrimitiveType>::Native> {
        let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
        let res = Date64Type::to_naive_date_opt(date)?;
        let res = add_months_date(res, months)?;
        let res = res.checked_add_signed(Duration::try_days(days as i64)?)?;
        let res = res.checked_add_signed(Duration::nanoseconds(nanos))?;
        Some(Date64Type::from_naive_date(res))
    }

    /// Subtract the given IntervalYearMonthType to an arrow Date64Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to subtract
    ///
    /// Returns `Some(Date64Type)` if it fits, `None` otherwise.
    pub fn subtract_year_months_opt(
        date: <Date64Type as ArrowPrimitiveType>::Native,
        delta: <IntervalYearMonthType as ArrowPrimitiveType>::Native,
    ) -> Option<<Date64Type as ArrowPrimitiveType>::Native> {
        let prior = Date64Type::to_naive_date_opt(date)?;
        let months = IntervalYearMonthType::to_months(-delta);
        let posterior = add_months_date(prior, months)?;
        Some(Date64Type::from_naive_date(posterior))
    }

    /// Subtract the given IntervalDayTimeType to an arrow Date64Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to subtract
    ///
    /// Returns `Some(Date64Type)` if it fits, `None` otherwise.
    pub fn subtract_day_time_opt(
        date: <Date64Type as ArrowPrimitiveType>::Native,
        delta: <IntervalDayTimeType as ArrowPrimitiveType>::Native,
    ) -> Option<<Date64Type as ArrowPrimitiveType>::Native> {
        let (days, ms) = IntervalDayTimeType::to_parts(delta);
        let res = Date64Type::to_naive_date_opt(date)?;
        let res = res.checked_sub_signed(Duration::try_days(days as i64)?)?;
        let res = res.checked_sub_signed(Duration::try_milliseconds(ms as i64)?)?;
        Some(Date64Type::from_naive_date(res))
    }

    /// Subtract the given IntervalMonthDayNanoType to an arrow Date64Type
    ///
    /// # Arguments
    ///
    /// * `date` - The date on which to perform the operation
    /// * `delta` - The interval to subtract
    ///
    /// Returns `Some(Date64Type)` if it fits, `None` otherwise.
    pub fn subtract_month_day_nano_opt(
        date: <Date64Type as ArrowPrimitiveType>::Native,
        delta: <IntervalMonthDayNanoType as ArrowPrimitiveType>::Native,
    ) -> Option<<Date64Type as ArrowPrimitiveType>::Native> {
        let (months, days, nanos) = IntervalMonthDayNanoType::to_parts(delta);
        let res = Date64Type::to_naive_date_opt(date)?;
        let res = add_months_date(res, -months)?;
        let res = res.checked_sub_signed(Duration::try_days(days as i64)?)?;
        let res = res.checked_sub_signed(Duration::nanoseconds(nanos))?;
        Some(Date64Type::from_naive_date(res))
    }
}

/// Crate private types for Decimal Arrays
///
/// Not intended to be used outside this crate
mod decimal {
    use super::*;

    pub trait DecimalTypeSealed {}
    impl DecimalTypeSealed for Decimal32Type {}
    impl DecimalTypeSealed for Decimal64Type {}
    impl DecimalTypeSealed for Decimal128Type {}
    impl DecimalTypeSealed for Decimal256Type {}
}

/// A trait over the decimal types, used by [`PrimitiveArray`] to provide a generic
/// implementation across the various decimal types
///
/// Implemented by [`Decimal32Type`], [`Decimal64Type`], [`Decimal128Type`] and [`Decimal256Type`]
/// for [`Decimal32Array`], [`Decimal64Array`], [`Decimal128Array`] and [`Decimal256Array`] respectively
///
/// [`PrimitiveArray`]: crate::array::PrimitiveArray
/// [`Decimal32Array`]: crate::array::Decimal32Array
/// [`Decimal64Array`]: crate::array::Decimal64Array
/// [`Decimal128Array`]: crate::array::Decimal128Array
/// [`Decimal256Array`]: crate::array::Decimal256Array
pub trait DecimalType:
    'static + Send + Sync + ArrowPrimitiveType + decimal::DecimalTypeSealed
{
    /// Width of the type
    const BYTE_LENGTH: usize;
    /// Maximum number of significant digits
    const MAX_PRECISION: u8;
    /// Maximum no of digits after the decimal point (note the scale can be negative)
    const MAX_SCALE: i8;
    /// The maximum value for each precision in `0..=MAX_PRECISION`: [0, 9, 99, ...]
    const MAX_FOR_EACH_PRECISION: &'static [Self::Native];
    /// fn to create its [`DataType`]
    const TYPE_CONSTRUCTOR: fn(u8, i8) -> DataType;
    /// Default values for [`DataType`]
    const DEFAULT_TYPE: DataType;

    /// "Decimal32", "Decimal64", "Decimal128" or "Decimal256", for use in error messages
    const PREFIX: &'static str;

    /// Formats the decimal value with the provided precision and scale
    fn format_decimal(value: Self::Native, precision: u8, scale: i8) -> String;

    /// Validates that `value` contains no more than `precision` decimal digits
    fn validate_decimal_precision(
        value: Self::Native,
        precision: u8,
        scale: i8,
    ) -> Result<(), ArrowError>;

    /// Determines whether `value` contains no more than `precision` decimal digits
    fn is_valid_decimal_precision(value: Self::Native, precision: u8) -> bool;
}

/// Validate that `precision` and `scale` are valid for `T`
///
/// Returns an Error if:
/// - `precision` is zero
/// - `precision` is larger than `T:MAX_PRECISION`
/// - `scale` is larger than `T::MAX_SCALE`
/// - `scale` is > `precision`
pub fn validate_decimal_precision_and_scale<T: DecimalType>(
    precision: u8,
    scale: i8,
) -> Result<(), ArrowError> {
    if precision == 0 {
        return Err(ArrowError::InvalidArgumentError(format!(
            "precision cannot be 0, has to be between [1, {}]",
            T::MAX_PRECISION
        )));
    }
    if precision > T::MAX_PRECISION {
        return Err(ArrowError::InvalidArgumentError(format!(
            "precision {} is greater than max {}",
            precision,
            T::MAX_PRECISION
        )));
    }
    if scale > T::MAX_SCALE {
        return Err(ArrowError::InvalidArgumentError(format!(
            "scale {} is greater than max {}",
            scale,
            T::MAX_SCALE
        )));
    }
    if scale > 0 && scale as u8 > precision {
        return Err(ArrowError::InvalidArgumentError(format!(
            "scale {scale} is greater than precision {precision}"
        )));
    }

    Ok(())
}

/// The decimal type for a Decimal32Array
#[derive(Debug)]
pub struct Decimal32Type {}

impl DecimalType for Decimal32Type {
    const BYTE_LENGTH: usize = 4;
    const MAX_PRECISION: u8 = DECIMAL32_MAX_PRECISION;
    const MAX_SCALE: i8 = DECIMAL32_MAX_SCALE;
    const MAX_FOR_EACH_PRECISION: &'static [i32] =
        &arrow_data::decimal::MAX_DECIMAL32_FOR_EACH_PRECISION;
    const TYPE_CONSTRUCTOR: fn(u8, i8) -> DataType = DataType::Decimal32;
    const DEFAULT_TYPE: DataType =
        DataType::Decimal32(DECIMAL32_MAX_PRECISION, DECIMAL32_DEFAULT_SCALE);
    const PREFIX: &'static str = "Decimal32";

    fn format_decimal(value: Self::Native, precision: u8, scale: i8) -> String {
        format_decimal_str(&value.to_string(), precision as usize, scale)
    }

    fn validate_decimal_precision(num: i32, precision: u8, scale: i8) -> Result<(), ArrowError> {
        validate_decimal32_precision(num, precision, scale)
    }

    fn is_valid_decimal_precision(value: Self::Native, precision: u8) -> bool {
        is_validate_decimal32_precision(value, precision)
    }
}

impl ArrowPrimitiveType for Decimal32Type {
    type Native = i32;

    const DATA_TYPE: DataType = <Self as DecimalType>::DEFAULT_TYPE;
}

impl primitive::PrimitiveTypeSealed for Decimal32Type {}

/// The decimal type for a Decimal64Array
#[derive(Debug)]
pub struct Decimal64Type {}

impl DecimalType for Decimal64Type {
    const BYTE_LENGTH: usize = 8;
    const MAX_PRECISION: u8 = DECIMAL64_MAX_PRECISION;
    const MAX_SCALE: i8 = DECIMAL64_MAX_SCALE;
    const MAX_FOR_EACH_PRECISION: &'static [i64] =
        &arrow_data::decimal::MAX_DECIMAL64_FOR_EACH_PRECISION;
    const TYPE_CONSTRUCTOR: fn(u8, i8) -> DataType = DataType::Decimal64;
    const DEFAULT_TYPE: DataType =
        DataType::Decimal64(DECIMAL64_MAX_PRECISION, DECIMAL64_DEFAULT_SCALE);
    const PREFIX: &'static str = "Decimal64";

    fn format_decimal(value: Self::Native, precision: u8, scale: i8) -> String {
        format_decimal_str(&value.to_string(), precision as usize, scale)
    }

    fn validate_decimal_precision(num: i64, precision: u8, scale: i8) -> Result<(), ArrowError> {
        validate_decimal64_precision(num, precision, scale)
    }

    fn is_valid_decimal_precision(value: Self::Native, precision: u8) -> bool {
        is_validate_decimal64_precision(value, precision)
    }
}

impl ArrowPrimitiveType for Decimal64Type {
    type Native = i64;

    const DATA_TYPE: DataType = <Self as DecimalType>::DEFAULT_TYPE;
}

impl primitive::PrimitiveTypeSealed for Decimal64Type {}

/// The decimal type for a Decimal128Array
#[derive(Debug)]
pub struct Decimal128Type {}

impl DecimalType for Decimal128Type {
    const BYTE_LENGTH: usize = 16;
    const MAX_PRECISION: u8 = DECIMAL128_MAX_PRECISION;
    const MAX_SCALE: i8 = DECIMAL128_MAX_SCALE;
    const MAX_FOR_EACH_PRECISION: &'static [i128] =
        &arrow_data::decimal::MAX_DECIMAL128_FOR_EACH_PRECISION;
    const TYPE_CONSTRUCTOR: fn(u8, i8) -> DataType = DataType::Decimal128;
    const DEFAULT_TYPE: DataType =
        DataType::Decimal128(DECIMAL128_MAX_PRECISION, DECIMAL_DEFAULT_SCALE);
    const PREFIX: &'static str = "Decimal128";

    fn format_decimal(value: Self::Native, precision: u8, scale: i8) -> String {
        format_decimal_str(&value.to_string(), precision as usize, scale)
    }

    fn validate_decimal_precision(num: i128, precision: u8, scale: i8) -> Result<(), ArrowError> {
        validate_decimal_precision(num, precision, scale)
    }

    fn is_valid_decimal_precision(value: Self::Native, precision: u8) -> bool {
        is_validate_decimal_precision(value, precision)
    }
}

impl ArrowPrimitiveType for Decimal128Type {
    type Native = i128;

    const DATA_TYPE: DataType = <Self as DecimalType>::DEFAULT_TYPE;
}

impl primitive::PrimitiveTypeSealed for Decimal128Type {}

/// The decimal type for a Decimal256Array
#[derive(Debug)]
pub struct Decimal256Type {}

impl DecimalType for Decimal256Type {
    const BYTE_LENGTH: usize = 32;
    const MAX_PRECISION: u8 = DECIMAL256_MAX_PRECISION;
    const MAX_SCALE: i8 = DECIMAL256_MAX_SCALE;
    const MAX_FOR_EACH_PRECISION: &'static [i256] =
        &arrow_data::decimal::MAX_DECIMAL256_FOR_EACH_PRECISION;
    const TYPE_CONSTRUCTOR: fn(u8, i8) -> DataType = DataType::Decimal256;
    const DEFAULT_TYPE: DataType =
        DataType::Decimal256(DECIMAL256_MAX_PRECISION, DECIMAL_DEFAULT_SCALE);
    const PREFIX: &'static str = "Decimal256";

    fn format_decimal(value: Self::Native, precision: u8, scale: i8) -> String {
        format_decimal_str(&value.to_string(), precision as usize, scale)
    }

    fn validate_decimal_precision(num: i256, precision: u8, scale: i8) -> Result<(), ArrowError> {
        validate_decimal256_precision(num, precision, scale)
    }

    fn is_valid_decimal_precision(value: Self::Native, precision: u8) -> bool {
        is_validate_decimal256_precision(value, precision)
    }
}

impl ArrowPrimitiveType for Decimal256Type {
    type Native = i256;

    const DATA_TYPE: DataType = <Self as DecimalType>::DEFAULT_TYPE;
}

impl primitive::PrimitiveTypeSealed for Decimal256Type {}

/// Crate private types for Byte Arrays
///
/// Not intended to be used outside this crate
pub(crate) mod bytes {
    use super::*;

    pub trait ByteArrayTypeSealed {}
    impl<O: OffsetSizeTrait> ByteArrayTypeSealed for GenericStringType<O> {}
    impl<O: OffsetSizeTrait> ByteArrayTypeSealed for GenericBinaryType<O> {}

    pub trait ByteArrayNativeType: std::fmt::Debug + Send + Sync {
        fn from_bytes_checked(b: &[u8]) -> Option<&Self>;

        /// # Safety
        ///
        /// `b` must be a valid byte sequence for `Self`
        unsafe fn from_bytes_unchecked(b: &[u8]) -> &Self;
    }

    impl ByteArrayNativeType for [u8] {
        #[inline]
        fn from_bytes_checked(b: &[u8]) -> Option<&Self> {
            Some(b)
        }

        #[inline]
        unsafe fn from_bytes_unchecked(b: &[u8]) -> &Self {
            b
        }
    }

    impl ByteArrayNativeType for str {
        #[inline]
        fn from_bytes_checked(b: &[u8]) -> Option<&Self> {
            std::str::from_utf8(b).ok()
        }

        #[inline]
        unsafe fn from_bytes_unchecked(b: &[u8]) -> &Self {
            unsafe { std::str::from_utf8_unchecked(b) }
        }
    }
}

/// A trait over the variable-size byte array types
///
/// See [Variable Size Binary Layout](https://arrow.apache.org/docs/format/Columnar.html#variable-size-binary-layout)
pub trait ByteArrayType: 'static + Send + Sync + bytes::ByteArrayTypeSealed {
    /// Type of offset i.e i32/i64
    type Offset: OffsetSizeTrait;
    /// Type for representing its equivalent rust type i.e
    /// Utf8Array will have native type has &str
    /// BinaryArray will have type as [u8]
    type Native: bytes::ByteArrayNativeType + AsRef<Self::Native> + AsRef<[u8]> + ?Sized;

    /// "Binary" or "String", for use in error messages
    const PREFIX: &'static str;

    /// Datatype of array elements
    const DATA_TYPE: DataType;

    /// Verifies that every consecutive pair of `offsets` denotes a valid slice of `values`
    fn validate(offsets: &OffsetBuffer<Self::Offset>, values: &Buffer) -> Result<(), ArrowError>;
}

/// [`ByteArrayType`] for string arrays
pub struct GenericStringType<O: OffsetSizeTrait> {
    phantom: PhantomData<O>,
}

impl<O: OffsetSizeTrait> ByteArrayType for GenericStringType<O> {
    type Offset = O;
    type Native = str;
    const PREFIX: &'static str = "String";

    const DATA_TYPE: DataType = if O::IS_LARGE {
        DataType::LargeUtf8
    } else {
        DataType::Utf8
    };

    fn validate(offsets: &OffsetBuffer<Self::Offset>, values: &Buffer) -> Result<(), ArrowError> {
        // Verify that the slice as a whole is valid UTF-8
        let validated = std::str::from_utf8(values).map_err(|e| {
            ArrowError::InvalidArgumentError(format!("Encountered non UTF-8 data: {e}"))
        })?;

        // Verify each offset is at a valid character boundary in this UTF-8 array
        for offset in offsets.iter() {
            let o = offset.as_usize();
            if !validated.is_char_boundary(o) {
                if o < validated.len() {
                    return Err(ArrowError::InvalidArgumentError(format!(
                        "Split UTF-8 codepoint at offset {o}"
                    )));
                }
                return Err(ArrowError::InvalidArgumentError(format!(
                    "Offset of {o} exceeds length of values {}",
                    validated.len()
                )));
            }
        }
        Ok(())
    }
}

/// An arrow utf8 array with i32 offsets
pub type Utf8Type = GenericStringType<i32>;
/// An arrow utf8 array with i64 offsets
pub type LargeUtf8Type = GenericStringType<i64>;

/// [`ByteArrayType`] for binary arrays
pub struct GenericBinaryType<O: OffsetSizeTrait> {
    phantom: PhantomData<O>,
}

impl<O: OffsetSizeTrait> ByteArrayType for GenericBinaryType<O> {
    type Offset = O;
    type Native = [u8];
    const PREFIX: &'static str = "Binary";

    const DATA_TYPE: DataType = if O::IS_LARGE {
        DataType::LargeBinary
    } else {
        DataType::Binary
    };

    fn validate(offsets: &OffsetBuffer<Self::Offset>, values: &Buffer) -> Result<(), ArrowError> {
        // offsets are guaranteed to be monotonically increasing and non-empty
        let max_offset = offsets.last().unwrap().as_usize();
        if values.len() < max_offset {
            return Err(ArrowError::InvalidArgumentError(format!(
                "Maximum offset of {max_offset} is larger than values of length {}",
                values.len()
            )));
        }
        Ok(())
    }
}

/// An arrow binary array with i32 offsets
pub type BinaryType = GenericBinaryType<i32>;
/// An arrow binary array with i64 offsets
pub type LargeBinaryType = GenericBinaryType<i64>;

mod byte_view {
    use crate::types::{BinaryViewType, StringViewType};

    pub trait Sealed: Send + Sync {}
    impl Sealed for StringViewType {}
    impl Sealed for BinaryViewType {}
}

/// A trait over the variable length bytes view array types
pub trait ByteViewType: byte_view::Sealed + 'static + PartialEq + Send + Sync {
    /// If element in array is utf8 encoded string.
    const IS_UTF8: bool;

    /// Datatype of array elements
    const DATA_TYPE: DataType = if Self::IS_UTF8 {
        DataType::Utf8View
    } else {
        DataType::BinaryView
    };

    /// "Binary" or "String", for use in displayed or error messages
    const PREFIX: &'static str;

    /// Type for representing its equivalent rust type i.e
    /// Utf8Array will have native type has &str
    /// BinaryArray will have type as [u8]
    type Native: bytes::ByteArrayNativeType + AsRef<Self::Native> + AsRef<[u8]> + ?Sized;

    /// Type for owned corresponding to `Native`
    type Owned: Debug + Clone + Sync + Send + AsRef<Self::Native>;

    /// Verifies that the provided buffers are valid for this array type
    fn validate(views: &[u128], buffers: &[Buffer]) -> Result<(), ArrowError>;
}

/// [`ByteViewType`] for string arrays
#[derive(PartialEq)]
pub struct StringViewType {}

impl ByteViewType for StringViewType {
    const IS_UTF8: bool = true;
    const PREFIX: &'static str = "String";

    type Native = str;
    type Owned = String;

    fn validate(views: &[u128], buffers: &[Buffer]) -> Result<(), ArrowError> {
        validate_string_view(views, buffers)
    }
}

/// [`BinaryViewType`] for string arrays
#[derive(PartialEq)]
pub struct BinaryViewType {}

impl ByteViewType for BinaryViewType {
    const IS_UTF8: bool = false;
    const PREFIX: &'static str = "Binary";
    type Native = [u8];
    type Owned = Vec<u8>;

    fn validate(views: &[u128], buffers: &[Buffer]) -> Result<(), ArrowError> {
        validate_binary_view(views, buffers)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use arrow_data::{BufferSpec, layout};
    use chrono::DateTime;

    #[test]
    fn month_day_nano_should_roundtrip() {
        let value = IntervalMonthDayNanoType::make_value(1, 2, 3);
        assert_eq!(IntervalMonthDayNanoType::to_parts(value), (1, 2, 3));
    }

    #[test]
    fn month_day_nano_should_roundtrip_neg() {
        let value = IntervalMonthDayNanoType::make_value(-1, -2, -3);
        assert_eq!(IntervalMonthDayNanoType::to_parts(value), (-1, -2, -3));
    }

    #[test]
    fn day_time_should_roundtrip() {
        let value = IntervalDayTimeType::make_value(1, 2);
        assert_eq!(IntervalDayTimeType::to_parts(value), (1, 2));
    }

    #[test]
    fn day_time_should_roundtrip_neg() {
        let value = IntervalDayTimeType::make_value(-1, -2);
        assert_eq!(IntervalDayTimeType::to_parts(value), (-1, -2));
    }

    #[test]
    fn year_month_should_roundtrip() {
        let value = IntervalYearMonthType::make_value(1, 2);
        assert_eq!(IntervalYearMonthType::to_months(value), 14);
    }

    #[test]
    fn year_month_should_roundtrip_neg() {
        let value = IntervalYearMonthType::make_value(-1, -2);
        assert_eq!(IntervalYearMonthType::to_months(value), -14);
    }

    fn test_layout<T: ArrowPrimitiveType>() {
        let layout = layout(&T::DATA_TYPE);

        assert_eq!(layout.buffers.len(), 1);

        let spec = &layout.buffers[0];
        assert_eq!(
            spec,
            &BufferSpec::FixedWidth {
                byte_width: std::mem::size_of::<T::Native>(),
                alignment: std::mem::align_of::<T::Native>(),
            }
        );
    }

    #[test]
    fn test_layouts() {
        test_layout::<Int8Type>();
        test_layout::<Int16Type>();
        test_layout::<Int32Type>();
        test_layout::<Int64Type>();
        test_layout::<UInt8Type>();
        test_layout::<UInt16Type>();
        test_layout::<UInt32Type>();
        test_layout::<UInt64Type>();
        test_layout::<Float16Type>();
        test_layout::<Float32Type>();
        test_layout::<Float64Type>();
        test_layout::<Decimal32Type>();
        test_layout::<Decimal64Type>();
        test_layout::<Decimal128Type>();
        test_layout::<Decimal256Type>();
        test_layout::<TimestampNanosecondType>();
        test_layout::<TimestampMillisecondType>();
        test_layout::<TimestampMicrosecondType>();
        test_layout::<TimestampNanosecondType>();
        test_layout::<TimestampSecondType>();
        test_layout::<Date32Type>();
        test_layout::<Date64Type>();
        test_layout::<Time32SecondType>();
        test_layout::<Time32MillisecondType>();
        test_layout::<Time64MicrosecondType>();
        test_layout::<Time64NanosecondType>();
        test_layout::<IntervalMonthDayNanoType>();
        test_layout::<IntervalDayTimeType>();
        test_layout::<IntervalYearMonthType>();
        test_layout::<DurationNanosecondType>();
        test_layout::<DurationMicrosecondType>();
        test_layout::<DurationMillisecondType>();
        test_layout::<DurationSecondType>();
    }

    #[test]
    fn timestamp_from_datetime() {
        use chrono::{FixedOffset, NaiveDate, NaiveTime, Utc};

        // Test UTC timezone
        let date = NaiveDate::from_ymd_opt(2021, 1, 1).unwrap();
        let time = NaiveTime::from_hms_opt(12, 0, 0).unwrap();
        let naive = NaiveDateTime::new(date, time);
        let datetime_utc = Utc.from_utc_datetime(&naive);

        assert_eq!(
            TimestampSecondType::from_datetime(datetime_utc).unwrap(),
            1609502400
        );
        assert_eq!(
            TimestampMillisecondType::from_datetime(datetime_utc).unwrap(),
            1609502400000
        );
        assert_eq!(
            TimestampMicrosecondType::from_datetime(datetime_utc).unwrap(),
            1609502400000000
        );
        assert_eq!(
            TimestampNanosecondType::from_datetime(datetime_utc).unwrap(),
            1609502400000000000
        );

        // Test FixedOffset timezone (+8:00): 12:00+8 = 04:00 UTC
        let tz_plus_8 = FixedOffset::east_opt(8 * 3600).unwrap();
        let datetime_plus_8 = tz_plus_8.from_local_datetime(&naive).unwrap();
        assert_eq!(
            TimestampSecondType::from_datetime(datetime_plus_8).unwrap(),
            1609502400 - 28800
        );

        // Test subsecond precision
        let datetime = DateTime::from_timestamp(1000000000, 123456789).unwrap();
        assert_eq!(
            TimestampSecondType::from_datetime(datetime).unwrap(),
            1000000000
        );
        assert_eq!(
            TimestampMillisecondType::from_datetime(datetime).unwrap(),
            1000000000123
        );
        assert_eq!(
            TimestampMicrosecondType::from_datetime(datetime).unwrap(),
            1000000000123456
        );
        assert_eq!(
            TimestampNanosecondType::from_datetime(datetime).unwrap(),
            1000000000123456789
        );
    }

    #[test]
    fn timestamp_from_datetime_overflow() {
        // Nanosecond precision has ~584 year range (1677-2262), easily overflows
        let far_past = DateTime::from_timestamp(-10_000_000_000, 0).unwrap();
        assert!(TimestampNanosecondType::from_datetime(far_past).is_none());
        // Other precisions should handle the same DateTime without overflow
        assert!(TimestampSecondType::from_datetime(far_past).is_some());
        assert!(TimestampMillisecondType::from_datetime(far_past).is_some());
        assert!(TimestampMicrosecondType::from_datetime(far_past).is_some());

        let far_future = DateTime::from_timestamp(10_000_000_000, 0).unwrap();
        assert!(TimestampNanosecondType::from_datetime(far_future).is_none());
        assert!(TimestampSecondType::from_datetime(far_future).is_some());
        assert!(TimestampMillisecondType::from_datetime(far_future).is_some());
        assert!(TimestampMicrosecondType::from_datetime(far_future).is_some());
    }

    #[test]
    fn timestamp_from_naive_datetime() {
        use crate::temporal_conversions::as_datetime_with_timezone;
        use chrono::{NaiveDate, NaiveTime};

        let date = NaiveDate::from_ymd_opt(2021, 1, 1).unwrap();
        let time = NaiveTime::from_hms_opt(12, 0, 0).unwrap();
        let naive = NaiveDateTime::new(date, time);

        // Test UTC (tz=None)
        assert_eq!(
            TimestampSecondType::from_naive_datetime(naive, None).unwrap(),
            1609502400
        );
        assert_eq!(
            TimestampMillisecondType::from_naive_datetime(naive, None).unwrap(),
            1609502400000
        );

        // Test with timezone (-05:00): 12:00-5 = 17:00 UTC
        let tz: Tz = "-05:00".parse().unwrap();
        let ts_sec = TimestampSecondType::from_naive_datetime(naive, Some(&tz)).unwrap();
        assert_eq!(ts_sec, 1609502400 + 5 * 3600);

        // Test all types with timezone and roundtrip
        let date = NaiveDate::from_ymd_opt(2024, 6, 15).unwrap();
        let time = NaiveTime::from_hms_opt(14, 30, 45).unwrap();
        let naive = NaiveDateTime::new(date, time);
        let tz: Tz = "+01:00".parse().unwrap();

        let ts_usec = TimestampMicrosecondType::from_naive_datetime(naive, Some(&tz)).unwrap();
        let recovered = as_datetime_with_timezone::<TimestampMicrosecondType>(ts_usec, tz).unwrap();
        assert_eq!(recovered.naive_local(), naive);
    }

    #[test]
    #[cfg(feature = "chrono-tz")]
    fn timestamp_from_naive_datetime_ambiguous() {
        use chrono::{NaiveDate, NaiveTime};

        // 2024-11-03 01:30:00 in US Eastern Time is ambiguous (daylight saving time ends)
        // It can be either 01:30:00 EDT (UTC-4) or 01:30:00 EST (UTC-5)
        let date = NaiveDate::from_ymd_opt(2024, 11, 3).unwrap();
        let time = NaiveTime::from_hms_opt(1, 30, 0).unwrap();
        let naive = NaiveDateTime::new(date, time);
        let tz: Tz = "America/New_York".parse().unwrap();

        // Should return the first/earlier time (EDT, UTC-4) = 05:30:00 UTC
        let result = TimestampSecondType::from_naive_datetime(naive, Some(&tz));
        assert!(result.is_some());
        assert_eq!(result.unwrap(), 1730611800);
    }

    #[test]
    #[cfg(feature = "chrono-tz")]
    fn timestamp_from_naive_datetime_none() {
        use chrono::{NaiveDate, NaiveTime};

        // 2024-03-10 02:30:00 in US Eastern Time doesn't exist
        // (daylight saving time starts at 02:00, jumps to 03:00)
        let date = NaiveDate::from_ymd_opt(2024, 3, 10).unwrap();
        let time = NaiveTime::from_hms_opt(2, 30, 0).unwrap();
        let naive = NaiveDateTime::new(date, time);
        let tz: Tz = "America/New_York".parse().unwrap();

        // Should return None
        let result = TimestampSecondType::from_naive_datetime(naive, Some(&tz));
        assert!(result.is_none());

        // Test for all timestamp types
        assert!(TimestampMillisecondType::from_naive_datetime(naive, Some(&tz)).is_none());
        assert!(TimestampMicrosecondType::from_naive_datetime(naive, Some(&tz)).is_none());
        assert!(TimestampNanosecondType::from_naive_datetime(naive, Some(&tz)).is_none());
    }
}
