Skip to content

Commit c7212b2

Browse files
amotlmatriv
andcommitted
Types: Unlock supporting timezone-aware DateTime fields
Co-authored-by: Marios Trivyzas <[email protected]>
1 parent 877ebaa commit c7212b2

File tree

3 files changed

+79
-9
lines changed

3 files changed

+79
-9
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
## Unreleased
55
- Added/reactivated documentation as `sqlalchemy-cratedb`
6+
- Unlocked supporting timezone-aware `DateTime` fields
67

78
## 2024/06/13 0.37.0
89
- Added support for CrateDB's [FLOAT_VECTOR] data type and its accompanying

src/sqlalchemy_cratedb/dialect.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
CrateTypeCompiler,
3232
CrateDDLCompiler
3333
)
34-
from crate.client.exceptions import TimezoneUnawareException
3534
from .sa_version import SA_VERSION, SA_1_4, SA_2_0
3635
from .type import FloatVector, ObjectArray, ObjectType
3736

@@ -113,14 +112,10 @@ def process(value):
113112

114113
class DateTime(sqltypes.DateTime):
115114

116-
TZ_ERROR_MSG = "Timezone aware datetime objects are not supported"
117-
118115
def bind_processor(self, dialect):
119116
def process(value):
120117
if value is not None:
121118
assert isinstance(value, datetime)
122-
if value.tzinfo is not None:
123-
raise TimezoneUnawareException(DateTime.TZ_ERROR_MSG)
124119
return value.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
125120
return value
126121
return process

tests/datetime_test.py

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@
2222
from __future__ import absolute_import
2323

2424
from datetime import datetime, tzinfo, timedelta
25+
import datetime as dt
2526
from unittest import TestCase, skipIf
2627
from unittest.mock import patch, MagicMock
2728

29+
import pytest
2830
import sqlalchemy as sa
29-
from sqlalchemy.exc import DBAPIError
30-
from sqlalchemy.orm import Session
31+
from sqlalchemy.orm import Session, sessionmaker
3132

3233
from sqlalchemy_cratedb import SA_VERSION, SA_1_4
3334

@@ -87,9 +88,82 @@ def test_date_can_handle_datetime(self):
8788
]
8889
self.session.query(self.Character).first()
8990

90-
def test_date_cannot_handle_tz_aware_datetime(self):
91+
def test_date_can_handle_tz_aware_datetime(self):
9192
character = self.Character()
9293
character.name = "Athur"
9394
character.timestamp = datetime(2009, 5, 13, 19, 19, 30, tzinfo=CST())
9495
self.session.add(character)
95-
self.assertRaises(DBAPIError, self.session.commit)
96+
97+
98+
Base = declarative_base()
99+
100+
101+
class FooBar(Base):
102+
__tablename__ = "foobar"
103+
name = sa.Column(sa.String, primary_key=True)
104+
date = sa.Column(sa.Date)
105+
datetime = sa.Column(sa.DateTime)
106+
107+
108+
@pytest.fixture
109+
def session(cratedb_service):
110+
engine = cratedb_service.database.engine
111+
session = sessionmaker(bind=engine)()
112+
113+
Base.metadata.drop_all(engine, checkfirst=True)
114+
Base.metadata.create_all(engine, checkfirst=True)
115+
return session
116+
117+
118+
@pytest.mark.skip("Test case not supported on SQLAlchemy 1.3")
119+
def test_datetime_notz(session):
120+
"""
121+
An integration test for `sa.Date` and `sa.DateTime`, not using timezones.
122+
"""
123+
124+
# Insert record.
125+
foo_item = FooBar(
126+
name="foo",
127+
date=dt.date(2009, 5, 13),
128+
datetime=dt.datetime(2009, 5, 13, 19, 19, 30),
129+
)
130+
session.add(foo_item)
131+
session.commit()
132+
session.execute(sa.text("REFRESH TABLE foobar"))
133+
134+
# Query record.
135+
result = session.execute(sa.select(FooBar.name, FooBar.date, FooBar.datetime)).mappings().first()
136+
137+
# Compare outcome.
138+
assert result["date"].year == 2009
139+
assert result["datetime"].year == 2009
140+
assert result["datetime"].tzname() is None
141+
assert result["datetime"].timetz() == dt.time(19, 19, 30)
142+
assert result["datetime"].tzinfo is None
143+
144+
145+
@pytest.mark.skip("Test case not supported on SQLAlchemy 1.3")
146+
def test_datetime_tz(session):
147+
"""
148+
An integration test for `sa.Date` and `sa.DateTime`, now using timezones.
149+
"""
150+
151+
# Insert record.
152+
foo_item = FooBar(
153+
name="foo",
154+
date=dt.date(2009, 5, 13),
155+
datetime=dt.datetime(2009, 5, 13, 19, 19, 30, tzinfo=CST()),
156+
)
157+
session.add(foo_item)
158+
session.commit()
159+
session.execute(sa.text("REFRESH TABLE foobar"))
160+
161+
# Query record.
162+
result = session.execute(sa.select(FooBar.name, FooBar.date, FooBar.datetime)).mappings().first()
163+
164+
# Compare outcome.
165+
assert result["date"].year == 2009
166+
assert result["datetime"].year == 2009
167+
assert result["datetime"].tzname() is None
168+
assert result["datetime"].timetz() == dt.time(19, 19, 30)
169+
assert result["datetime"].tzinfo is None

0 commit comments

Comments
 (0)