Skip to content
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

unix datetime tests fail if TZ != UTC #33

Closed
mgorny opened this issue Apr 24, 2022 · 9 comments · Fixed by #81
Closed

unix datetime tests fail if TZ != UTC #33

mgorny opened this issue Apr 24, 2022 · 9 comments · Fixed by #81

Comments

@mgorny
Copy link
Contributor

mgorny commented Apr 24, 2022

The following tests fail if the system timezone is not UTC:

FAILED tests/test_datetime.py::test_is_datetime[unix-int] - assert 946684800 == IsDatetime(approx=datetime.datetime(2000, 1, 1, 0, 0...
FAILED tests/test_datetime.py::test_is_datetime[unix-float] - assert 946684800.123 == IsDatetime(approx=datetime.datetime(2000, 1, 1...
FAILED tests/test_docs.py::test_docs_examples[dirty_equals/_datetime.py:45-58] - assert 946684800.123 == IsDatetime(approx=datetime....

Full output:

============================================================== FAILURES ===============================================================
_____________________________________________________ test_is_datetime[unix-int] ______________________________________________________

value = 946684800, dirty = IsDatetime(approx=datetime.datetime(2000, 1, 1, 0, 0), unix_number=True), expect_match = True

    @pytest.mark.parametrize(
        'value,dirty,expect_match',
        [
            pytest.param(datetime(2000, 1, 1), IsDatetime(approx=datetime(2000, 1, 1)), True, id='same'),
            # Note: this requires the system timezone to be UTC
            pytest.param(946684800, IsDatetime(approx=datetime(2000, 1, 1), unix_number=True), True, id='unix-int'),
            # Note: this requires the system timezone to be UTC
            pytest.param(946684800.123, IsDatetime(approx=datetime(2000, 1, 1), unix_number=True), True, id='unix-float'),
            pytest.param(946684800, IsDatetime(approx=datetime(2000, 1, 1)), False, id='unix-different'),
            pytest.param(
                '2000-01-01T00:00', IsDatetime(approx=datetime(2000, 1, 1), iso_string=True), True, id='iso-string-true'
            ),
            pytest.param('2000-01-01T00:00', IsDatetime(approx=datetime(2000, 1, 1)), False, id='iso-string-different'),
            pytest.param('broken', IsDatetime(approx=datetime(2000, 1, 1)), False, id='iso-string-wrong'),
            pytest.param(
                '28/01/87', IsDatetime(approx=datetime(1987, 1, 28), format_string='%d/%m/%y'), True, id='string-format'
            ),
            pytest.param('28/01/87', IsDatetime(approx=datetime(2000, 1, 1)), False, id='string-format-different'),
            pytest.param('foobar', IsDatetime(approx=datetime(2000, 1, 1)), False, id='string-format-wrong'),
            pytest.param(datetime.now().isoformat(), IsNow(iso_string=True), True, id='isnow-str-true'),
            pytest.param(datetime(2000, 1, 1).isoformat(), IsNow(iso_string=True), False, id='isnow-str-different'),
            pytest.param([1, 2, 3], IsDatetime(approx=datetime(2000, 1, 1)), False, id='wrong-type'),
            pytest.param(
                datetime(2020, 1, 1, 12, 13, 14), IsDatetime(approx=datetime(2020, 1, 1, 12, 13, 14)), True, id='tz-same'
            ),
            pytest.param(
                datetime(2020, 1, 1, 12, 13, 14, tzinfo=timezone.utc),
                IsDatetime(approx=datetime(2020, 1, 1, 12, 13, 14), enforce_tz=False),
                True,
                id='tz-utc',
            ),
            pytest.param(
                datetime(2020, 1, 1, 12, 13, 14, tzinfo=timezone.utc),
                IsDatetime(approx=datetime(2020, 1, 1, 12, 13, 14)),
                False,
                id='tz-utc-different',
            ),
            pytest.param(
                datetime(2020, 1, 1, 12, 13, 14),
                IsDatetime(approx=datetime(2020, 1, 1, 12, 13, 14, tzinfo=timezone.utc), enforce_tz=False),
                False,
                id='tz-approx-tz',
            ),
            pytest.param(
                datetime(2020, 1, 1, 12, 13, 14, tzinfo=timezone(offset=timedelta(hours=1))),
                IsDatetime(approx=datetime(2020, 1, 1, 12, 13, 14), enforce_tz=False),
                True,
                id='tz-1-hour',
            ),
            pytest.param(
                pytz.timezone('Europe/London').localize(datetime(2022, 2, 15, 15, 15)),
                IsDatetime(
                    approx=pytz.timezone('America/New_York').localize(datetime(2022, 2, 15, 10, 15)), enforce_tz=False
                ),
                True,
                id='tz-both-tz',
            ),
            pytest.param(
                pytz.timezone('Europe/London').localize(datetime(2022, 2, 15, 15, 15)),
                IsDatetime(approx=pytz.timezone('America/New_York').localize(datetime(2022, 2, 15, 10, 15))),
                False,
                id='tz-both-tz-different',
            ),
            pytest.param(datetime(2000, 1, 1), IsDatetime(ge=datetime(2000, 1, 1)), True, id='ge'),
            pytest.param(datetime(1999, 1, 1), IsDatetime(ge=datetime(2000, 1, 1)), False, id='ge-not'),
            pytest.param(datetime(2000, 1, 2), IsDatetime(gt=datetime(2000, 1, 1)), True, id='gt'),
            pytest.param(datetime(2000, 1, 1), IsDatetime(gt=datetime(2000, 1, 1)), False, id='gt-not'),
        ],
    )
    def test_is_datetime(value, dirty, expect_match):
        if expect_match:
>           assert value == dirty
E           assert 946684800 == IsDatetime(approx=datetime.datetime(2000, 1, 1, 0, 0), unix_number=True)

tests/test_datetime.py:80: AssertionError
____________________________________________________ test_is_datetime[unix-float] _____________________________________________________

value = 946684800.123, dirty = IsDatetime(approx=datetime.datetime(2000, 1, 1, 0, 0), unix_number=True), expect_match = True

    @pytest.mark.parametrize(
        'value,dirty,expect_match',
        [
            pytest.param(datetime(2000, 1, 1), IsDatetime(approx=datetime(2000, 1, 1)), True, id='same'),
            # Note: this requires the system timezone to be UTC
            pytest.param(946684800, IsDatetime(approx=datetime(2000, 1, 1), unix_number=True), True, id='unix-int'),
            # Note: this requires the system timezone to be UTC
            pytest.param(946684800.123, IsDatetime(approx=datetime(2000, 1, 1), unix_number=True), True, id='unix-float'),
            pytest.param(946684800, IsDatetime(approx=datetime(2000, 1, 1)), False, id='unix-different'),
            pytest.param(
                '2000-01-01T00:00', IsDatetime(approx=datetime(2000, 1, 1), iso_string=True), True, id='iso-string-true'
            ),
            pytest.param('2000-01-01T00:00', IsDatetime(approx=datetime(2000, 1, 1)), False, id='iso-string-different'),
            pytest.param('broken', IsDatetime(approx=datetime(2000, 1, 1)), False, id='iso-string-wrong'),
            pytest.param(
                '28/01/87', IsDatetime(approx=datetime(1987, 1, 28), format_string='%d/%m/%y'), True, id='string-format'
            ),
            pytest.param('28/01/87', IsDatetime(approx=datetime(2000, 1, 1)), False, id='string-format-different'),
            pytest.param('foobar', IsDatetime(approx=datetime(2000, 1, 1)), False, id='string-format-wrong'),
            pytest.param(datetime.now().isoformat(), IsNow(iso_string=True), True, id='isnow-str-true'),
            pytest.param(datetime(2000, 1, 1).isoformat(), IsNow(iso_string=True), False, id='isnow-str-different'),
            pytest.param([1, 2, 3], IsDatetime(approx=datetime(2000, 1, 1)), False, id='wrong-type'),
            pytest.param(
                datetime(2020, 1, 1, 12, 13, 14), IsDatetime(approx=datetime(2020, 1, 1, 12, 13, 14)), True, id='tz-same'
            ),
            pytest.param(
                datetime(2020, 1, 1, 12, 13, 14, tzinfo=timezone.utc),
                IsDatetime(approx=datetime(2020, 1, 1, 12, 13, 14), enforce_tz=False),
                True,
                id='tz-utc',
            ),
            pytest.param(
                datetime(2020, 1, 1, 12, 13, 14, tzinfo=timezone.utc),
                IsDatetime(approx=datetime(2020, 1, 1, 12, 13, 14)),
                False,
                id='tz-utc-different',
            ),
            pytest.param(
                datetime(2020, 1, 1, 12, 13, 14),
                IsDatetime(approx=datetime(2020, 1, 1, 12, 13, 14, tzinfo=timezone.utc), enforce_tz=False),
                False,
                id='tz-approx-tz',
            ),
            pytest.param(
                datetime(2020, 1, 1, 12, 13, 14, tzinfo=timezone(offset=timedelta(hours=1))),
                IsDatetime(approx=datetime(2020, 1, 1, 12, 13, 14), enforce_tz=False),
                True,
                id='tz-1-hour',
            ),
            pytest.param(
                pytz.timezone('Europe/London').localize(datetime(2022, 2, 15, 15, 15)),
                IsDatetime(
                    approx=pytz.timezone('America/New_York').localize(datetime(2022, 2, 15, 10, 15)), enforce_tz=False
                ),
                True,
                id='tz-both-tz',
            ),
            pytest.param(
                pytz.timezone('Europe/London').localize(datetime(2022, 2, 15, 15, 15)),
                IsDatetime(approx=pytz.timezone('America/New_York').localize(datetime(2022, 2, 15, 10, 15))),
                False,
                id='tz-both-tz-different',
            ),
            pytest.param(datetime(2000, 1, 1), IsDatetime(ge=datetime(2000, 1, 1)), True, id='ge'),
            pytest.param(datetime(1999, 1, 1), IsDatetime(ge=datetime(2000, 1, 1)), False, id='ge-not'),
            pytest.param(datetime(2000, 1, 2), IsDatetime(gt=datetime(2000, 1, 1)), True, id='gt'),
            pytest.param(datetime(2000, 1, 1), IsDatetime(gt=datetime(2000, 1, 1)), False, id='gt-not'),
        ],
    )
    def test_is_datetime(value, dirty, expect_match):
        if expect_match:
>           assert value == dirty
E           assert 946684800.123 == IsDatetime(approx=datetime.datetime(2000, 1, 1, 0, 0), unix_number=True)

tests/test_datetime.py:80: AssertionError
_________________________________________ test_docs_examples[dirty_equals/_datetime.py:45-58] _________________________________________

module_name = '_datetime_45_58'
source_code = '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nfrom dirty_equals import Is...string=True)\n\nassert datetime(2000, 1, 2) == IsDatetime(gt=y2k)\nassert datetime(1999, 1, 2) != IsDatetime(gt=y2k)\n'
import_execute = <function import_execute.<locals>._import_execute at 0x7f3d4b0a4dc0>

    def test_docs_examples(module_name, source_code, import_execute):
>       import_execute(module_name, source_code, True)

tests/test_docs.py:69: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
tests/test_docs.py:25: in _import_execute
    spec.loader.exec_module(module)
/usr/lib/python3.10/site-packages/_pytest/assertion/rewrite.py:168: in exec_module
    exec(co, module.__dict__)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    from dirty_equals import IsDatetime
    from datetime import datetime
    
    y2k = datetime(2000, 1, 1)
    assert datetime(2000, 1, 1) == IsDatetime(approx=y2k)
    # Note: this requires the system timezone to be UTC
    assert 946684800.123 == IsDatetime(approx=y2k, unix_number=True)
E   assert 946684800.123 == IsDatetime(approx=datetime.datetime(2000, 1, 1, 0, 0), unix_number=True)
E    +  where IsDatetime(approx=datetime.datetime(2000, 1, 1, 0, 0), unix_number=True) = IsDatetime(approx=datetime.datetime(2000, 1, 1, 0, 0), unix_number=True)

../pytest-of-mgorny/pytest-9/test_docs_examples_dirty_equal32/_datetime_45_58.py:52: AssertionError
======================================================= short test summary info =======================================================
FAILED tests/test_datetime.py::test_is_datetime[unix-int] - assert 946684800 == IsDatetime(approx=datetime.datetime(2000, 1, 1, 0, 0...
FAILED tests/test_datetime.py::test_is_datetime[unix-float] - assert 946684800.123 == IsDatetime(approx=datetime.datetime(2000, 1, 1...
FAILED tests/test_docs.py::test_docs_examples[dirty_equals/_datetime.py:45-58] - assert 946684800.123 == IsDatetime(approx=datetime....
==================================================== 3 failed, 481 passed in 1.09s ====================================================
@samuelcolvin
Copy link
Owner

Fix welcome, but the simplest fix might be to run tests with TZ=utc ....

If that's not an option, it shouldn't be hard to fix in python.

@mgorny
Copy link
Contributor Author

mgorny commented Apr 24, 2022

To be honest, I don't know if that's a problem with the tests or with the underlying logic, i.e. if it doesn't break real code.

@samuelcolvin
Copy link
Owner

I think (hope) it's just that tests assume UTC.

@dvzrv
Copy link

dvzrv commented Jul 13, 2023

Came here (late) to report the same (for Arch Linux).
I guess it would be nice if the tests were self-contained and would set this themselves, as it makes them more robust.

@samuelcolvin
Copy link
Owner

agreed, PR welcome to set the timezone for these tests.

@alexmojaki
Copy link
Contributor

Since this library is used in tests, other people using this are likely to have the same problem in their own tests. So someone might write a test, not realize that it only passes in their (not necessarily UTC) timezone, and then it mysteriously fails for someone else. We probably can't do anything about that, but when a user realises that the probem is timezones, I think it's very likely that they'll try using enforce_tz=False and be confused about that not working. Taking care of this seems like exactly the kind of thing that dirty_equals is typically meant to do, so they'll probably look for some kind of solution within dirty_equals and be surprised not to find one.

@alexmojaki
Copy link
Contributor

In fact, now that I think of it, #48 seems like a perfect demonstration of this point. @samuelcolvin you intuitively expected enforce_tz=False to make that example magically pass, right?

@samuelcolvin
Copy link
Owner

Honestly I don't remember, I just know there are dragons herein, so I'm a bit unwilling to change things unless I'm sure it's an improvement.

That being said, I'm sure it can be improved, so if you're confident this is an improvement, I'll look again.

It would be good to add docs or content to the PR body that clearly explains the change for end users.

@alexmojaki
Copy link
Contributor

OK, after seeing https://dirty-equals.helpmanual.io/latest/types/datetime/#timezones I agree about the dragons and I don't want to complicate things further. I've switched to the simpler fix.

samuelcolvin pushed a commit that referenced this issue Nov 14, 2023
...and a few other things that got in my way as an initial contributor.

Closes #33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants