From 809870096d1dd1e5ed7d63629f09428c12d04317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=B3mas=20=C3=81rni=20J=C3=B3nasson?= Date: Wed, 16 Oct 2019 09:00:05 +0000 Subject: [PATCH] Ensure `now()` respects DST by using `.fold` attribute --- pendulum/__init__.py | 17 +++++++++++- pyproject.toml | 2 ++ tests/datetime/test_construct.py | 46 ++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/pendulum/__init__.py b/pendulum/__init__.py index 78524b2c..a89e0369 100644 --- a/pendulum/__init__.py +++ b/pendulum/__init__.py @@ -189,8 +189,23 @@ def instance( # on a fixed offset tz = tz.utcoffset(dt).total_seconds() / 3600 + transition_rule = POST_TRANSITION + if _HAS_FOLD: + if dt.fold is not None: + transition_rule = PRE_TRANSITION + if dt.fold: + transition_rule = POST_TRANSITION + return datetime( - dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, tz=tz + dt.year, + dt.month, + dt.day, + dt.hour, + dt.minute, + dt.second, + dt.microsecond, + tz=tz, + dst_rule=transition_rule, ) diff --git a/pyproject.toml b/pyproject.toml index 95082049..43abbf8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,6 +41,8 @@ mkdocs = { version = "^1.0", python = "^3.5" } pymdown-extensions = "^6.0" pygments = "^2.2" markdown-include = "^0.5.1" +freezegun = "^0.3.15" + [tool.isort] diff --git a/tests/datetime/test_construct.py b/tests/datetime/test_construct.py index b5db944c..0ad6ed07 100644 --- a/tests/datetime/test_construct.py +++ b/tests/datetime/test_construct.py @@ -8,8 +8,10 @@ import pytest import pytz +from freezegun import freeze_time from pendulum import DateTime from pendulum.tz import timezone +from pendulum.utils._compat import PY36 from ..conftest import assert_datetime @@ -102,6 +104,50 @@ def test_now(): assert now.hour != in_paris.hour +@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") +@freeze_time("2016-03-27 00:30:00") +def test_now_dls_off(): + utc = pendulum.now("UTC") + in_paris = pendulum.now("Europe/Paris") + in_paris_from_utc = utc.in_tz("Europe/Paris") + assert in_paris.hour == 1 + assert not in_paris.is_dst() + assert in_paris.isoformat() == in_paris_from_utc.isoformat() + + +@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") +@freeze_time("2016-03-27 01:30:00") +def test_now_dls_transitioning_on(): + utc = pendulum.now("UTC") + in_paris = pendulum.now("Europe/Paris") + in_paris_from_utc = utc.in_tz("Europe/Paris") + assert in_paris.hour == 3 + assert in_paris.is_dst() + assert in_paris.isoformat() == in_paris_from_utc.isoformat() + + +@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") +@freeze_time("2016-10-30 00:30:00") +def test_now_dls_on(): + utc = pendulum.now("UTC") + in_paris = pendulum.now("Europe/Paris") + in_paris_from_utc = utc.in_tz("Europe/Paris") + assert in_paris.hour == 2 + assert in_paris.is_dst() + assert in_paris.isoformat() == in_paris_from_utc.isoformat() + + +@pytest.mark.skipif(not PY36, reason="fold attribute only present in Python 3.6+") +@freeze_time("2016-10-30 01:30:00") +def test_now_dls_transitioning_off(): + utc = pendulum.now("UTC") + in_paris = pendulum.now("Europe/Paris") + in_paris_from_utc = utc.in_tz("Europe/Paris") + assert in_paris.hour == 2 + assert not in_paris.is_dst() + assert in_paris.isoformat() == in_paris_from_utc.isoformat() + + def test_now_with_fixed_offset(): now = pendulum.now(6)