Repository: spulec/freezegun

Issue:
Support for calendar.timegm()
It would be awesome to support [`calendar.timegm()`](https://docs.python.org/3.7/library/calendar.html#calendar.timegm).

It's actually the inverse of `time.gmtime()` which is currently supported and frozen properly.

Discussion:

Patch:
diff --git a/freezegun/api.py b/freezegun/api.py
index bc61270..0339059 100644
--- a/freezegun/api.py
+++ b/freezegun/api.py
@@ -26,9 +26,10 @@ real_time = time.time
 real_localtime = time.localtime
 real_gmtime = time.gmtime
 real_strftime = time.strftime
+real_timegm = calendar.timegm
 real_date = datetime.date
 real_datetime = datetime.datetime
-real_date_objects = [real_time, real_localtime, real_gmtime, real_strftime, real_date, real_datetime]
+real_date_objects = [real_time, real_localtime, real_gmtime, real_strftime, real_timegm, real_date, real_datetime]
 
 if _TIME_NS_PRESENT:
     real_time_ns = time.time_ns
@@ -179,7 +180,7 @@ def fake_time():
     if _should_use_real_time():
         return real_time()
     current_time = get_current_time()
-    return calendar.timegm(current_time.timetuple()) + current_time.microsecond / 1000000.0
+    return real_timegm(current_time.timetuple()) + current_time.microsecond / 1000000.0
 
 if _TIME_NS_PRESENT:
     def fake_time_ns():
@@ -215,6 +216,14 @@ def fake_strftime(format, time_to_format=None):
     else:
         return real_strftime(format, time_to_format)
 
+
+def fake_timegm(struct_time):
+    if _should_use_real_time():
+        return real_timegm(struct_time)
+    else:
+        return real_timegm(get_current_time().timetuple())
+
+
 if real_clock is not None:
     def fake_clock():
         if _should_use_real_time():
@@ -586,6 +595,8 @@ class _freeze_time(object):
             return freeze_factory
 
         # Change the modules
+        calendar.timegm = fake_timegm
+
         datetime.datetime = FakeDatetime
         datetime.date = FakeDate
 
@@ -609,6 +620,7 @@ class _freeze_time(object):
             ('real_localtime', real_localtime, fake_localtime),
             ('real_strftime', real_strftime, fake_strftime),
             ('real_time', real_time, fake_time),
+            ('real_timegm', real_timegm, fake_timegm),
         ]
 
         if _TIME_NS_PRESENT:
@@ -655,6 +667,7 @@ class _freeze_time(object):
         tz_offsets.pop()
 
         if not freeze_factories:
+            calendar.timegm = real_timegm
             datetime.datetime = real_datetime
             datetime.date = real_date
             copyreg.dispatch_table.pop(real_datetime)


Test patch:
diff --git a/tests/another_module.py b/tests/another_module.py
index 637c889..936bd7d 100644
--- a/tests/another_module.py
+++ b/tests/another_module.py
@@ -1,3 +1,4 @@
+from calendar import timegm
 from datetime import date, datetime
 from time import time, localtime, gmtime, strftime
 
@@ -8,6 +9,7 @@ from freezegun.api import (
     fake_localtime,
     fake_gmtime,
     fake_strftime,
+    fake_timegm,
 )
 
 
@@ -37,6 +39,10 @@ def get_strftime():
     return strftime
 
 
+def get_timegm():
+    return timegm
+
+
 # Fakes
 
 def get_fake_datetime():
@@ -61,3 +67,7 @@ def get_fake_gmtime():
 
 def get_fake_strftime():
     return fake_strftime
+
+
+def get_fake_timegm():
+    return fake_timegm
diff --git a/tests/test_class_import.py b/tests/test_class_import.py
index 1c98215..36a6178 100644
--- a/tests/test_class_import.py
+++ b/tests/test_class_import.py
@@ -1,3 +1,5 @@
+import calendar
+import datetime
 import time
 import sys
 from .fake_module import (
@@ -17,8 +19,8 @@ from freezegun.api import (
     fake_localtime,
     fake_gmtime,
     fake_strftime,
+    fake_timegm,
 )
-import datetime
 
 
 @freeze_time("2012-01-14")
@@ -148,6 +150,8 @@ def test_import_after_start():
         assert another_module.get_gmtime() is fake_gmtime
         assert another_module.get_strftime() is time.strftime
         assert another_module.get_strftime() is fake_strftime
+        assert another_module.get_timegm() is calendar.timegm
+        assert another_module.get_timegm() is fake_timegm
 
         # Fakes
         assert another_module.get_fake_datetime() is FakeDatetime
@@ -156,6 +160,7 @@ def test_import_after_start():
         assert another_module.get_fake_localtime() is fake_localtime
         assert another_module.get_fake_gmtime() is fake_gmtime
         assert another_module.get_fake_strftime() is fake_strftime
+        assert another_module.get_fake_timegm() is fake_timegm
 
     # Reals
     assert another_module.get_datetime() is datetime.datetime
@@ -170,6 +175,8 @@ def test_import_after_start():
     assert not another_module.get_gmtime() is fake_gmtime
     assert another_module.get_strftime() is time.strftime
     assert not another_module.get_strftime() is fake_strftime
+    assert another_module.get_timegm() is calendar.timegm
+    assert not another_module.get_timegm() is fake_timegm
 
     # Fakes
     assert another_module.get_fake_datetime() is FakeDatetime
@@ -178,6 +185,7 @@ def test_import_after_start():
     assert another_module.get_fake_localtime() is fake_localtime
     assert another_module.get_fake_gmtime() is fake_gmtime
     assert another_module.get_fake_strftime() is fake_strftime
+    assert another_module.get_fake_timegm() is fake_timegm
 
 
 def test_none_as_initial():
diff --git a/tests/test_datetimes.py b/tests/test_datetimes.py
index edd4a27..12da8b9 100644
--- a/tests/test_datetimes.py
+++ b/tests/test_datetimes.py
@@ -1,4 +1,5 @@
 import time
+import calendar
 import datetime
 import unittest
 import locale
@@ -210,6 +211,13 @@ def test_time_gmtime():
         assert time_struct.tm_isdst == -1
 
 
+def test_calendar_timegm():
+    time_struct = time.gmtime()
+    assert calendar.timegm(time_struct) != 1326511294
+    with freeze_time('2012-01-14 03:21:34'):
+        assert calendar.timegm(time_struct) == 1326511294
+
+
 @pytest.mark.skipif(not HAS_CLOCK,
                     reason="time.clock was removed in Python 3.8")
 def test_time_clock():
@@ -644,6 +652,8 @@ def test_should_use_real_time():
     from freezegun import api
     api.call_stack_inspection_limit = 100  # just to increase coverage
 
+    current_time = time.gmtime()
+
     with freeze_time(frozen):
         assert time.time() == expected_frozen
         # assert time.localtime() == expected_frozen_local
@@ -653,6 +663,8 @@ def test_should_use_real_time():
         if HAS_TIME_NS:
             assert time.time_ns() == expected_frozen * 1e9
 
+        assert calendar.timegm(current_time) == expected_frozen
+
     with freeze_time(frozen, ignore=['_pytest', 'nose']):
         assert time.time() != expected_frozen
         # assert time.localtime() != expected_frozen_local
@@ -662,6 +674,8 @@ def test_should_use_real_time():
         if HAS_TIME_NS:
             assert time.time_ns() != expected_frozen * 1e9
 
+        assert calendar.timegm(current_time) != expected_frozen
+
 
 @pytest.mark.skipif(not HAS_TIME_NS,
                     reason="time.time_ns is present only on 3.7 and above")