Metadata-Version: 1.1
Name: anybox.testing.datetime
Version: 0.5
Summary: Various utilities related to date and time for testing purposes.
Home-page: https://bitbucket.org/anybox/anybox.testing.datetime
Author: Georges Racinet
Author-email: gracinet@anybox.fr
License: GPLv3+
Description: anybox.testing.datetime
        =======================
        
        This package allows to **cheat with current time** in tests.
        It has initially been used in Odoo to test workflows spanning over a long period of time.
        
        Currently it mainly provides a ``datetime.set_now()`` method to fake the
        current time, and a ``datetime.real_now()`` to return back to the original
        time.
        
        Usage
        ~~~~~
        
        Before anything, the package must be imported in order to replace the
        regular ``datetime`` module with the modified one::
        
          >>> import anybox.testing.datetime
          >>> from datetime import datetime
          >>> import time
        
        Let's keep the real value of ``now`` around::
        
          >>> start = datetime.now()
          >>> start_t = time.time()
          >>> start_s = time.strftime('%Y-%m-%d')
          >>> start_l = time.localtime()
          >>> start_c = time.ctime()
        
        Then you can change the current time::
        
          >>> datetime.set_now(datetime(2001, 01, 01, 3, 57, 0))
          >>> datetime.now()
          datetime(2001, 1, 1, 3, 57)
          >>> datetime.today()
          datetime(2001, 1, 1, 3, 57)
        
        The time module goes along::
        
          >>> datetime.fromtimestamp(time.time())
          datetime(2001, 1, 1, 3, 57)
        
        Note that you can expect a few microseconds difference (not displayed
        here because ``datetime.fromtimestamp`` ignores them).
        
        Some other functions from the time module also return the current time:
        
          >>> time.localtime()
          time.struct_time(tm_year=2001, tm_mon=1, tm_mday=1, tm_hour=3, tm_min=57, tm_sec=0, tm_wday=0, tm_yday=1, tm_isdst=-1)
          >>> time.strftime('%Y-%m-%d')
          '2001-01-01'
          >>> time.ctime()
          'Mon Jan  1 03:57:00 2001'
          >>> time.asctime()
          'Mon Jan  1 03:57:00 2001'
          >>> time.gmtime()
          time.struct_time(tm_year=2001, tm_mon=1, tm_mday=1, tm_hour=3, tm_min=57, tm_sec=0, tm_wday=0, tm_yday=1, tm_isdst=-1)
        
        The remaining behaviours are not altered
        
          >>> time.localtime(0).tm_year
          1970
          >>> time.strftime('%Y-%m-%d', datetime(1999,9,9).timetuple())
          '1999-09-09'
          >>> time.ctime(5)
          'Thu Jan  1 02:00:05 1970'
          >>> time.asctime(time.localtime(5))
          'Thu Jan  1 02:00:05 1970'
          >>> time.gmtime(5.0)
          time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=0, tm_min=0, tm_sec=5, tm_wday=3, tm_yday=1, tm_isdst=0)
        
        
        
        Don't forget afterwards to get back to the regular system clock, otherwise
        many pieces of code might get very suprised if the system clock looks as if
        it's frozen::
        
          >>> datetime.real_now()
        
        Now let's check it worked::
        
          >>> now = datetime.now()
          >>> now > start
          True
          >>> from datetime import timedelta
          >>> now - start < timedelta(0, 0, 10000) # 10 ms
          True
        
        And with the ``time`` module::
        
          >>> now_t = time.time()
          >>> now_t > start_t
          True
          >>> now_t - start_t < 0.01 # 10 ms again
          True
          >>> time.strftime('%Y-%m-%d') == start_s
          True
          >>> time.localtime().tm_mday == start_l.tm_mday
          True
        
        Other constructors are still available (this is a non regression
        test)::
        
          >>> import datetime
          >>> datetime.time(3, 57, 0)
          datetime.time(3, 57)
          >>> datetime.datetime(2013, 1, 1, 3, 57, 0)
          datetime(2013, 1, 1, 3, 57)
          >>> datetime.date(2013, 1, 1)
          datetime.date(2013, 1, 1)
        
        Behind the hood
        ~~~~~~~~~~~~~~~
        
        Our replacement class is the one loaded from the ``datetime`` module,
        but instances of the original ``datetime`` class behave exactly as
        instances of our ``datetime.datetime``. This is needed because most
        computational methods, actually return an object of the original
        ``datetime`` class. This works with python >= 2.6 only.
        
        First let's check that our class is a subclass of the original
        one. If this fails, this test does not mean anything anymore::
        
          >>> datetime.datetime is datetime.original_datetime
          False
          >>> issubclass(datetime.datetime, datetime.original_datetime)
          True
        
        Then let's demonstrate the behaviour::
        
          >>> odt = datetime.original_datetime(2012, 1, 1)
          >>> isinstance(odt, datetime.datetime)
          True
          >>> issubclass(datetime.original_datetime, datetime.datetime)
          True
        
        We'll need a ``tzinfo`` subclass from now on.
        
          >>> from datetime import tzinfo
          >>> class mytzinfo(tzinfo):
          ...     def utcoffset(self, dt):
          ...         return timedelta(hours=2)
          ...     def dst(self, dt):
          ...         return timedelta(0)
        
        Compatibility
        ~~~~~~~~~~~~~
        
        Over the lifespan of this development toolkit module, we've had to ensure
        compatibility with several subsystems
        
        logging
        -------
        
        In the logging module, ``time.localtime`` is used as a method. We just check it works
        
          >>> import logging
          >>> datetime.datetime.set_now(datetime.datetime(2000, 1, 1))
          >>> logging.Formatter().converter().tm_year >= 2014
          True
          >>> datetime.datetime.real_now()
        
        SQLite
        ------
        
        Also, ``sqlite3`` does recognize our ``datetime`` and ``date`` classes as
        if they were the original ones::
        
          >>> import sqlite3
          >>> cnx = sqlite3.connect(':memory:')
          >>> cr = cnx.cursor()
          >>> cr = cr.execute("CREATE TABLE dates (dt text, d text)")
          >>> dt = datetime.datetime(2013, 1, 25, 12, 34, 0)
          >>> d = datetime.date(2013, 4, 7)
          >>> cr = cr.execute("INSERT INTO dates VALUES (?, ?)", (dt, d))
          >>> cr = cr.execute("SELECT dt, d from dates")
          >>> cr.fetchall()
          [(u'2013-01-25 12:34:00', u'2013-04-07')]
        
        Restore original time
        ~~~~~~~~~~~~~~~~~~~~~
        
        Now let's try this again with the original ones::
        
          >>> dt = datetime.datetime.now()
          >>> isinstance(dt, datetime.original_datetime)
          True
          >>> d = datetime.date.today()
          >>> cr = cr.execute("INSERT INTO dates VALUES (?, ?)", (dt, d))
          >>> cr = cr.execute("SELECT dt, d from dates")
          >>> res = cr.fetchall() # can't check the value, it changes a lot !
        
        
        Data streaming aka pickle
        -------------------------
        
        The mock_dt support pickling::
        
          >>> import pickle
          >>> from StringIO import StringIO
          >>> stream = StringIO()
          >>> v = datetime.datetime(2013, 1, 1, 3, 57, 0)
          >>> pickle.dump(v, stream)
          >>> stream.seek(0)
          >>> v2 = pickle.load(stream)
          >>> v == v2
          True
          >>> type(v2)
          <class 'anybox.testing.datetime.mock_dt.datetime'>
          >>> stream = StringIO()
          >>> v = datetime.datetime.now()
          >>> pickle.dump(v, stream)
          >>> stream.seek(0)
          >>> v2 = pickle.load(stream)
          >>> v == v2
          True
          >>> type(v2)
          <class 'anybox.testing.datetime.mock_dt.datetime'>
          >>> stream = StringIO()
          >>> datetime.datetime.set_now(datetime.datetime(2001, 01, 01, 3, 57, 0))
          >>> v = datetime.datetime.now()
          >>> pickle.dump(v, stream)
          >>> stream.seek(0)
          >>> v2 = pickle.load(stream)
          >>> v == v2
          True
          >>> type(v2)
          <class 'anybox.testing.datetime.mock_dt.datetime'>
        
        Test
        ~~~~
        
        This README is also a doctest. To test it and other doctests for this package,
        simply install Nose and run::
        
          $ nosetests
        
        
        Changes
        ~~~~~~~
        
        0.5 (2015-04-12)
        ----------------
        
        - Added support for time.* functions
        - #6: fixed picking error
        
        
        0.4.2 (2013-06-11)
        ------------------
        
        - A few improvements in the doc
        
        0.4.1 (2013-04-24)
        ------------------
        
        - #3: fixed compatibility problem with ``sqlite3`` (spotted via IPython/IPdb)
        
        0.3.1 (2012-11-28)
        ------------------
        
        - #1: tested code using time zone optional arg of now() doesn't break
          any more (no real time zone support, though)
        
        0.3 (2012-11-23)
        ----------------
        
        - Fixed the problem that ``datetime`` objects generated from
          computations used to fail ``isinstance`` tests.
        
        0.2.1 (2012-11-22)
        ------------------
        
        - Fixed issue with ``datetime.time`` masking
        
        0.1 (2012-07-15)
        ----------------
        
        - initial version
        
Platform: UNKNOWN
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Classifier: Topic :: Software Development :: Testing
