Repository: joke2k/faker

Issue:
Tests: OverflowError on i686 and mips64el
test_date_time_this_period and test_date_time_this_period_with_tzinfo fail on i686 and mips64el. For details please see
- http://hydra.gnu.org/build/1542859 (i686)
- http://hydra.gnu.org/build/1542830 (mips64el)
- http://hydra.gnu.org/build/1542764 (x86_64, passing)

```
ERROR: test_date_time_this_period (faker.tests.FactoryTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/guix-build-python-fake-factory-0.7.2.drv-0/fake-factory-0.7.2/faker/tests/__init__.py", line 376, in test_date_time_this_period
    self.assertTrue(self._datetime_to_time(provider.date_time_this_century(before_now=False, after_now=True)) >= self._datetime_to_time(datetime.datetime.now()))
  File "/tmp/guix-build-python-fake-factory-0.7.2.drv-0/fake-factory-0.7.2/faker/providers/date_time/__init__.py", line 387, in date_time_this_century
    return cls.date_time_between_dates(now, next_century_start, tzinfo)
  File "/tmp/guix-build-python-fake-factory-0.7.2.drv-0/fake-factory-0.7.2/faker/providers/date_time/__init__.py", line 365, in date_time_between_dates
    datetime_to_timestamp(datetime_end),
  File "/tmp/guix-build-python-fake-factory-0.7.2.drv-0/fake-factory-0.7.2/faker/providers/date_time/__init__.py", line 22, in datetime_to_timestamp
    return mktime(dt.timetuple())
OverflowError: mktime argument out of range

======================================================================
ERROR: test_date_time_this_period_with_tzinfo (faker.tests.FactoryTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/guix-build-python-fake-factory-0.7.2.drv-0/fake-factory-0.7.2/faker/tests/__init__.py", line 405, in test_date_time_this_period_with_tzinfo
    provider.date_time_this_century(before_now=False, after_now=True, tzinfo=utc) >= datetime.datetime.now()
  File "/tmp/guix-build-python-fake-factory-0.7.2.drv-0/fake-factory-0.7.2/faker/providers/date_time/__init__.py", line 387, in date_time_this_century
    return cls.date_time_between_dates(now, next_century_start, tzinfo)
  File "/tmp/guix-build-python-fake-factory-0.7.2.drv-0/fake-factory-0.7.2/faker/providers/date_time/__init__.py", line 365, in date_time_between_dates
    datetime_to_timestamp(datetime_end),
  File "/tmp/guix-build-python-fake-factory-0.7.2.drv-0/fake-factory-0.7.2/faker/providers/date_time/__init__.py", line 21, in datetime_to_timestamp
    dt = dt.astimezone(tzlocal())
  File "/gnu/store/3wppcdw10k68aiw2x1r0x2m9zd10lg63-python-dateutil-2.5.2/lib/python3.4/site-packages/python_dateutil-2.5.2-py3.4.egg/dateutil/tz/tz.py", line 99, in utcoffset
    if self._isdst(dt):
  File "/gnu/store/3wppcdw10k68aiw2x1r0x2m9zd10lg63-python-dateutil-2.5.2/lib/python3.4/site-packages/python_dateutil-2.5.2-py3.4.egg/dateutil/tz/tz.py", line 143, in _isdst
    return time.localtime(timestamp+time.timezone).tm_isdst
OverflowError: timestamp out of range for platform time_t
```



Discussion:
See:
http://stackoverflow.com/questions/2518706/python-mktime-overflow-error
https://docs.python.org/2/library/time.html#time.mktime
"The earliest date for which it can generate a time is platform-dependent."

So can you please disable the respective tests on i686?
Disabling tests is a bad idea since it covers problems.

