Repository: joke2k/faker
Pull request #196 (2015-01-20 20:42:48)

Date Time This [Period] Only Returns DateTime in Period
It makes sense that a function named 'date_time_this_year' should return a DateTime in **this** year. 

##### Previous
```
fake.date_time_this_year() # any DateTime in the last 12 months 
```
##### Now
```
fake.date_time_this_year() # any DateTime in the current year
fake.date_time_between(start_date='-1y', end_date='now') # same as prior functionality
fake.date_time_this_year(after_now=False) # any DateTime in the current year before datetime.now()
fake.date_time_this_year(before_now=False) # any DateTime in the current year after datetime.now()
```
#150 


Patch:
diff --git a/faker/providers/date_time.py b/faker/providers/date_time.py
index 855af137..31e76540 100644
--- a/faker/providers/date_time.py
+++ b/faker/providers/date_time.py
@@ -321,33 +321,112 @@ class Provider(BaseProvider):
         return datetime.fromtimestamp(timestamp)
 
     @classmethod
-    def date_time_this_century(cls):
+    def date_time_between_dates(cls, datetime_start=datetime.now(), datetime_end=datetime.now()):
         """
-        :example DateTime('1964-04-04 11:02:02')
+        Takes two DateTime objects and returns a random date between the two given dates.
+        Accepts DateTime objects.
+
+        :param datetime_start DateTime
+        :param datetime_end DateTime
+        :example DateTime('1999-02-02 11:42:52')
+        :return DateTime
+        """
+        timestamp = random.randint(datetime_to_timestamp(datetime_start),
+                                       datetime_to_timestamp(datetime_end))
+        return datetime.fromtimestamp(timestamp)
+
+    @classmethod
+    def date_time_this_century(cls, before_now=True, after_now=False):
+        """
+        Gets a DateTime object for the decade year. 
+        
+        :param before_now: include days in current decade before today
+        :param after_now: include days in current decade after today
+        :example DateTime('2012-04-04 11:02:02')
+        :return DateTime
         """
-        return cls.date_time_between('-%dy' % (datetime.now().year % 100))
+        now = datetime.now()
+        this_century_start = datetime(now.year - (now.year % 10), 1, 1)
+        next_century_start = datetime(this_century_start.year + 10, 1, 1)
+
+        if before_now and after_now:
+            return cls.date_time_between_dates(this_century_start, next_century_start)
+        elif not before_now and after_now:
+            return cls.date_time_between_dates(now, next_century_start)
+        elif not after_now and before_now:
+            return cls.date_time_between_dates(this_century_start, now)
+        else:
+            return now
 
     @classmethod
-    def date_time_this_decade(cls):
+    def date_time_this_decade(cls, before_now=True, after_now=False):
         """
-        :example DateTime('2004-04-04 11:02:02')
+        Gets a DateTime object for the decade year. 
+        
+        :param before_now: include days in current decade before today
+        :param after_now: include days in current decade after today
+        :example DateTime('2012-04-04 11:02:02')
+        :return DateTime
         """
-        return cls.date_time_between('-%dy' % (datetime.now().year % 10))
+        now = datetime.now()
+        this_decade_start = datetime(now.year - (now.year % 10), 1, 1)
+        next_decade_start = datetime(this_decade_start.year + 10, 1, 1)
+
+        if before_now and after_now:
+            return cls.date_time_between_dates(this_decade_start, next_decade_start)
+        elif not before_now and after_now:
+            return cls.date_time_between_dates(now, next_decade_start)
+        elif not after_now and before_now:
+            return cls.date_time_between_dates(this_decade_start, now)
+        else:
+            return now
 
     @classmethod
-    def date_time_this_year(cls):
+    def date_time_this_year(cls, before_now=True, after_now=False):
         """
+        Gets a DateTime object for the current year. 
+        
+        :param before_now: include days in current year before today
+        :param after_now: include days in current year after today
         :example DateTime('2012-04-04 11:02:02')
+        :return DateTime
         """
-        return cls.date_time_between('-%dm' % (datetime.now().month))
+        now = datetime.now()
+        this_year_start = now.replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0)
+        next_year_start = datetime(now.year + 1, 1, 1)
 
