Skip to content

Commit 821dd68

Browse files
add timezone section for inserts
1 parent 2ad5e7e commit 821dd68

File tree

2 files changed

+96
-1
lines changed

2 files changed

+96
-1
lines changed

docs/integrations/language-clients/python/advanced-inserting.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,101 @@ df = pd.DataFrame({
129129
client.insert_df_arrow("users", df)
130130
```
131131

132+
### Time zones {#time-zones}
133+
134+
When inserting Python `datetime.datetime` objects into ClickHouse `DateTime` or `DateTime64` columns, ClickHouse Connect automatically handles timezone information. Since ClickHouse stores all DateTime values internally as timezone-naive Unix timestamps (seconds or fractional seconds since the epoch), timezone conversion happens automatically on the client side during insertion.
135+
136+
#### Timezone-aware datetime objects {#timezone-aware-datetime-objects}
137+
138+
If you insert a timezone-aware Python `datetime.datetime` object, ClickHouse Connect will automatically call `.timestamp()` to convert it to a Unix timestamp, which correctly accounts for the timezone offset. This means you can insert datetime objects from any timezone, and they will be correctly stored as their UTC equivalent timestamp.
139+
140+
```python
141+
import clickhouse_connect
142+
from datetime import datetime
143+
import pytz
144+
145+
client = clickhouse_connect.get_client()
146+
client.command("CREATE TABLE events (event_time DateTime) ENGINE Memory")
147+
148+
# Insert timezone-aware datetime objects
149+
denver_tz = pytz.timezone('America/Denver')
150+
tokyo_tz = pytz.timezone('Asia/Tokyo')
151+
152+
data = [
153+
[datetime(2023, 6, 15, 10, 30, 0, tzinfo=pytz.UTC)],
154+
[denver_tz.localize(datetime(2023, 6, 15, 10, 30, 0))],
155+
[tokyo_tz.localize(datetime(2023, 6, 15, 10, 30, 0))]
156+
]
157+
158+
client.insert('events', data, column_names=['event_time'])
159+
results = client.query("SELECT * from events")
160+
print(*results.result_rows, sep="\n")
161+
# Output:
162+
# (datetime.datetime(2023, 6, 15, 10, 30),)
163+
# (datetime.datetime(2023, 6, 15, 16, 30),)
164+
# (datetime.datetime(2023, 6, 15, 1, 30),)
165+
```
166+
167+
In this example, all three datetime objects represent different points in time because they have different timezones. Each will be correctly converted to its corresponding Unix timestamp and stored in ClickHouse.
168+
169+
:::note
170+
When using pytz, you must use the `localize()` method to attach timezone information to a naive datetime. Passing `tzinfo=` directly to the datetime constructor will use incorrect historical offsets. For UTC, `tzinfo=pytz.UTC` works correctly. See [pytz docs](https://pythonhosted.org/pytz/#localized-times-and-date-arithmetic) for more info.
171+
:::
172+
173+
#### Timezone-naive datetime objects {#timezone-naive-datetime-objects}
174+
175+
If you insert a timezone-naive Python `datetime.datetime` object (one without `tzinfo`), the `.timestamp()` method will interpret it as being in the system's local timezone. To avoid ambiguity, it's recommended to:
176+
177+
1. Always use timezone-aware datetime objects when inserting, or
178+
2. Ensure your system timezone is set to UTC, or
179+
3. Manually convert to epoch timestamps before inserting
180+
181+
```python
182+
import clickhouse_connect
183+
from datetime import datetime
184+
import pytz
185+
186+
client = clickhouse_connect.get_client()
187+
188+
# Recommended: Always use timezone-aware datetimes
189+
utc_time = datetime(2023, 6, 15, 10, 30, 0, tzinfo=pytz.UTC)
190+
client.insert('events', [[utc_time]], column_names=['event_time'])
191+
192+
# Alternative: Convert to epoch timestamp manually
193+
naive_time = datetime(2023, 6, 15, 10, 30, 0)
194+
epoch_timestamp = int(naive_time.replace(tzinfo=pytz.UTC).timestamp())
195+
client.insert('events', [[epoch_timestamp]], column_names=['event_time'])
196+
```
197+
198+
#### DateTime columns with timezone metadata {#datetime-columns-with-timezone-metadata}
199+
200+
ClickHouse columns can be defined with timezone metadata (e.g., `DateTime('America/Denver')` or `DateTime64(3, 'Asia/Tokyo')`). This metadata doesn't affect how data is stored (still as UTC timestamps), but it controls the timezone used when querying data back from ClickHouse.
201+
202+
When inserting into such columns, ClickHouse Connect converts your Python datetime to a Unix timestamp (accounting for its timezone if present). When you query the data back, ClickHouse Connect will return the datetime converted to the column's timezone, regardless of what timezone you used when inserting.
203+
204+
```python
205+
import clickhouse_connect
206+
from datetime import datetime
207+
import pytz
208+
209+
client = clickhouse_connect.get_client()
210+
211+
# Create table with Los Angeles timezone metadata
212+
client.command("CREATE TABLE events (event_time DateTime('America/Los_Angeles')) ENGINE Memory")
213+
214+
# Insert a New York time (10:30 AM EDT, which is 14:30 UTC)
215+
ny_tz = pytz.timezone("America/New_York")
216+
data = ny_tz.localize(datetime(2023, 6, 15, 10, 30, 0))
217+
client.insert("events", [[data]], column_names=["event_time"])
218+
219+
# When queried back, the time is automatically converted to Los Angeles timezone
220+
# 10:30 AM New York (UTC-4) = 14:30 UTC = 7:30 AM Los Angeles (UTC-7)
221+
results = client.query("select * from events")
222+
print(*results.result_rows, sep="\n")
223+
# Output:
224+
# (datetime.datetime(2023, 6, 15, 7, 30, tzinfo=<DstTzInfo 'America/Los_Angeles' PDT-1 day, 17:00:00 DST>),)
225+
```
226+
132227
## File inserts {#file-inserts}
133228

134229
The `clickhouse_connect.driver.tools` package includes the `insert_file` method that allows inserting data directly from the file system into an existing ClickHouse table. Parsing is delegated to the ClickHouse server. `insert_file` accepts the following parameters:

docs/integrations/language-clients/python/sqlalchemy.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ with Session(engine) as session:
127127
session.commit()
128128
```
129129

130-
## Scope and limitations
130+
## Scope and limitations {#scope-and-limitations}
131131
- Core focus: Enable SQLAlchemy Core features like `SELECT` with `JOIN`s (`INNER`, `LEFT OUTER`, `FULL OUTER`, `CROSS`), `WHERE`, `ORDER BY`, `LIMIT`/`OFFSET`, and `DISTINCT`.
132132
- `DELETE` with `WHERE` only: The dialect supports lightweight `DELETE` but requires an explicit `WHERE` clause to avoid accidental full-table deletes. To clear a table, use `TRUNCATE TABLE`.
133133
- No `UPDATE`: ClickHouse is append-optimized. The dialect does not implement `UPDATE`. If you need to change data, apply transformations upstream and re-insert, or use explicit text SQL (for example, `ALTER TABLE ... UPDATE`) at your own risk.

0 commit comments

Comments
 (0)