Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to change the date/time in Python for all modules?

Tags:

When I write with business logic, my code often depends on the current time. For example the algorithm which looks at each unfinished order and checks if an invoice should be sent (which depends on the no of days since the job was ended). In these cases creating an invoice is not triggered by an explicit user action but by a background job.

Now this creates a problem for me when it comes to testing:

  • I can test invoice creation itself easily
  • However it is hard to create an order in a test and check that the background job identifies the correct orders at the correct time.

So far I found two solutions:

  • In the test setup, calculate the job dates relative to the current date. Downside: The code becomes quite complicated as there are no explicit dates written anymore. Sometimes the business logic is pretty complex for edge cases so it becomes hard to debug due to all these relative dates.
  • I have my own date/time accessor functions which I use throughout my code. In the test I just set a current date and all modules get this date. So I can simulate an order creation in February and check that the invoice is created in April easily. Downside: 3rd party modules do not use this mechanism so it's really hard to integrate+test these.

The second approach was way more successful to me after all. Therefore I'm looking for a way to set the time Python's datetime+time modules return. Setting the date is usually enough, I don't need to set the current hour or second (even though this would be nice).

Is there such a utility? Is there an (internal) Python API that I can use?

like image 883
Felix Schwarz Avatar asked Apr 17 '10 10:04

Felix Schwarz


People also ask

How do you change date and time in Python?

For example, the %d-%m-%Y %H:%M:%S codes convert date to dd-mm-yyyy hh:mm:ss format. Use this step if you want to convert a time object to string format. like, hours minutes seconds ( hh:mm:ss ). Use the time.

How do you set custom time in Python?

The datetime. strptime() sets the hour and minute values to the default, 0 . Note that for a two-digit year (like 15 ) you'd use %y , not %Y , which is for a four-digit year. if you need to add an amount that is negative or more than one day then only the timedelta() solution would work.

What is date/time module in Python?

Python Datetime module supplies classes to work with date and time. These classes provide a number of functions to deal with dates, times and time intervals. Date and datetime are an object in Python, so when you manipulate them, you are actually manipulating objects and not string or timestamps.


2 Answers

Monkey-patching time.time is probably sufficient, actually, as it provides the basis for almost all the other time-based routines in Python. This appears to handle your use case pretty well, without resorting to more complex tricks, and it doesn't matter when you do it (aside from the few stdlib packages like Queue.py and threading.py that do from time import time in which case you must patch before they get imported):

>>> import datetime >>> datetime.datetime.now() datetime.datetime(2010, 4, 17, 14, 5, 35, 642000) >>> import time >>> def mytime(): return 120000000.0 ... >>> time.time = mytime >>> datetime.datetime.now() datetime.datetime(1973, 10, 20, 17, 20) 

That said, in years of mocking objects for various types of automated testing, I've needed this approach only very rarely, as most of the time it's my own application code that needs the mocking, and not the stdlib routines. After all, you know they work already. If you are encountering situations where your own code has to handle values returned by library routines, you may want to mock the library routines themselves, at least when checking how your own app will handle the timestamps.

The best approach by far is to build your own date/time service routine(s) which you use exclusively in your application code, and build into that the ability for tests to supply fake results as required. For example, I do a more complex equivalent of this sometimes:

# in file apptime.py (for example) import time as _time  class MyTimeService(object):     def __init__(self, get_time=None):         self.get_time = get_time or _time.time      def __call__(self):         return self.get_time()  time = MyTimeService() 

Now in my app code I just do import apptime as time; time.time() to get the current time value, whereas in test code I can first do apptime.time = MyTimeService(mock_time_func) in my setUp() code to supply fake time results.

Update: Years later there's an alternative, as noted in Dave Forgac's answer.

like image 150
Peter Hansen Avatar answered Oct 18 '22 09:10

Peter Hansen


The freezegun package was made specifically for this purpose. It allows you to change the date for code under test. It can be used directly or via a decorator or context manager. One example:

from freezegun import freeze_time import datetime  @freeze_time("2012-01-14") def test():     assert datetime.datetime.now() == datetime.datetime(2012, 1, 14) 

For more examples see the project: https://github.com/spulec/freezegun

like image 40
Dave Forgac Avatar answered Oct 18 '22 09:10

Dave Forgac