Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions cl_sii/libs/tz_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,5 +166,12 @@ def validate_dt_tz(value: datetime, tz: PytzTimezone) -> None:
"""
if not dt_is_aware(value):
raise ValueError("Value must be a timezone-aware datetime object.")

# The 'zone' attribute is not defined in the abstract base class 'datetime.tzinfo'. We need to
# check that it is there before using it below to prevent unexpected exceptions when dealing
# with Python Standard Library time zones that are instances of class 'datetime.timezone'.
assert hasattr(value.tzinfo, 'zone'), f"Object {value.tzinfo!r} must have 'zone' attribute."
assert hasattr(tz, 'zone'), f"Object {tz!r} must have 'zone' attribute."

if value.tzinfo.zone != tz.zone: # type: ignore
raise ValueError(f"Timezone of datetime value must be '{tz.zone!s}'.", value)
47 changes: 46 additions & 1 deletion tests/test_libs_tz_utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import datetime
import re
import unittest

from cl_sii.libs.tz_utils import ( # noqa: F401
convert_naive_dt_to_tz_aware, convert_tz_aware_dt_to_naive,
dt_is_aware, dt_is_naive, get_now_tz_aware, validate_dt_tz,
PytzTimezone, TZ_UTC,
PytzTimezone, _TZ_CL_SANTIAGO, TZ_UTC,
)


Expand Down Expand Up @@ -37,3 +39,46 @@ def test_dt_is_naive(self) -> None:
def test_validate_dt_tz(self) -> None:
# TODO: implement for 'validate_dt_tz'
pass

def test_validate_dt_tz_tzinfo_zone_attribute_check(self) -> None:
# Time zone: UTC. Source: Pytz:
tzinfo_utc_pytz = TZ_UTC
dt_with_tzinfo_utc_pytz = convert_naive_dt_to_tz_aware(
datetime.datetime(2021, 1, 6, 15, 21),
tzinfo_utc_pytz,
)

# Time zone: UTC. Source: Python Standard Library:
tzinfo_utc_stdlib = datetime.timezone.utc
dt_with_tzinfo_utc_stdlib = datetime.datetime.fromisoformat('2021-01-06T15:04+00:00')

# Time zone: Not UTC. Source: Pytz:
tzinfo_not_utc_pytz = _TZ_CL_SANTIAGO
dt_with_tzinfo_not_utc_pytz = convert_naive_dt_to_tz_aware(
datetime.datetime(2021, 1, 6, 15, 21),
tzinfo_not_utc_pytz,
)

# Time zone: Not UTC. Source: Python Standard Library:
tzinfo_not_utc_stdlib = datetime.timezone(datetime.timedelta(days=-1, seconds=75600))
dt_with_tzinfo_not_utc_stdlib = datetime.datetime.fromisoformat('2021-01-06T15:04-03:00')

# Test datetimes with UTC time zone:
expected_error_message = re.compile(
r"^Object datetime.timezone.utc must have 'zone' attribute.$"
)
with self.assertRaisesRegex(AssertionError, expected_error_message):
validate_dt_tz(dt_with_tzinfo_utc_pytz, tzinfo_utc_stdlib)
with self.assertRaisesRegex(AssertionError, expected_error_message):
validate_dt_tz(dt_with_tzinfo_utc_stdlib, tzinfo_utc_pytz)

# Test datetimes with non-UTC time zone:
expected_error_message = re.compile(
r"^Object"
r" datetime.timezone\(datetime.timedelta\(days=-1, seconds=75600\)\)"
r" must have 'zone' attribute.$"
)
with self.assertRaisesRegex(AssertionError, expected_error_message):
validate_dt_tz(dt_with_tzinfo_not_utc_pytz, tzinfo_not_utc_stdlib) # type: ignore
with self.assertRaisesRegex(AssertionError, expected_error_message):
validate_dt_tz(dt_with_tzinfo_not_utc_stdlib, tzinfo_not_utc_pytz)