The bug is caused by [this line](https://github.com/joke2k/faker/blob/v0.7.2/faker/providers/date_time/__init__.py#L382) creating a value which not accepted at these platforms:
```
next_century_start = datetime(this_century_start.year + 100, 1, 1, tzinfo=tzinfo)
```
The same problem theoretically exists in `date_time_this_decade()` with `next_decade()` for the case the tests are run near the end of the supported year-range.

The solution is to use
```
next_century_start = datetime(min(this_century_start.year + 100, datetime.date.max.year),
                              1, 1, tzinfo=tzinfo)
```
Same for `date_time_this_decade()`.

Maybe you also want to issue a warning if this case occurs.
`datetime.MAXYEAR` is the same as `datetime.date.max.year`. You may prefer this 
Oh, well, this might analysis might be wrong: `MAXYEAR` [is defined to be 9999](https://docs.python.org/2/library/datetime.html#datetime.MAXYEAR).
I can switch behavior for 32bit system to something like solution suggested by @htgoebel. What should the max year be on 32bit systems?
Depending this on 32-bit is dangerous and wrong: [mips64el is 64-bit](https://en.wikipedia.org/wiki/MIPS_architecture#MIPS32.2FMIPS64).

I'm afraid you need to have a deeper look into your code to find out where you query the boundary.

Resolving patch:
diff --git a/faker/providers/date_time/__init__.py b/faker/providers/date_time/__init__.py
--- a/faker/providers/date_time/__init__.py
+++ b/faker/providers/date_time/__init__.py
@@ -5,7 +5,7 @@
 import re
 
 from calendar import timegm
-from datetime import timedelta
+from datetime import timedelta, MAXYEAR
 from time import time
 
 from dateutil import relativedelta
@@ -1490,7 +1490,7 @@ def date_time_this_century(
         this_century_start = datetime(
             now.year - (now.year % 100), 1, 1, tzinfo=tzinfo)
         next_century_start = datetime(
-            this_century_start.year + 100, 1, 1, tzinfo=tzinfo)
+            min(this_century_start.year + 100, MAXYEAR), 1, 1, tzinfo=tzinfo)
 
         if before_now and after_now:
             return self.date_time_between_dates(
@@ -1520,7 +1520,7 @@ def date_time_this_decade(
         this_decade_start = datetime(
             now.year - (now.year % 10), 1, 1, tzinfo=tzinfo)
         next_decade_start = datetime(
-            this_decade_start.year + 10, 1, 1, tzinfo=tzinfo)
+            min(this_decade_start.year + 10, MAXYEAR), 1, 1, tzinfo=tzinfo)
 
         if before_now and after_now:
             return self.date_time_between_dates(


Test patch:
diff --git a/tests/providers/test_date_time.py b/tests/providers/test_date_time.py
--- a/tests/providers/test_date_time.py
+++ b/tests/providers/test_date_time.py
@@ -209,15 +209,26 @@ def _datetime_to_time(self, value):
 
     def test_date_time_this_period(self):
         # test century
+        this_century_start = self._datetime_to_time(
+            datetime(datetime.now().year - (datetime.now().year % 100), 1, 1)
+        )
+
         self.assertTrue(self._datetime_to_time(self.factory.date_time_this_century(after_now=False)) <= self._datetime_to_time(datetime.now()))
         self.assertTrue(self._datetime_to_time(self.factory.date_time_this_century(before_now=False, after_now=True)) >= self._datetime_to_time(datetime.now()))
+        self.assertTrue(self._datetime_to_time(self.factory.date_time_this_century(before_now=True, after_now=True)) >= this_century_start)
+
         # test decade
+        this_decade_start = self._datetime_to_time(
+            datetime(datetime.now().year - (datetime.now().year % 10), 1, 1)
+        )
+
         self.assertTrue(self._datetime_to_time(self.factory.date_time_this_decade(after_now=False)) <= self._datetime_to_time(datetime.now()))
         self.assertTrue(self._datetime_to_time(self.factory.date_time_this_decade(before_now=False, after_now=True)) >= self._datetime_to_time(datetime.now()))
         self.assertEqual(
             self._datetime_to_time(self.factory.date_time_this_decade(before_now=False, after_now=False)),
             self._datetime_to_time(datetime.now())
         )
+        self.assertTrue(self._datetime_to_time(self.factory.date_time_this_decade(before_now=True, after_now=True)) >= this_decade_start)
         # test year
         self.assertTrue(self._datetime_to_time(self.factory.date_time_this_year(after_now=False)) <= self._datetime_to_time(datetime.now()))
         self.assertTrue(self._datetime_to_time(self.factory.date_time_this_year(before_now=False, after_now=True)) >= self._datetime_to_time(datetime.now()))