+        if before_now and after_now:
+            return cls.date_time_between_dates(this_year_start, next_year_start)
+        elif not before_now and after_now:
+            return cls.date_time_between_dates(now, next_year_start)
+        elif not after_now and before_now:
+            return cls.date_time_between_dates(this_year_start, now)
+        else:
+            return now
+        
     @classmethod
-    def date_time_this_month(cls):
+    def date_time_this_month(cls, before_now=True, after_now=False):
         """
+        Gets a DateTime object for the current month. 
+        
+        :param before_now: include days in current month before today
+        :param after_now: include days in current month after today
         :example DateTime('2012-04-04 11:02:02')
+        :return DateTime
         """
-        return cls.date_time_between('-%dd' % (datetime.now().day))
+        now = datetime.now()
+        this_month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
+        next_month_start = datetime(now.year, now.month + 1 % 12, 1)
 
+        if before_now and after_now:
+            return cls.date_time_between_dates(this_month_start, next_month_start)
+        elif not before_now and after_now:
+            return cls.date_time_between_dates(now, next_month_start)
+        elif not after_now and before_now:
+            return cls.date_time_between_dates(this_month_start, now)
+        else:
+            return now
+            
     @classmethod
     def am_pm(cls):
         return cls.date('%p')


Test patch:
diff --git a/faker/tests/__init__.py b/faker/tests/__init__.py
index 8ca6177c..43ad9d17 100644
--- a/faker/tests/__init__.py
+++ b/faker/tests/__init__.py
@@ -6,6 +6,7 @@ import json
 import os
 import random
 import unittest
+import datetime
 
 from faker import Generator
 from faker.utils import text, decorators
@@ -223,6 +224,45 @@ class FactoryTestCase(unittest.TestCase):
         # test that certain formatting strings are allowed on post-1900 dates
         result = datetime_safe.date(2008, 2, 29).strftime('%y')
         self.assertEqual(result, r'08')
+    
+    def test_date_time_between_dates(self):
+        from faker.providers.date_time import Provider
+        provider = Provider
+
+        timestamp_start = random.randint(0,10000000000)
+        timestamp_end = timestamp_start+1
+
+        datetime_start = datetime.datetime.fromtimestamp(timestamp_start)
+        datetime_end = datetime.datetime.fromtimestamp(timestamp_end)
+
+        random_date = provider.date_time_between_dates(datetime_start, datetime_end)
+        self.assertLessEqual(datetime_start, random_date)
+        self.assertGreaterEqual(datetime_end, random_date)
+
+    def test_date_time_this_period(self):
+        from faker.providers.date_time import Provider
+        provider = Provider
+        now = datetime.datetime.now()
+        # test century
+        self.assertLessEqual(provider.date_time_this_century(after_now=False), now)
+        self.assertGreaterEqual(provider.date_time_this_century(before_now=False), now)
+        self.assertAlmostEqual(provider.date_time_this_century(before_now=False, after_now=False),
+                               now, delta=datetime.timedelta(seconds=1))
+        # test decade
+        self.assertLessEqual(provider.date_time_this_decade(after_now=False), now)
+        self.assertGreaterEqual(provider.date_time_this_decade(before_now=False), now)
+        self.assertAlmostEqual(provider.date_time_this_decade(before_now=False, after_now=False),
+                               now, delta=datetime.timedelta(seconds=1))
+        # test year
+        self.assertLessEqual(provider.date_time_this_year(after_now=False), now)
+        self.assertGreaterEqual(provider.date_time_this_year(before_now=False), now)
+        self.assertAlmostEqual(provider.date_time_this_year(before_now=False, after_now=False),
+                               now, delta=datetime.timedelta(seconds=1))
+        # test month
+        self.assertLessEqual(provider.date_time_this_month(after_now=False), now)
+        self.assertGreaterEqual(provider.date_time_this_month(before_now=False), now)
+        self.assertAlmostEqual(provider.date_time_this_month(before_now=False, after_now=False),
+                               now, delta=datetime.timedelta(seconds=1))
 
 
 if __name__ == '__main__':