Repository: spulec/freezegun (2020-01-13 ~ 2025-08-09)

problem_statement: datetime.datetime.fromtimestamp returns unexpected values
`datetime.datetime.fromtimestamp` [should return](https://docs.python.org/2/library/datetime.html#datetime.date.fromtimestamp) a local date, but the system timezone is used and the `tz_offset` parameter seems ignored.

```python
import freezegun, datetime, time
with freezegun.freeze_time("1970-01-01 00:00:01"):
    print(datetime.datetime.fromtimestamp(0))

with freezegun.freeze_time("1970-01-01 00:00:01", tz_offset=6):
    print(datetime.datetime.fromtimestamp(0))
```
```bash
# freezegun behavior
$ env TZ=UTC-12 python fromtimestamp.py
1970-01-01 12:00:00
1970-01-01 12:00:00

# Expected results
1970-01-01 00:00:00
1970-01-01 06:00:00
```
date: 2020-01-13
patch: diff --git a/freezegun/api.py b/freezegun/api.py
index 13484ca..773339c 100644
--- a/freezegun/api.py
+++ b/freezegun/api.py
@@ -1,3 +1,4 @@
+import dateutil
 import datetime
 import functools
 import sys
@@ -21,6 +22,8 @@ except ImportError:
     MayaDT = None
 
 _TIME_NS_PRESENT = hasattr(time, 'time_ns')
+_EPOCH = datetime.datetime(1970, 1, 1)
+_EPOCHTZ = datetime.datetime(1970, 1, 1, tzinfo=dateutil.tz.UTC)
 
 real_time = time.time
 real_localtime = time.localtime
@@ -344,6 +347,19 @@ class FakeDatetime(with_metaclass(FakeDatetimeMeta, real_datetime, FakeDate)):
             tz = tzlocal()
         return datetime_to_fakedatetime(real_datetime.astimezone(self, tz))
 
+    @classmethod
+    def fromtimestamp(cls, t, tz=None):
+        if tz is None:
+            return real_datetime.fromtimestamp(
+                    t, tz=dateutil.tz.tzoffset("freezegun", cls._tz_offset())
+                ).replace(tzinfo=None)
+        return datetime_to_fakedatetime(real_datetime.fromtimestamp(t, tz))
+
+    def timestamp(self):
+        if self.tzinfo is None:
+            return (self - _EPOCH - self._tz_offset()).total_seconds()
+        return (self - _EPOCHTZ).total_seconds()
+
     @classmethod
     def now(cls, tz=None):
         now = cls._time_to_freeze() or real_datetime.now()

test_patch: diff --git a/tests/test_datetimes.py b/tests/test_datetimes.py
index 4689dcf..b59bd6d 100644
--- a/tests/test_datetimes.py
+++ b/tests/test_datetimes.py
@@ -5,6 +5,7 @@ import unittest
 import locale
 import sys
 from unittest import SkipTest
+from dateutil.tz import UTC
 
 import pytest
 from tests import utils
@@ -456,9 +457,7 @@ def test_nested_context_manager():
 def _assert_datetime_date_and_time_are_all_equal(expected_datetime):
     assert datetime.datetime.now() == expected_datetime
     assert datetime.date.today() == expected_datetime.date()
-    datetime_from_time = datetime.datetime.fromtimestamp(time.time())
-    timezone_adjusted_datetime = datetime_from_time + datetime.timedelta(seconds=time.timezone)
-    assert timezone_adjusted_datetime == expected_datetime
+    assert datetime.datetime.fromtimestamp(time.time()) == expected_datetime
 
 
 def test_nested_context_manager_with_tz_offsets():
@@ -693,7 +692,6 @@ def test_time_ns():
     assert time.time_ns() != expected_timestamp * 1e9
 
 
-@pytest.mark.skip("timezone handling is currently incorrect")
 def test_compare_datetime_and_time_with_timezone(monkeypatch):
     """
     Compare the result of datetime.datetime.now() and time.time() in a non-UTC timezone. These
@@ -704,13 +702,28 @@ def test_compare_datetime_and_time_with_timezone(monkeypatch):
             m.setenv("TZ", "Europe/Berlin")
             time.tzset()
 
-            dt1 = datetime.datetime.now()
-            dt2 = datetime.datetime.fromtimestamp(time.time())
-            assert dt1 == dt2
+            now = datetime.datetime.now()
+            assert now == datetime.datetime.fromtimestamp(time.time())
+            assert now == datetime.datetime.utcfromtimestamp(time.time())
+            assert now == datetime.datetime.utcnow()
+            assert now.timestamp() == time.time()
     finally:
         time.tzset()  # set the timezone back to what is was before
 
 
+def test_timestamp_with_tzoffset():
+    with freeze_time("2000-01-01", tz_offset=6):
+        utcnow = datetime.datetime(2000, 1, 1, 0)
+        nowtz = datetime.datetime(2000, 1, 1, 0, tzinfo=UTC)
+        now = datetime.datetime(2000, 1, 1, 6)
+        assert now == datetime.datetime.now()
+        assert now == datetime.datetime.fromtimestamp(time.time())
+        assert now.timestamp() == time.time()
+        assert nowtz.timestamp() == time.time()
+
+        assert utcnow == datetime.datetime.utcfromtimestamp(time.time())
+        assert utcnow == datetime.datetime.utcnow()
+
 @pytest.mark.skip("timezone handling is currently incorrect")
 def test_datetime_in_timezone(monkeypatch):
     """


----

problem_statement: Freezegun breaks pytest fixture discovery starting pytest 8.4.0
https://github.com/pytest-dev/pytest/issues/13479

Recent pytest version changed fixture definitions and now freezegun is somehow incompatible with recent pytest.

I don't know the internals of both projects, I'm just here to report. In our codebase we had similar issue to what was described in the linked issue from pytest repo.

date: 2025-06-07
patch: diff --git a/freezegun/__init__.py b/freezegun/__init__.py
--- a/freezegun/__init__.py
+++ b/freezegun/__init__.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+# noop comment for filter
 """
 freezegun
 ~~~~~~~~

test_patch: diff --git a/tests/test_class_decorator.py b/tests/test_class_decorator.py
new file mode 100644
index 0000000..4370ea4
--- /dev/null
+++ b/tests/test_class_decorator.py
@@ -0,0 +1,19 @@
+from datetime import datetime
+from unittest import TestCase
+
+import pytest
+from freezegun import freeze_time
+from freezegun.api import FakeDatetime
+
+@freeze_time("2022-10-01")
+class TestClassDecoratorWithFixture:
+    @pytest.fixture
+    def ff(self) -> datetime:
+        return datetime.now()
+
+    def test_with_fixture(self, ff: datetime) -> None:
+        assert ff == FakeDatetime(2022, 10, 1, 0, 0)
+        assert datetime.now() == FakeDatetime(2022, 10, 1, 0, 0)
+
+    def test_without_fixture(self) -> None:
+        assert datetime.now() == FakeDatetime(2022, 10, 1, 0, 0)


----

problem_statement: freezegun 1.5.4 crashes if parametrized argument is called "func"
Likely introduced by this commit: https://github.com/spulec/freezegun/commit/10c93f2d6c98f82e54470b05144cd0f17d3e1b46

Reproducer:

```python
import datetime
import freezegun
import pytest

@pytest.mark.parametrize("func", ("a", "b"))
@freezegun.freeze_time(datetime.datetime.now())
def test_freezegun_crashes(func):
    print(func)

@pytest.mark.parametrize("arg", ("a", "b"))
@freezegun.freeze_time(datetime.datetime.now())
def test_freezegun_does_not_crash(arg):
    print(arg)
```

If a test has an argument called `func`, pytest will crash:

```
item = <Function test_freezegun_crashes[a]>

    @pytest.hookimpl(wrapper=True, tryfirst=True)
    def pytest_runtest_call(item):
>       result = yield

/path/to/venv/lib/python3.11/site-packages/pytestqt/plugin.py:189: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

args = (), kwargs = {'func': 'a'}, time_factory = <freezegun.api.FrozenDateTimeFactory object at 0x727db48800d0>

    @functools.wraps(func)
    def wrapper(*args: "P.args", **kwargs: "P.kwargs") -> T:
        with self as time_factory:
>           return self._call_with_time_factory(time_factory, func, *args, **kwargs)
E           TypeError: _freeze_time._call_with_time_factory() got multiple values for argument 'func'
```

date: 2025-08-09
patch: diff --git a/freezegun/__init__.py b/freezegun/__init__.py
--- a/freezegun/__init__.py
+++ b/freezegun/__init__.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+# noop comment for filter
 # noop comment for filter
 """
 freezegun