Skip to content

Commit

Permalink
Merge pull request #355 from edwinwzhe/avoid_unnecessary_tz_conversion
Browse files Browse the repository at this point in the history
Avoid unnecessary timezone conversion (#354)
  • Loading branch information
xzkostyan committed Feb 1, 2023
2 parents f27e772 + 0373bc8 commit 152ffbf
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 9 deletions.
18 changes: 9 additions & 9 deletions clickhouse_driver/columns/numpy/datetimecolumn.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ def __init__(self, timezone=None, offset_naive=True, local_timezone=None,
def apply_timezones_after_read(self, dt):
timezone = self.timezone if self.timezone else self.local_timezone

ts = pd.to_datetime(dt, utc=True).tz_convert(timezone)

if self.offset_naive:
if self.offset_naive and timezone.zone != 'UTC':
ts = pd.to_datetime(dt, utc=True).tz_convert(timezone)
ts = ts.tz_localize(None)
return ts.to_numpy(self.datetime_dtype)

return ts.to_numpy(self.datetime_dtype)
return dt

def apply_timezones_before_write(self, items):
if isinstance(items, pd.DatetimeIndex):
Expand Down Expand Up @@ -120,24 +120,24 @@ def create_numpy_datetime_column(spec, column_options):

context = column_options['context']

tz_name = timezone = None
tz_name = None
offset_naive = True

# As Numpy do not use local timezone for converting timestamp to
# datetime we need always detect local timezone for manual converting.
local_timezone = get_localzone_name_compat()
local_tz_name = get_localzone_name_compat()

# Use column's timezone if it's specified.
if spec and spec[-1] == ')':
tz_name = spec[1:-2]
offset_naive = False
else:
if not context.settings.get('use_client_time_zone', False):
if local_timezone != context.server_info.timezone:
if local_tz_name != context.server_info.timezone:
tz_name = context.server_info.timezone

if tz_name:
timezone = get_timezone(tz_name)
timezone = get_timezone(tz_name) if tz_name else None
local_timezone = get_timezone(local_tz_name) if local_tz_name else None

return cls(timezone=timezone, offset_naive=offset_naive,
local_timezone=local_timezone, **column_options)
72 changes: 72 additions & 0 deletions tests/numpy/columns/test_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,78 @@ def test_datetime_with_timezone_column_use_client_timezone(self):
inserted[0], self.make_tz_numpy_array(dt, self.col_tz_name)
)

@require_server_version(1, 1, 54337)
def test_read_tz_aware_column(self):
# read data from tz aware column Asia/Novosibirsk
# offset_naive is False -> tz convert not needed

with self.create_table(self.table_columns(with_tz=True)):
with patch.object(
pd, 'to_datetime', wraps=pd.to_datetime
) as to_datetime_spy:
self.client.execute(
'INSERT INTO test (a) VALUES', [self.dt_arr], columnar=True
)

self.emit_cli(
"INSERT INTO test (a) VALUES ('2017-07-14 05:40:00')",
)

to_datetime_calls_before_read = to_datetime_spy.call_count

query = 'SELECT * FROM test'
inserted = self.client.execute(query, columnar=True)

self.assertEqual(
to_datetime_calls_before_read,
to_datetime_spy.call_count
)

self.assertArraysEqual(
inserted[0],
self.make_tz_numpy_array(self.dt, self.col_tz_name)
)

@require_server_version(1, 1, 54337)
def test_read_tz_naive_column_with_client_timezone(self):
# read data from column without timezone
# client timezone = Asia/Novosibirsk
# offset_naive is True and timezone is not UTC -> tz convert needed

settings = {'use_client_time_zone': True}

with patch_env_tz('Asia/Novosibirsk'):
with self.create_table(self.table_columns()):
with patch.object(
pd, 'to_datetime', wraps=pd.to_datetime
) as to_datetime_spy:
self.client.execute(
'INSERT INTO test (a) VALUES', [self.dt_arr],
settings=settings, columnar=True
)

self.emit_cli(
"INSERT INTO test (a) VALUES ('2017-07-14 05:40:00')",
use_client_time_zone=1
)

to_datetime_calls_before_read = to_datetime_spy.call_count

query = 'SELECT * FROM test'
inserted = self.client.execute(
query, settings=settings, columnar=True
)

self.assertEqual(
to_datetime_calls_before_read + 2,
to_datetime_spy.call_count
)

self.assertArraysEqual(
inserted[0],
self.make_numpy_d64ns([self.dt_str] * 2)
)


class DateTime64TimezonesTestCase(DateTimeTimezonesTestCase):
dt_type = 'DateTime64'
Expand Down

0 comments on commit 152ffbf

Please sign in to comment.