From ee9e35127288d5bf5901d99e17989c9bc52781b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Larra=C3=ADn?= Date: Tue, 11 Jun 2019 18:07:06 -0400 Subject: [PATCH 1/3] libs.tz_utils: remove `TZ_CL_SANTIAGO` Reference it from `base.constants` instead. --- cl_sii/dte/data_models.py | 3 ++- cl_sii/libs/tz_utils.py | 12 ++++-------- tests/test_libs_tz_utils.py | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/cl_sii/dte/data_models.py b/cl_sii/dte/data_models.py index beff1846..998bd3f7 100644 --- a/cl_sii/dte/data_models.py +++ b/cl_sii/dte/data_models.py @@ -22,6 +22,7 @@ import cl_sii.contribuyente.constants import cl_sii.rut.constants +from cl_sii.base.constants import SII_OFFICIAL_TZ from cl_sii.libs import tz_utils from cl_sii.rut import Rut @@ -327,7 +328,7 @@ class DteDataL2(DteDataL1): # constants ########################################################################### - DATETIME_FIELDS_TZ = tz_utils.TZ_CL_SANTIAGO + DATETIME_FIELDS_TZ = SII_OFFICIAL_TZ ########################################################################### # fields diff --git a/cl_sii/libs/tz_utils.py b/cl_sii/libs/tz_utils.py index 9d420f07..aa74826c 100644 --- a/cl_sii/libs/tz_utils.py +++ b/cl_sii/libs/tz_utils.py @@ -27,11 +27,7 @@ TZ_UTC = pytz.UTC # type: PytzTimezone -TZ_CL_SANTIAGO = pytz.timezone('America/Santiago') # type: PytzTimezone - -# TODO: remove -UTC = TZ_UTC -TIMEZONE_CL_SANTIAGO = TZ_CL_SANTIAGO +_TZ_CL_SANTIAGO: PytzTimezone = pytz.timezone('America/Santiago') def get_now_tz_aware() -> datetime: @@ -66,7 +62,7 @@ def convert_naive_dt_to_tz_aware(dt: datetime, tz: PytzTimezone) -> datetime: >>> dt_tz_aware_1.isoformat() '2018-10-23T04:54:13+00:00' - >>> dt_tz_aware_2 = convert_naive_dt_to_tz_aware(dt_naive, TZ_CL_SANTIAGO) + >>> dt_tz_aware_2 = convert_naive_dt_to_tz_aware(dt_naive, _TZ_CL_SANTIAGO) >>> dt_tz_aware_2 datetime.datetime(2018, 10, 23, 1, 54, 13, tzinfo=) @@ -91,7 +87,7 @@ def dt_is_aware(value: datetime) -> bool: False >>> dt_is_aware(convert_naive_dt_to_tz_aware(dt_naive, TZ_UTC)) True - >>> dt_is_aware(convert_naive_dt_to_tz_aware(dt_naive, TZ_CL_SANTIAGO)) + >>> dt_is_aware(convert_naive_dt_to_tz_aware(dt_naive, _TZ_CL_SANTIAGO)) True """ @@ -110,7 +106,7 @@ def dt_is_naive(value: datetime) -> bool: True >>> dt_is_naive(convert_naive_dt_to_tz_aware(dt_naive, TZ_UTC)) False - >>> dt_is_naive(convert_naive_dt_to_tz_aware(dt_naive, TZ_CL_SANTIAGO)) + >>> dt_is_naive(convert_naive_dt_to_tz_aware(dt_naive, _TZ_CL_SANTIAGO)) False """ diff --git a/tests/test_libs_tz_utils.py b/tests/test_libs_tz_utils.py index 4fe2145b..ad13991b 100644 --- a/tests/test_libs_tz_utils.py +++ b/tests/test_libs_tz_utils.py @@ -2,7 +2,7 @@ from cl_sii.libs.tz_utils import ( # noqa: F401 convert_naive_dt_to_tz_aware, dt_is_aware, dt_is_naive, get_now_tz_aware, - PytzTimezone, TZ_CL_SANTIAGO, TZ_UTC, + PytzTimezone, TZ_UTC, ) From b33e31a96208c82b625cd8e7909775b318c3b512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Larra=C3=ADn?= Date: Tue, 11 Jun 2019 18:14:27 -0400 Subject: [PATCH 2/3] libs.tz_utils: add `validate_dt_tz` Move from `dte.data_models` and rename. Tests not yet implemented. --- cl_sii/dte/data_models.py | 9 +-------- cl_sii/libs/tz_utils.py | 10 ++++++++++ tests/test_dte_data_models.py | 4 ---- tests/test_libs_tz_utils.py | 6 +++++- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/cl_sii/dte/data_models.py b/cl_sii/dte/data_models.py index 998bd3f7..ccd9b359 100644 --- a/cl_sii/dte/data_models.py +++ b/cl_sii/dte/data_models.py @@ -94,13 +94,6 @@ def validate_non_empty_bytes(value: bytes) -> None: raise ValueError("Bytes value length is 0.") -def validate_correct_tz(value: datetime, tz: tz_utils.PytzTimezone) -> None: - if not tz_utils.dt_is_aware(value): - raise ValueError("Value must be a timezone-aware datetime.", value) - if value.tzinfo.zone != tz.zone: # type: ignore - raise ValueError(f"Timezone of datetime value must be '{tz.zone!s}'.", value) - - @dataclasses.dataclass(frozen=True) class DteNaturalKey: @@ -407,7 +400,7 @@ def __post_init__(self) -> None: if self.firma_documento_dt is not None: if not isinstance(self.firma_documento_dt, datetime): raise TypeError("Inappropriate type of 'firma_documento_dt'.") - validate_correct_tz(self.firma_documento_dt, self.DATETIME_FIELDS_TZ) + tz_utils.validate_dt_tz(self.firma_documento_dt, self.DATETIME_FIELDS_TZ) if self.signature_value is not None: if not isinstance(self.signature_value, bytes): diff --git a/cl_sii/libs/tz_utils.py b/cl_sii/libs/tz_utils.py index aa74826c..8609c077 100644 --- a/cl_sii/libs/tz_utils.py +++ b/cl_sii/libs/tz_utils.py @@ -114,3 +114,13 @@ def dt_is_naive(value: datetime) -> bool: raise TypeError # source: 'django.utils.timezone.is_naive' @ Django 2.1.7 return value.utcoffset() is None + + +def validate_dt_tz(value: datetime, tz: PytzTimezone) -> None: + """ + Validate that ``tz`` is the timezone of ``value``. + """ + if not dt_is_aware(value): + raise ValueError("Value must be a timezone-aware datetime object.") + if value.tzinfo.zone != tz.zone: # type: ignore + raise ValueError(f"Timezone of datetime value must be '{tz.zone!s}'.", value) diff --git a/tests/test_dte_data_models.py b/tests/test_dte_data_models.py index 01d705fb..bbb67073 100644 --- a/tests/test_dte_data_models.py +++ b/tests/test_dte_data_models.py @@ -284,7 +284,3 @@ def test_validate_non_empty_str(self) -> None: def test_validate_non_empty_bytes(self) -> None: # TODO: implement for 'validate_non_empty_bytes' pass - - def test_validate_correct_tz(self) -> None: - # TODO: implement for 'validate_correct_tz' - pass diff --git a/tests/test_libs_tz_utils.py b/tests/test_libs_tz_utils.py index ad13991b..4e1a0360 100644 --- a/tests/test_libs_tz_utils.py +++ b/tests/test_libs_tz_utils.py @@ -1,7 +1,7 @@ import unittest from cl_sii.libs.tz_utils import ( # noqa: F401 - convert_naive_dt_to_tz_aware, dt_is_aware, dt_is_naive, get_now_tz_aware, + convert_naive_dt_to_tz_aware, dt_is_aware, dt_is_naive, get_now_tz_aware, validate_dt_tz, PytzTimezone, TZ_UTC, ) @@ -27,3 +27,7 @@ def test_dt_is_naive(self) -> None: # TODO: implement for 'dt_is_naive' # Reuse doctests/examples in function docstring. pass + + def test_validate_dt_tz(self) -> None: + # TODO: implement for 'validate_dt_tz' + pass From f978f3a1de865a93df46cc36e5ce2003254105cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Larra=C3=ADn?= Date: Tue, 7 May 2019 18:00:18 -0400 Subject: [PATCH 3/3] libs.tz_utils: add `convert_tz_aware_dt_to_naive` Tests not yet implemented. --- cl_sii/libs/tz_utils.py | 37 +++++++++++++++++++++++++++++++++++++ tests/test_libs_tz_utils.py | 8 +++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/cl_sii/libs/tz_utils.py b/cl_sii/libs/tz_utils.py index 8609c077..58de84aa 100644 --- a/cl_sii/libs/tz_utils.py +++ b/cl_sii/libs/tz_utils.py @@ -78,6 +78,43 @@ def convert_naive_dt_to_tz_aware(dt: datetime, tz: PytzTimezone) -> datetime: return dt_tz_aware +def convert_tz_aware_dt_to_naive(dt: datetime, tz: PytzTimezone = None) -> datetime: + """ + Convert a timezone-aware datetime object to an offset-naive one. + + Default ``tz`` is UTC. + + >>> dt_tz_aware = datetime(2018, 10, 1, 2, 30, 0, tzinfo=TZ_UTC) + >>> dt_tz_aware.isoformat() + '2018-10-01T02:30:00+00:00' + + >>> dt_naive_utc = convert_tz_aware_dt_to_naive(dt_tz_aware, TZ_UTC) + >>> dt_naive_utc.isoformat() + '2018-10-01T02:30:00' + + >>> dt_naive_cl_santiago = convert_tz_aware_dt_to_naive(dt_tz_aware, _TZ_CL_SANTIAGO) + >>> dt_naive_cl_santiago.isoformat() + '2018-09-30T23:30:00' + + >>> int((dt_naive_cl_santiago - dt_naive_utc).total_seconds() / 3600) + -3 + >>> (dt_naive_cl_santiago.date() - dt_naive_utc.date()).days + -1 + + :param dt: timezone-aware datetime + :param tz: timezone e.g. ``pytz.timezone('America/Santiago')`` + :raises ValueError: if ``dt`` is not timezone-aware + + """ + if not dt_is_aware(dt): + raise ValueError("Value must be a timezone-aware datetime object.") + + if tz is None: + tz = TZ_UTC + dt_naive = dt.astimezone(tz).replace(tzinfo=None) # type: datetime + return dt_naive + + def dt_is_aware(value: datetime) -> bool: """ Return whether datetime ``value`` is "aware". diff --git a/tests/test_libs_tz_utils.py b/tests/test_libs_tz_utils.py index 4e1a0360..a685c5a5 100644 --- a/tests/test_libs_tz_utils.py +++ b/tests/test_libs_tz_utils.py @@ -1,7 +1,8 @@ import unittest from cl_sii.libs.tz_utils import ( # noqa: F401 - convert_naive_dt_to_tz_aware, dt_is_aware, dt_is_naive, get_now_tz_aware, validate_dt_tz, + 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, ) @@ -18,6 +19,11 @@ def test_convert_naive_dt_to_tz_aware(self) -> None: # Reuse doctests/examples in function docstring. pass + def test_convert_tz_aware_dt_to_naive(self) -> None: + # TODO: implement for 'convert_tz_aware_dt_to_naive' + # Reuse doctests/examples in function docstring. + pass + def test_dt_is_aware(self) -> None: # TODO: implement for 'dt_is_aware' # Reuse doctests/examples in function docstring.