Repository: spulec/freezegun

Issue:
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
```

Discussion:

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):
     """