==================================
Epoch Timestamp Management Utility
==================================

The `epoch` provides a set of routines that help with the management
of UNIX epoch timestamps, including generation, adjustment, and
parsing.


Project
=======

* Homepage: https://github.com/metagriffin/epoch
* Bugs: https://github.com/metagriffin/epoch/issues


Installation
============

.. code:: bash

  $ pip install epoch


Usage
=====

The following routines are available:

* ``epoch.now()`` : float

  Returns a float representation of the current UNIX epoch timestamp,
  i.e. the number of seconds since 1970/01/01.

* ``epoch.sod([ts][, tz][, offset][, replace])`` : float

  Returns the epoch timestamp of the start of the current day relative
  to the timezone `tz`. If `ts` is specified, the start of the day
  containing `ts` is returned. If `offset` is specified, it is taken
  to be an integral number of days to offset the returned value by.
  Note that due to leap seconds, daylight savings, etc, this is more
  complex than just 60 seconds * 60 minutes * 24 hours. If `replace`
  is specified, it is a dictionary of datetime attributes to replace
  after all other modifications have been made.

  For example, the following will return the epoch timestamp in
  Anchorage, AK, USA for tomorrow at 3 PM local time:

  .. code:: python

    epoch.sod(offset=1, tz='America/Anchorage', replace=dict(hour=15))


* ``epoch.sow([ts][, tz][, offset][, day][, replace])`` : float

  Returns the epoch timestamp of the start of the current Gregorian
  week relative to the timezone `tz`. If `ts` is specified, the start
  of the week containing `ts` is returned. If `offset` is specified,
  it is taken to be an integral number of weeks to offset the returned
  value by. Note that due to leap days, leap seconds, daylight
  savings, etc, this is more complex than just 60 seconds * 60 minutes
  * 24 hours * 7 days. If `day` is specified, it specifies which day
  is defined to be the "first" day of the week, where ``0`` (the
  default) is Monday through ``6`` being Sunday. If `replace` is
  specified, it is a dictionary of datetime attributes to replace
  after all other modifications have been made (see `epoch.sod` for
  examples).

* ``epoch.som([ts][, tz][, offset][, replace])`` : float

  Returns the epoch timestamp of the start of the current Gregorian
  month relative to the timezone `tz`. If `ts` is specified, the start
  of the month containing `ts` is returned. If `offset` is specified,
  it is taken to be an integral number of months to offset the
  returned value by. If `replace` is specified, it is a dictionary of
  datetime attributes to replace after all other modifications have
  been made (see `epoch.sod` for examples).

* ``epoch.soy([ts][, tz][, offset][, replace])`` : float

  Returns the epoch timestamp of the start of the current Gregorian
  year relative to the timezone `tz`. If `ts` is specified, the start
  of the year containing `ts` is returned. If `offset` is specified,
  it is taken to be an integral number of years to offset the returned
  value by. If `replace` is specified, it is a dictionary of datetime
  attributes to replace after all other modifications have been made
  (see `epoch.sod` for examples).

* ``epoch.zulu([ts][, ms])`` : string

  Returns the specified epoch time `ts` (or current time if None or
  not provided) as an ISO 8601 Combined string in zulu time (with
  millisecond precision), e.g. ``epoch.zulu(1362187446.553)`` =>
  ``'2013-03-02T01:24:06.553Z'``. If `ms` is True (the default),
  milliseconds will be included, otherwise truncated. If `ts` has
  beyond-millisecond precision, it will be truncated to
  millisecond-level precision.

* ``epoch.parseZulu(text)`` : float

  Parses an ISO 8601 Combined string into an epoch timestamp. Note
  that this function is intended to be used with strings generated by
  `epoch.zulu`, and is therefore not very forgiving. For a much more
  human-friendly parser, try::

    import dateutil.parser
    result = dateutil.parser.parse(text, tzinfos = {'UTC': 0}))

  but please note that it does not properly warn about ambiguities;
  for example ``01/02/03`` gets interpreted without hesitation as
  ``2003/01/02``... ugh.

* ``epoch.parse(value)`` : float

  Tries the following methods of extracting an epoch timestamp from
  `text`:

  * Checks for None, integer, or float type (and returns that as-is)
  * Checks for an all-digits text, and casts that to float
  * Fallsback to parsing via :func:`epoch.parseZulu`

  Note that this function is intended to be used with code-generated
  strings (such as those generated by `epoch.zulu`), and is therefore
  not very forgiving. For a much more human-friendly parser, see the
  example in :func:`epoch.parseZulu`.

* ``epoch.tsreplace([ts][, tz][, *params])`` : float

  An epoch timestamp-oriented version of `epoch.dtreplace`. Example:

  .. code:: python

    import epoch

    ts = epoch.parse('2015-12-08T14:56:33Z')
    # ts == 1449586593.0

    ts = epoch.tsreplace(ts, hour=9, minute=30)
    # ts == 1449567033.0
    s = epoch.zulu(ts)
    # s == '2015-12-08T09:30:33.000Z'

    ts = epoch.tsreplace(ts, tz='Europe/Paris', hour=9, minute=30)
    # ts == 1449563433.0
    s = epoch.zulu(ts)
    # s == '2015-12-08T08:30:33.000Z'

* ``epoch.dtreplace(dt[, *params])`` : datetime

  A version of :meth:`datetime.datetime.replace()` that properly
  maintains the `dt.tzinfo` if the replace will cause DST boundary
  switching.

* ``epoch.ts2age(ts[, origin][, tz])`` : float

  ## TODO: DOCUMENT
  ## import pdb;pdb.set_trace()

* ``epoch.age2ts(age[, origin][, tz])`` : float

  ## TODO: DOCUMENT
  ## import pdb;pdb.set_trace()

Note that the `epoch` package, when working with `datetime` objects,
always uses timezone-aware objects.
