Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 1 addition & 32 deletions datafusion/common/src/scalar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3075,38 +3075,7 @@ impl ScalarValue {
target_type: &DataType,
cast_options: &CastOptions<'static>,
) -> Result<Self> {
let scalar_array = match (self, target_type) {
(
ScalarValue::Decimal128(Some(decimal_value), _, scale),
DataType::Timestamp(time_unit, None),
) => {
let scale_factor = 10_i128.pow(*scale as u32);
let seconds = decimal_value / scale_factor;
let fraction = decimal_value % scale_factor;

let timestamp_value = match time_unit {
TimeUnit::Second => ScalarValue::Int64(Some(seconds as i64)),
TimeUnit::Millisecond => {
let millis = seconds * 1_000 + (fraction * 1_000) / scale_factor;
ScalarValue::Int64(Some(millis as i64))
}
TimeUnit::Microsecond => {
let micros =
seconds * 1_000_000 + (fraction * 1_000_000) / scale_factor;
ScalarValue::Int64(Some(micros as i64))
}
TimeUnit::Nanosecond => {
let nanos = seconds * 1_000_000_000
+ (fraction * 1_000_000_000) / scale_factor;
ScalarValue::Int64(Some(nanos as i64))
}
};

timestamp_value.to_array()?
}
_ => self.to_array()?,
};

let scalar_array = self.to_array()?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that is pretty nice fixing bugs by deleting code 🏆

let cast_arr = cast_with_options(&scalar_array, target_type, cast_options)?;
ScalarValue::try_from_array(&cast_arr, 0)
}
Expand Down
72 changes: 68 additions & 4 deletions datafusion/sqllogictest/test_files/timestamps.slt
Original file line number Diff line number Diff line change
Expand Up @@ -530,12 +530,12 @@ SELECT to_timestamp(123456789.123456789) as c1, cast(123456789.123456789 as time
query PPP
SELECT to_timestamp(arrow_cast(1.1, 'Decimal128(2,1)')) as c1, cast(arrow_cast(1.1, 'Decimal128(2,1)') as timestamp) as c2, arrow_cast(1.1, 'Decimal128(2,1)')::timestamp as c3;
----
1970-01-01T00:00:01.100 1970-01-01T00:00:01.100 1970-01-01T00:00:01.100
1970-01-01T00:00:01.100 1970-01-01T00:00:00.000000001 1970-01-01T00:00:00.000000001

query PPP
SELECT to_timestamp(arrow_cast(-1.1, 'Decimal128(2,1)')) as c1, cast(arrow_cast(-1.1, 'Decimal128(2,1)') as timestamp) as c2, arrow_cast(-1.1, 'Decimal128(2,1)')::timestamp as c3;
----
1969-12-31T23:59:58.900 1969-12-31T23:59:58.900 1969-12-31T23:59:58.900
1969-12-31T23:59:58.900 1969-12-31T23:59:59.999999999 1969-12-31T23:59:59.999999999

query PPP
SELECT to_timestamp(arrow_cast(0.0, 'Decimal128(2,1)')) as c1, cast(arrow_cast(0.0, 'Decimal128(2,1)') as timestamp) as c2, arrow_cast(0.0, 'Decimal128(2,1)')::timestamp as c3;
Expand All @@ -545,12 +545,12 @@ SELECT to_timestamp(arrow_cast(0.0, 'Decimal128(2,1)')) as c1, cast(arrow_cast(0
query PPP
SELECT to_timestamp(arrow_cast(1.23456789, 'Decimal128(9,8)')) as c1, cast(arrow_cast(1.23456789, 'Decimal128(9,8)') as timestamp) as c2, arrow_cast(1.23456789, 'Decimal128(9,8)')::timestamp as c3;
----
1970-01-01T00:00:01.234567890 1970-01-01T00:00:01.234567890 1970-01-01T00:00:01.234567890
1970-01-01T00:00:01.234567890 1970-01-01T00:00:00.000000001 1970-01-01T00:00:00.000000001

query PPP
SELECT to_timestamp(arrow_cast(123456789.123456789, 'Decimal128(18,9)')) as c1, cast(arrow_cast(123456789.123456789, 'Decimal128(18,9)') as timestamp) as c2, arrow_cast(123456789.123456789, 'Decimal128(18,9)')::timestamp as c3;
----
1973-11-29T21:33:09.123456784 1973-11-29T21:33:09.123456784 1973-11-29T21:33:09.123456784
1973-11-29T21:33:09.123456784 1970-01-01T00:00:00.123456789 1970-01-01T00:00:00.123456789


# from_unixtime
Expand Down Expand Up @@ -3529,3 +3529,67 @@ select to_timestamp('-1');

query error DataFusion error: Arrow error: Parser error: Error parsing timestamp from '\-1': timestamp must contain at least 10 characters
select to_timestamp(arrow_cast('-1', 'Utf8'));

query P
SELECT CAST(CAST(1 AS decimal(17,2)) AS timestamp(3)) AS a UNION ALL
SELECT CAST(CAST(one AS decimal(17,2)) AS timestamp(3)) AS a FROM (VALUES (1)) t(one);
----
1970-01-01T00:00:00.001
1970-01-01T00:00:00.001
Comment on lines +3534 to +3538
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the desired semantics when casting from decimal(p,s) to timestamp(q)?

For example, when casting from decimal(38,3) to timestamp(3) it could feel natural to interpret decimal's whole integer part as seconds-of-epoch, and decimal's fraction as millis-of-second.

With the current implementation, the decimal's fraction seems to be discarded.
(Should we have tests with some non-whole integer decimal values as well)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what the semantics should be when casting from decimal --> timestamp. I recommend that we add the tests @findepi suggests and file a follow on ticket to clarify this point


query P
SELECT arrow_cast(CAST(1 AS decimal(17,2)), 'Timestamp(Nanosecond, None)') AS a UNION ALL
SELECT arrow_cast(CAST(one AS decimal(17,2)), 'Timestamp(Nanosecond, None)') AS a FROM (VALUES (1)) t(one);
----
1970-01-01T00:00:00.000000001
1970-01-01T00:00:00.000000001

query P
SELECT arrow_cast(CAST(1 AS decimal(17,2)), 'Timestamp(Microsecond, None)') AS a UNION ALL
SELECT arrow_cast(CAST(one AS decimal(17,2)), 'Timestamp(Microsecond, None)') AS a FROM (VALUES (1)) t(one);
----
1970-01-01T00:00:00.000001
1970-01-01T00:00:00.000001

query P
SELECT arrow_cast(CAST(1 AS decimal(17,2)), 'Timestamp(Millisecond, None)') AS a UNION ALL
SELECT arrow_cast(CAST(one AS decimal(17,2)), 'Timestamp(Millisecond, None)') AS a FROM (VALUES (1)) t(one);
----
1970-01-01T00:00:00.001
1970-01-01T00:00:00.001

query P
SELECT arrow_cast(CAST(1 AS decimal(17,2)), 'Timestamp(Second, None)') AS a UNION ALL
SELECT arrow_cast(CAST(one AS decimal(17,2)), 'Timestamp(Second, None)') AS a FROM (VALUES (1)) t(one);
----
1970-01-01T00:00:01
1970-01-01T00:00:01


query P
SELECT arrow_cast(CAST(1.123 AS decimal(17,3)), 'Timestamp(Nanosecond, None)') AS a UNION ALL
SELECT arrow_cast(CAST(one AS decimal(17,3)), 'Timestamp(Nanosecond, None)') AS a FROM (VALUES (1.123)) t(one);
----
1970-01-01T00:00:00.000000001
1970-01-01T00:00:00.000000001

query P
SELECT arrow_cast(CAST(1.123 AS decimal(17,3)), 'Timestamp(Microsecond, None)') AS a UNION ALL
SELECT arrow_cast(CAST(one AS decimal(17,3)), 'Timestamp(Microsecond, None)') AS a FROM (VALUES (1.123)) t(one);
----
1970-01-01T00:00:00.000001
1970-01-01T00:00:00.000001

query P
SELECT arrow_cast(CAST(1.123 AS decimal(17,3)), 'Timestamp(Millisecond, None)') AS a UNION ALL
SELECT arrow_cast(CAST(one AS decimal(17,3)), 'Timestamp(Millisecond, None)') AS a FROM (VALUES (1.123)) t(one);
----
1970-01-01T00:00:00.001
1970-01-01T00:00:00.001

query P
SELECT arrow_cast(CAST(1.123 AS decimal(17,3)), 'Timestamp(Second, None)') AS a UNION ALL
SELECT arrow_cast(CAST(one AS decimal(17,3)), 'Timestamp(Second, None)') AS a FROM (VALUES (1.123)) t(one);
----
1970-01-01T00:00:01
1970-01-01T00:00:01