.. image:: http://img.shields.io/pypi/v/sact.epoch.svg?style=flat
:target: https://pypi.python.org/pypi/sact.epoch/
:alt: Latest PyPI version
.. image:: http://img.shields.io/pypi/dm/sact.epoch.svg?style=flat
:target: https://pypi.python.org/pypi/sact.epoch/
:alt: Number of PyPI downloads
.. image:: http://img.shields.io/travis/0k/sact.epoch/master.svg?style=flat
:target: https://travis-ci.org/0k/sact.epoch/
:alt: Travis CI build status
.. image:: http://img.shields.io/coveralls/0k/sact.epoch/master.svg?style=flat
:target: https://coveralls.io/r/0k/sact.epoch
:alt: Test coverage
``sact.epoch`` is a Python library using Zope Component Architecture providing
legacy ``datetime.datetime`` subclass that allows a simple time abstraction
mecanism, allowing code that would use it as reference to be diverted both
on the local time zone and the real time.
FEATURES
========
using ``sact.epoch.Time``:
- you'll never again hear about "naive" time, a ``Time`` instance
represents one absolute unique moment, even if you can look at
these moments through different timezones.
- you will be able to get timestamp of any Time object without thinking about it
(that was rather complicated with ``datetime``, see:
http://stackoverflow.com/questions/5067218/get-utc-timestamp-in-python-with-datetime,
and eventually made it in python 3.3, without clearing all the complexity of naive
datetimes...
http://docs.python.org/3/library/datetime.html#datetime.datetime.timestamp,)
- you'll be able to test code that make use of ``.now()`` by setting
or stopping current time as seen be ``.now()`` function without touching
at the code. Even more, you'll be able to divert local timezone access easily.
These assumptions are in the code:
- Timestamps are UTC. ``Time`` object are instanced UTC by default
when not specified otherwise, so ``.fromtimestamp`` and
``.utcfromtimestamp`` does the same thing in ``sact.epoch.Time``.
- Time object are considered equal if their timestamp is equal. Note that
2 ``Time`` instance could be on different timezone and be considered equal.
- ``Time`` object is nothing more than an absolute time reference (a timestamp),
and a timezone information.
- Access to system information, for instance: call to get the local
system timezone, and call to get the local system time, should be
explicit or changeable for tests. (legacy module ``time`` and
``datetime`` will make lot of these calls)
Replaces datetime
-----------------
You can replace ``datetime`` by ``Time``... it has the same API. So you
could instanciate them as you instanciated ``datetime``s::
>>> from sact.epoch import Time
>>> Time.now() # doctest: +ELLIPSIS
With a difference: there are no naive ``Time``, this means that all
``Time`` objects have a timezone set. By default, it's UTC timezone
that is used.
You could also use a valid ``datetime`` to instanciate a ``Time``
object::
>>> from sact.epoch.clock import Time, UTC, TzLocal
>>> from datetime import datetime
>>> d = datetime(1970, 1, 1, tzinfo=UTC())
>>> Time(d)
Or use ``strptime()``, notice that we need a new ``hint_src_tz`` param,
which informs the code in which timezone the parsed string should be
considered::
Manageable clock for tests
--------------------------
There are code that deals with the current system time and
timezone. ``sact.epoch`` will give you mean to divert these calls so
as to fix time and timezone so that test output remain checkable.
Diverting system clock
^^^^^^^^^^^^^^^^^^^^^^
This use zope utilities. We can give a better view thanks to a
manageable clock as time reference::
>>> from sact.epoch.clock import ManageableClock
>>> clock = ManageableClock()
We will stop the time to epoch::
>>> clock.stop()
>>> clock.ts = 0
And register our clock as the General clock::
>>> from zope.component import globalSiteManager as gsm
>>> gsm.registerUtility(clock)
>>> Time.now()
Diverting system timezone
^^^^^^^^^^^^^^^^^^^^^^^^^
Now, let's set our TzTest as local timezone, remember it has 5 minutes
difference to UTC::
>>> from sact.epoch import testTimeZone
>>> from sact.epoch.interfaces import ITimeZone
If not specified, it uses ``Time.now()`` as reference date to infer
missing element of the given date string::
>>> Time("Thu 10:36", hint_src_tz=UTC())
As it remains a ``datetime.datetime`` sub-class you can instanciate it
like a ``datetime``::
>>> Time(1980, 1, 2)
Properties
^^^^^^^^^^
Getting timestamp from a datetime was nightmarish. Now simply::
>>> t = Time(1980, 1, 1)
>>> t.timestamp
315532800
or (as an alias)::
>>> t.ts
315532800
And of course::
>>> Time.fromtimestamp(t.timestamp) == t
True
As a matter of fact, ``.fromtimestamp`` is equivalent to
``.utcfromtimestamp``. (Let's repeat it: timestamp should ALWAYS be
considered UTC). If you want system local time from a timestamp you
could::
>>> Time.fromtimestamp(t.timestamp).local
Getting the local/utc zoned time (not changing the time, only the
timezone)::
This was to illustrate the fact that the time didn't change.
Getting some common representations::
>>> t.iso
'1980-01-01 00:00:00+00:00'
This last string formatting of a date is complete, and you can easily get
a accurate ``sact.epoch.Time`` object with it, by direct instanciation:
>>> Time(t.iso) == t
True
Warning, these representation will loose the tz info::
>>> t.short ## warning: this representation does not include tz info
'1980-01-01 00:00:00'
>>> t.local.short ## warning: this representation does not include tz info
'1980-01-01 00:05:00'
>>> t.short_short ## warning: this representation does not include tz info
'1980-01-01 00:00'
As they don't provide the timezone's information, you can't instanciate them
directly without providing an hint thanks to giving the timezone, or by giving
a relative datetime from which the timezone will be taken::
>>> Time(t.short)
Traceback (most recent call last):
...
ValueError: No timezone hinted, nor found.
>>> Time(t.short, hint_src_tz=UTC())
strptime
^^^^^^^^
``Time.strptime()`` was modified from the ``datetime.datetime``
version, it now asks for the source timezone info::
Notice that the ``Time`` instance is in UTC, so ``15:08`` in implicit
local time zone, became ``15:03`` in UTC. If you wanted the local
``Time`` instance instead::