diff --git a/ibis/backends/base/sql/glot/datatypes.py b/ibis/backends/base/sql/glot/datatypes.py index a63410f199924..535cf2d4d41f9 100644 --- a/ibis/backends/base/sql/glot/datatypes.py +++ b/ibis/backends/base/sql/glot/datatypes.py @@ -202,21 +202,14 @@ def _to_ibis_STRUCT(cls, *fields: sge.ColumnDef) -> dt.Struct: return dt.Struct(types, nullable=cls.default_nullable) @classmethod - def _to_ibis_TIMESTAMP( - cls, - scale: sge.DataTypeSize | None = None, - timezone: sge.DataTypeSize | None = None, - ) -> dt.Timestamp: + def _to_ibis_TIMESTAMP(cls, scale=None) -> dt.Timestamp: return dt.Timestamp( - timezone=None if timezone is None else timezone.this.this, scale=cls.default_temporal_scale if scale is None else int(scale.this.this), nullable=cls.default_nullable, ) @classmethod - def _to_ibis_TIMESTAMPTZ( - cls, scale: sge.DataTypeSize | None = None - ) -> dt.Timestamp: + def _to_ibis_TIMESTAMPTZ(cls, scale=None) -> dt.Timestamp: return dt.Timestamp( timezone="UTC", scale=cls.default_temporal_scale if scale is None else int(scale.this.this), @@ -280,13 +273,13 @@ def _from_ibis_Decimal(cls, dtype: dt.Decimal) -> sge.DataType: @classmethod def _from_ibis_Timestamp(cls, dtype: dt.Timestamp) -> sge.DataType: - return sge.DataType( - this=typecode.TIMESTAMP, - expressions=[ - sge.Literal.number(dtype.scale), - sge.Literal.string(dtype.timezone), - ], - ) + code = typecode.TIMESTAMP if dtype.timezone is None else typecode.TIMESTAMPTZ + if dtype.scale: + scale = sge.Literal.number(dtype.scale) + scale = sge.DataTypeSize(this=typecode.DATATYPESIZE, expressions=[scale]) + return sge.DataType(this=code, expressions=[scale]) + else: + return sge.DataType(this=code) class PostgresType(SqlglotType): diff --git a/ibis/backends/base/sql/glot/tests/test_datatypes.py b/ibis/backends/base/sql/glot/tests/test_datatypes.py index 82902f7232698..129747c3cc297 100644 --- a/ibis/backends/base/sql/glot/tests/test_datatypes.py +++ b/ibis/backends/base/sql/glot/tests/test_datatypes.py @@ -37,7 +37,7 @@ def assert_dtype_roundtrip(ibis_type, sqlglot_expected=None): | its.uuid_dtype(nullable=true) | its.date_dtype(nullable=true) | its.time_dtype(nullable=true) - | its.timestamp_dtype(nullable=true) + | its.timestamp_dtype(timezone=st.none(), nullable=true) # | its.interval_dtype(nullable=true) | its.array_dtypes(roundtripable_types, nullable=true) | its.map_dtypes(roundtripable_types, roundtripable_types, nullable=true) diff --git a/ibis/backends/clickhouse/datatypes.py b/ibis/backends/clickhouse/datatypes.py index b97cc05065dc5..db3c8b679f060 100644 --- a/ibis/backends/clickhouse/datatypes.py +++ b/ibis/backends/clickhouse/datatypes.py @@ -53,30 +53,24 @@ def _to_ibis_NULLABLE(cls, inner_type: sge.DataType) -> dt.DataType: return cls.to_ibis(inner_type, nullable=True) @classmethod - def _to_ibis_DATETIME( - cls, - first: sge.DataTypeSize | None = None, - second: sge.DataTypeSize | None = None, - ) -> dt.Timestamp: - if first is not None and second is not None: - scale = first - timezone = second - elif first is not None and second is None: - timezone, scale = ( - (first, second) if first.this.is_string else (second, first) - ) - else: - scale = first - timezone = second - return cls._to_ibis_TIMESTAMP(scale=scale, timezone=timezone) + def _to_ibis_DATETIME(cls, timezone: sge.Literal | None = None) -> dt.Timestamp: + return dt.Timestamp( + timezone=None if timezone is None else timezone.this.this, + scale=cls.default_temporal_scale, + nullable=cls.default_nullable, + ) @classmethod def _to_ibis_DATETIME64( cls, scale: sge.DataTypeSize | None = None, - timezone: sge.DataTypeSize | None = None, + timezone: sge.Literal | None = None, ) -> dt.Timestamp: - return cls._to_ibis_TIMESTAMP(scale=scale, timezone=timezone) + return dt.Timestamp( + timezone=None if timezone is None else timezone.this.this, + scale=cls.default_temporal_scale if scale is None else int(scale.this.this), + nullable=cls.default_nullable, + ) @classmethod def _to_ibis_LOWCARDINALITY(cls, inner_type: sge.DataType) -> dt.DataType: @@ -99,6 +93,7 @@ def _from_ibis_Timestamp(cls, dtype: dt.Timestamp) -> sge.DataType: else: scale = sge.Literal.number(dtype.scale) + scale = sge.DataTypeSize(this=typecode.DATATYPESIZE, expressions=[scale]) if dtype.timezone is None: return sge.DataType(this=typecode.DATETIME64, expressions=[scale]) else: diff --git a/ibis/backends/clickhouse/tests/test_types.py b/ibis/backends/clickhouse/tests/test_datatypes.py similarity index 96% rename from ibis/backends/clickhouse/tests/test_types.py rename to ibis/backends/clickhouse/tests/test_datatypes.py index 29fc743f1bc43..7fa4b81479fa6 100644 --- a/ibis/backends/clickhouse/tests/test_types.py +++ b/ibis/backends/clickhouse/tests/test_datatypes.py @@ -207,13 +207,19 @@ def test_columns_types_with_additional_argument(con): ), id="nested", ), + param("DateTime", dt.Timestamp(nullable=False), id="datetime"), + param( + "DateTime('Europe/Budapest')", + dt.Timestamp(timezone="Europe/Budapest", nullable=False), + id="datetime_timezone", + ), + param("DateTime64", dt.Timestamp(nullable=False), id="datetime64"), param( "DateTime64(0)", dt.Timestamp(scale=0, nullable=False), id="datetime64_zero" ), param( "DateTime64(1)", dt.Timestamp(scale=1, nullable=False), id="datetime64_one" ), - param("DateTime64", dt.Timestamp(nullable=False), id="datetime64"), ] + [ param(