Skip to content

date_part returning wrong results due to overflows #14738

Description

@gabotechs

Describe the bug

When playing with the date_part function, I see that there's ways of triggering int32 multiplication overflows that either panic on a debug build, or return the wrong number at runtime.

To Reproduce

Executing the following statement shows the behavior.

SELECT date_part('microsecond', timestamp '1970-01-01T00:40:00' - timestamp '1970-01-01T00:00:00')

DataFusion fiddle link <- returns a wrong random number

Postgres fiddle link <- returns 0

Expected behavior

The date_part function should behave the same as Postgres

Additional context

Not 100% sure, but I would say that the changes introduced in #13466 look suspicious. There, the inner calculations are using int32 types, which are easy to overflow. Special mention to this line of code:
https://github.com/gabotechs/datafusion/blob/763bd681f09d58ce285ab3a677b81291c41adfce/datafusion/functions/src/datetime/date_part.rs#L290-L290

Activity

  1. gabotechs commented on Feb 18, 2025

    @gabotechs
    ContributorAuthor

    It can also be replicated with intervals:

    SELECT date_part('microseconds', interval '1 hour')
    -- returns -694967296, but the result should be 0
  2. gabotechs commented on Feb 18, 2025

    @gabotechs
    ContributorAuthor

    It seems like even without integer overflows, the overall logic is wrong:

    SELECT date_part('seconds', interval '1 hour')
    -- returns 3600, but the result should be 0
    
  3. Omega359 commented on Feb 18, 2025

    @Omega359
    Contributor

    It seems like even without integer overflows, the overall logic is wrong:

    SELECT date_part('seconds', interval '1 hour')
    -- returns 3600, but the result should be 0
    

    Hmm. I think this may be a separate issue as indeed it doesn't follow the typical pattern for that function where

    SELECT date_part('seconds', interval '1 hour 5 second');

    should return 5 (the seconds element in the interval, not the overall seconds the interval covers)

  4. alamb commented on Mar 3, 2026

    @alamb
    Contributor

    Appears to still be a problem

    andrewlamb@Andrews-MacBook-Pro-3:~/Software/arrow-rs$ datafusion-cli
    DataFusion CLI v52.1.0
    > SELECT date_part('microsecond', timestamp '1970-01-01T00:40:00' - timestamp '1970-01-01T00:00:00');
    +------------------------------------------------------------------------------------------+
    | date_part(Utf8("microsecond"),Utf8("1970-01-01T00:40:00") - Utf8("1970-01-01T00:00:00")) |
    +------------------------------------------------------------------------------------------+
    | -1894967296                                                                              |
    +------------------------------------------------------------------------------------------+
    1 row(s) fetched.
    Elapsed 0.040 seconds.
  5. buraksenn commented on Apr 13, 2026

    @buraksenn
    Contributor

    I gave this a look and was able to fix it by casting duration array to nanosecond and then extracting components. However, it seemed like a fragile workaround so I was not sure of opening a PR.

    Smth like this:

    fn duration_part(array: &dyn Array, unit: IntervalUnit) -> Result<ArrayRef> {
        const NANOS_PER_MINUTE: i64 = 60 * 1_000_000_000;
    
    
        let divisor = match unit {
            Second => 1_000_000_000,
            Millisecond => 1_000_000,
            Microsecond => 1_000,
            Nanosecond => 1,
        };
    
        let nanos_array = cast(array, &Duration(Nanosecond))?;
        let result = nanos_array.unary(|d| (d % NANOS_PER_MINUTE) / divisor);
    
        Ok(Arc::new(result))
    }
    

    Should this be solved in https://github.com/apache/arrow-rs? Otherwise I can open a PR with the approach above

  6. alamb commented on Apr 13, 2026

    @alamb
    Contributor

    Should this be solved in https://github.com/apache/arrow-rs?

    Yes I think that is probably the right approach

  7. Nisarg0007 commented on Sep 9, 2026

    @Nisarg0007

    I'd like to work on this. I reproduced the issue on v55.0.0:

    SELECT date_part(
        'microsecond',
        timestamp '1970-01-01T00:40:00'
        - timestamp '1970-01-01T00:00:00'
    );

    This currently panics in debug builds at date_part.rs:426 with attempt to multiply with overflow; release builds silently wrap and return an incorrect value.

    I also confirmed that the interval-semantics portion of this issue was fixed by #14817, so I intend to target only the remaining Duration path.

    My current plan is to perform the subsecond calculation using i64 internally while preserving the existing Int32 return type. For values outside the Int32 range, should this return an error or saturate/clamp?

    @alamb — you previously suggested the duration-part work might belong in arrow-rs. My proposed change is limited to fixing the existing i32 arithmetic in DataFusion's date_part.rs rather than introducing an upstream kernel. Would a local fix be appropriate here?

    If this approach sounds good, I'd be happy to take this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions