Skip to content

Add support for TIMEZONE #252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 16, 2022
Merged

Conversation

ulisesojeda
Copy link
Contributor

Add support for TIMEZONE

Fixes #27

Copy link
Contributor

@mdesmet mdesmet left a comment

Choose a reason for hiding this comment

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

Thanks for this contribution, overall looks very good.

Note that this also should be documented.

cur = trino_connection.cursor()
cur.execute('SHOW TABLES FROM information_schema')
cur.fetchall()
assert cur._request.http_headers[constants.HEADER_TIMEZONE] == "Europe/Brussels"
Copy link
Contributor

Choose a reason for hiding this comment

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

instead of the request header testing, please check using SELECT current_timezone()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Thanks

cur = trino_connection.cursor()
cur.execute('SHOW TABLES FROM information_schema')
cur.fetchall()
assert cur._request.http_headers[constants.HEADER_TIMEZONE] == get_localzone_name()
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as in test_set_timezone_in_connection.

@@ -1056,3 +1060,51 @@ def test_request_headers_role_empty(mock_get_and_post):
req.get("URL")
_, get_kwargs = get.call_args
assert_headers_with_roles(post_kwargs["headers"], None)


def assert_headers_timezone(headers: Dict[str, str], timezone: Optional[str]):
Copy link
Contributor

Choose a reason for hiding this comment

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

Make second parameter required instead of optional and move get_localzone_name() call to the test itself. Makes the test easier to follow.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure.

trino/client.py Outdated
@@ -127,6 +131,7 @@ def __init__(
self._roles = roles.copy() if roles is not None else {}
self._prepared_statements: Dict[str, str] = {}
self._object_lock = threading.Lock()
self._timezone = timezone or get_localzone_name()
Copy link
Contributor

Choose a reason for hiding this comment

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

This might be a breaking change, In the sense that suddenly the local timezone will be used compared to UTC.

I'll leave this to the maintainers to decide if we need to put it behind a feature flag or announce it as a breaking change.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, how should we proceed?

Copy link
Contributor

Choose a reason for hiding this comment

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

@hashhar , @ebyhr : Please advice.

Copy link
Member

Choose a reason for hiding this comment

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

Q: Before this change the server returned values in UTC? If yes, then won't setting timezone=UTC here explicitly preserve old behaviour? If yes then it means a workaround exists to preserve old behaviour and we can mention it in release notes.

Note that in larger picture this makes Python client match JDBC client etc. So I'd consider it a bugfix which happens to be breaking change.

Copy link
Contributor Author

@ulisesojeda ulisesojeda Nov 18, 2022

Choose a reason for hiding this comment

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

The current behavior is timezone=UTC as default.

setup.py Outdated
@@ -39,6 +39,7 @@
"pytest",
"pytest-runner",
"click",
"tzlocal",
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be removed as it is already included in the install_requires.

@ulisesojeda ulisesojeda force-pushed the support-timezone branch 3 times, most recently from 74aa1b4 to 7972aab Compare October 12, 2022 10:07
@ulisesojeda
Copy link
Contributor Author

Thanks for this contribution, overall looks very good.

Note that this also should be documented.

Done the doc. Thanks for your review and comments @mdesmet !

README.md Outdated
@@ -357,6 +357,20 @@ conn = trino.dbapi.connect(
)
```

## Timezone

Sets the time zone for the session using the time zone name. Defaults to the timezone set on your workstation.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Sets the time zone for the session using the time zone name. Defaults to the timezone set on your workstation.
The time zone for the session can be explicitly set using the official IANA time zone name. When not set the time zone defaults to the client side local timezone.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍 Thanks

trino/client.py Outdated
@@ -127,6 +131,7 @@ def __init__(
self._roles = roles.copy() if roles is not None else {}
self._prepared_statements: Dict[str, str] = {}
self._object_lock = threading.Lock()
self._timezone = timezone or get_localzone_name()
Copy link
Contributor

Choose a reason for hiding this comment

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

@hashhar , @ebyhr : Please advice.

session_tz = res[0][0]
localzone = get_localzone_name()
assert session_tz == localzone or \
(session_tz == "UTC" and localzone == "Etc/UTC") \
Copy link
Contributor

Choose a reason for hiding this comment

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

I conclude from this test if we set the timezone header to "Etc/UTC", Trino actually understands this but returns UTC as the current_timezone().

Is my understanding correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Exactly

trino/client.py Outdated
@@ -127,6 +131,7 @@ def __init__(
self._roles = roles.copy() if roles is not None else {}
self._prepared_statements: Dict[str, str] = {}
self._object_lock = threading.Lock()
self._timezone = timezone or get_localzone_name()
Copy link
Contributor

Choose a reason for hiding this comment

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

is there a way to validate the time zone using tzlocal?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. I added the check with ZoneInfo

@mdesmet mdesmet requested review from ebyhr and hashhar October 12, 2022 11:01
@ulisesojeda ulisesojeda force-pushed the support-timezone branch 2 times, most recently from 1bf9336 to 423e360 Compare October 19, 2022 07:58
@mdesmet
Copy link
Contributor

mdesmet commented Oct 27, 2022

@ulisesojeda : Please don't merge master into your branch but rebase on master.

@ulisesojeda ulisesojeda force-pushed the support-timezone branch 2 times, most recently from e611269 to 0acb5e7 Compare October 31, 2022 15:16
@ulisesojeda
Copy link
Contributor Author

@ebyhr @hashhar could you please check it?

Copy link
Member

@hashhar hashhar left a comment

Choose a reason for hiding this comment

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

Thanks for implementing this @ulisesojeda. I've started reviewing this and sorry about the delays here.

Copy link
Member

@hashhar hashhar left a comment

Choose a reason for hiding this comment

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

Nice, thanks for a lot for adding this support.

Left some comments.

@@ -47,6 +48,12 @@
_RetryWithExponentialBackoff,
)

try:
from zoneinfo._common import ZoneInfoNotFoundError # type: ignore
Copy link
Member

Choose a reason for hiding this comment

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

Can directly import zoneinfo.ZoneInfoNotFoundError - that's the public location.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

as per https://pypi.org/project/backports.zoneinfo/, with need to import the backport for Python <3.9. Did I miss something?

Copy link
Member

Choose a reason for hiding this comment

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

Oh, good point. I was checking with 3.10 only.

Can you add a TODO comment that it can be simplified once we move past 3.9?

Copy link
Member

Choose a reason for hiding this comment

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

Sorry I was saying something like this:

try:
    from zoneinfo import ZoneInfoNotFoundError
except ModuleNotFoundError:
    from backports.zoneinfo._common import ZoneInfoNotFoundError

@@ -127,6 +138,9 @@ def __init__(
self._roles = roles.copy() if roles is not None else {}
self._prepared_statements: Dict[str, str] = {}
self._object_lock = threading.Lock()
if timezone: # Check timezone validity
ZoneInfo(timezone)
Copy link
Member

Choose a reason for hiding this comment

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

Just for other reviewers this throws on invalid timezones (invalidity is determined by the OS's tzdb files or the tzdata package shipped by Python itself):

ZoneInfoNotFoundError: 'No time zone found with key Foobar'

Copy link
Member

@hashhar hashhar left a comment

Choose a reason for hiding this comment

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

LGTM % some comments.

Thanks for this.

I plan to not merge this for 0.320.0 so that other features can be part of 0.320.0 which people might be waiting for. Then 0.321.0 can introduce this change. I hope that's ok?

While there's a way to preserve old beahviour it requires someone to read release notes so I'd like to announce on Trino community before this change.

@@ -13,9 +13,4 @@ repos:
additional_dependencies:
- "types-pytz"
- "types-requests"

Copy link
Contributor

@mdesmet mdesmet Nov 18, 2022

Choose a reason for hiding this comment

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

The isort section should not be removed. Can you revert this?

Copy link
Member

Choose a reason for hiding this comment

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

good catch. I didn't notice this. 🙈

@@ -79,8 +79,8 @@
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Database :: Front-Ends",
],
python_requires=">=3.7",
install_requires=["pytz", "requests"],
python_requires='>=3.7',
Copy link
Contributor

Choose a reason for hiding this comment

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

Please revert the unrelated change on python_requires

@ulisesojeda
Copy link
Contributor Author

LGTM % some comments.

Thanks for this.

I plan to not merge this for 0.320.0 so that other features can be part of 0.320.0 which people might be waiting for. Then 0.321.0 can introduce this change. I hope that's ok?

While there's a way to preserve old beahviour it requires someone to read release notes so I'd like to announce on Trino community before this change.

Sure. Thanks for the review @hashhar

@hashhar
Copy link
Member

hashhar commented Dec 12, 2022

Rebased. Will merge once CI is done.

Co-authored-by: Ulises <[email protected]>
Co-authored-by: Ashhar Hasan <[email protected]>
@hashhar hashhar merged commit a83dcfe into trinodb:master Dec 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

The python client seems to ignore system timezone unlike Trino CLI
3 participants