attotime
========
High precision datetime implementation for Python
-------------------------------------------------
Features
========
* Fractional nanosecond resolution using Python `decimal <https://docs.python.org/2/library/decimal.html>`_ module
* API as close to Python's native `datetime <https://docs.python.org/2/library/datetime.html>`_ implementation as possible
attotimedelta
=============
:code:`attotimedelta` objects represent the difference between two dates or times. It wraps a native `timedelta <https://docs.python.org/2/library/datetime.html#timedelta-objects>`_ object, and stores fractional nanoseconds as a :code:`Decimal`.
*class* attotime.attotimedelta([days[, seconds[, microseconds[, milliseconds[, minutes[, hours[, weeks[, nanoseconds]]]]]]]])
-----------------------------------------------------------------------------------------------------------------------------
All arguments are optional and default to 0. All arguments may be ints, floats, or :code:`Decimal`, and may be positive or negative.
Only *days*, *seconds*, *microseconds*, and *nanoseconds* are stored internally. *days*, *seconds*, and *microseconds* are stored in a native `timedelta` object, *nanoseconds* are contained in a :code:`Decimal`.
Instance attributes (read-only)
-------------------------------
* :code:`days` Between -999999999 and 999999999 inclusive.
* :code:`seconds` Between 0 and 86399 inclusive.
* :code:`microseconds` Between 0 and 999999 inclusive.
* :code:`nanoseconds` A :code:`Decimal` between 0 and 999 inclusive.
Supported operations
--------------------
* :code:`td1 = td2 + td3` Sum of td2 and td3.
* :code:`td1 = td2 - td3` Difference of td2 and td3.
* :code:`td1 = i * td2` or :code:`td1 = td2 * i` Delta multiplied by an integer, float, or :code:`Decimal`.
* :code:`td1 = td2 // i` Computes the floor, discarding the remainder.
* :code:`+td1` Returns an :code:`attotimedelta` with the same value.
* :code:`-td1` Equivalent to :code:`td1 * -1`.
* :code:`abs(td1)` Equivalent to :code:`+td1` when :code:`td1.days >= 0`, :code:`-td1` when :code:`t1.days < 0`.
* :code:`str(td1)` Returns a string in the form :code:`[D day[s], ][H]H:MM:SS[.UUUUUU]`, where :code:`D` is negative for :code:`td1 < 0` and :code:`UUUUUU` can be expanded for up to 16 place fixed point.
* :code:`repr(td1)` Returns a string in the form :code:`attotime.objects.attotimedelta(D[, S[, U]])`, where :code:`D` is negative for :code:`td1 < 0`.
Instance methods
----------------
attotimedelta.total_seconds()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Return the total number of seconds contained in the duration as a :code:`Decimal`.
attotimedelta.total_nanoseconds()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Return the total number of nanoseconds contained in the duration as a :code:`Decimal`.
attodatetime
============
:code:`attodatetime` is a single object wrapping a native `date <https://docs.python.org/2/library/datetime.html#date-objects>`_ object and an :code:`attotime` object for the purposes of storing date and time information with fractional nanoseconds stored as a :code:`Decimal`.
*class* attotime.attodatetime(year, month, day[, hour[, minute[, second[, microsecond[, nanosecond[, tzinfo]]]]]])
------------------------------------------------------------------------------------------------------------------
Year, month, and day are required. :code:`tzinfo` may be :code:`None`, or an instance of a `tzinfo subclass <https://docs.python.org/2/library/datetime.html#tzinfo-objects>`_. The nanosecond argument may be a float or :code:`Decimal`. The remaining arguments must be ints.
Class methods
-------------
attodatetime.today()
^^^^^^^^^^^^^^^^^^^^
Return the current local datetime, with :code:`tzinfo` :code:`None`. This is equivalent to :code:`attodatetime.fromtimestamp(time.time())`.
attodatetime.now([tz])
^^^^^^^^^^^^^^^^^^^^^^
Return the current local date and time. If optional argument :code:`tz` is :code:`None` this is like :code:`today()`.
If :code:`tz` is not :code:`None`, it must be an instance of a tzinfo subclass, and the current date and time are converted to :code:`tz`'s time zone.
attodatetime.utcnow()
^^^^^^^^^^^^^^^^^^^^^
Return the current UTC date and time, with :code:`tzinfo` :code:`None`.
attodatetime.fromtimestamp(timestamp, [tz])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Return the local date and time corresponding to the POSIX timestamp, such as returned by :code:`time.time()`. If optional argument :code:`tz` is :code:`None` or not specified, the timestamp is converted to the platform’s local date and time, and the returned :code:`attodatetime` object is naive.
If :code:`tz` is not :code:`None`, it must be an instance of a :code:`tzinfo` subclass, and the timestamp is converted to :code:`tz`’s time zone. The returned :code:`attodatetime`'s :code:`tzinfo` is set to the provided :code:`tz`.
attodatetime.utcfromtimestamp(timestamp)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Return the UTC :code:`attodatetime` corresponding to the POSIX timestamp, with :code:`tzinfo` :code:`None`.
attodatetime.fromordinal(ordinal)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Return an :code:`attodatetime` corresponding to the proleptic Gregorian ordinal, where January 1 of year 1 has ordinal 1. **ValueError** is raised unless 1 <= ordinal <= :code:`datetime.max.toordinal()` (note native Python :code:`datetime` range checking). The hour, minute, second and microsecond of the result are all 0, and :code:`tzinfo` is :code:`None`.
attodatetime.combine(date, time)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Return an :code:`attodatetime` object whose date components are equal to the given date object’s, and whose time components and :code:`tzinfo` attributes are equal to the given time object’s. If date is a :code:`attodatetime` (or native Python :code:`datetime`), its time components and :code:`tzinfo` attributes are ignored.
attodatetime.strptime(date_string, format)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Return an :code:`attodatetime` corresponding to :code:`date_string`, parsed according to :code:`format`. Only the directives explicitly listed in the `strftime() and strptime() Behavior <https://docs.python.org/2/library/datetime.html#strftime-strptime-behavior>`_ section of the Python documentation are supported, as well as the following:
+---------------------------+---------------------------+---------------------------+
| Directive | Meaning | Example |
+===========================+===========================+===========================+
| %o | Picosecond as a decimal | 000000, 000001, …, 999999 |
| | number, zero-padded on | |
| | the left. | |
+---------------------------+---------------------------+---------------------------+
| %q | Attosecond as a decimal | 000000, 000001, …, 999999 |
| | number, zero-padded on | |
| | the left. | |
+---------------------------+---------------------------+---------------------------+
| %v | Yoctosecond as a decimal | 000000, 000001, …, 999999 |
| | number, zero-padded on | |
| | the left. | |
+---------------------------+---------------------------+---------------------------+
Instance attributes (read-only)
-------------------------------
* :code:`year` Between Python native :code:`datetime` :code:`MINYEAR` and :code:`MAXYEAR`, inclusive.
* :code:`month` Between 1 and 12 inclusive.
* :code:`day` Between 1 and the number of days in the given month of the given year.
* :code:`hour` In :code:`range(24)`.
* :code:`minute` In :code:`range(60)`.
* :code:`second` In :code:`range(60)`.
* :code:`microsecond` In :code:`range(1000000)`.
* :code:`nanosecond` In :code:`range(1000)`, as :code:`Decimal`.
* :code:`tzinfo` The object passed as the :code:`tzinfo` argument to the :code:`attodatetime` constructor, or :code:`None` if none was passed.
Supported operations
--------------------
* :code:`dt2 = dt1 + td` dt1 moved forward the duration of the :code:`attotimedelta` if :code:`attotimedelta.days > 0`, or backward if :code:`attotimedelta.days < 0`.
* :code:`dt2 = dt1 - td` dt1 moved backward the duration of the :code:`attotimedelta` if :code:`attotimedelta.days > 0`, or forward if :code:`attotimedelta.days < 0`.
* :code:`td = dt1 - dt2` The duration of time between dt1 and dt2, as an :code:`attotimedelta`.
* :code:`dt1 < dt2` dt1 is considered less than dt2 if dt1 precedes dt2 in time.
* :code:`str(dt1)` Equivalent to :code:`dt1.isoformat(separator=' ')`.
* :code:`repr(dt1)` Returns a string in the form :code:`attotime.objects.attodatetime(Y, M, D, h, m, s, us, ns, [tz])`.
Instance methods
----------------
attodatetime.date()
^^^^^^^^^^^^^^^^^^^
Return a :code:`date` object with same year, month and day.
attodatetime.time()
^^^^^^^^^^^^^^^^^^^
Return an :code:`attotime` object with the same hour, minute, second, microsecond, and nanosecond. :code:`tzinfo` is :code:`None`.
attodatetime.timetz()
^^^^^^^^^^^^^^^^^^^^^
Return an :code:`attotime` object with the same hour, minute, second, microsecond, nanosecond, and :code:`tzinfo` attributes.
attodatetime.replace([year[, month[, day[, hour[, minute[, second[, microsecond[, nanosecond[, tzinfo]]]]]]]]])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Return an :code:`attodatetime` object with the same attributes, except for those attributes given new values by whichever keyword arguments are specified. Note that :code:`tzinfo=None` can be specified to create a naive :code:`attodatetime` from an aware :code:`attodatetime` with no conversion of date and time data.
attodatetime.astimezone(tz)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Return an :code:`attodatetime` object with new :code:`tzinfo` attribute :code:`tz`, adjusting the date and time data so the result is the same UTC time as self, but in :code:`tz`’s local time.
A :code:`ValueError` is raised if :code:`self` is naive.
attodatetime.utcoffset()
^^^^^^^^^^^^^^^^^^^^^^^^
If :code:`tzinfo` is :code:`None`, returns :code:`None`, else return :code:`self.tzinfo.utcoffset(self)` as an :code:`attotimedelta`.
attodatetime.dst()
^^^^^^^^^^^^^^^^^^
If :code:`tzinfo` is :code:`None`, returns :code:`None`, else return :code:`self.tzinfo.dst(self)` as an :code:`attotimedelta`.
attodatetime.tzname()
^^^^^^^^^^^^^^^^^^^^^
If :code:`tzinfo` is :code:`None`, returns :code:`None`, else returns :code:`self.tzinfo.tzname(self)`.
attodatetime.timetuple()
^^^^^^^^^^^^^^^^^^^^^^^^
Return the result of `datetime.timetuple() <https://docs.python.org/2/library/datetime.html#datetime.datetime.timetuple>`_ for a native Python :code:`datetime` matching the :code:`attodatetime`. Nanosecond precision is lost.
attodatetime.utctimetuple()
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Return the result of `datetime.utctimetuple() <https://docs.python.org/2/library/datetime.html#datetime.datetime.utctimetuple>`_ for a native Python :code:`datetime` matching the :code:`attodatetime`. Nanosecond precision is lost.
attodatetime.toordinal()
^^^^^^^^^^^^^^^^^^^^^^^^
Return the proleptic Gregorian ordinal of the date. The same as :code:`self.date().toordinal()`.
attodatetime.weekday()
^^^^^^^^^^^^^^^^^^^^^^
Return the day of the week as an integer, where Monday is 0 and Sunday is 6. The same as :code:`self.date().weekday()`.
attodatetime.isoweekday()
^^^^^^^^^^^^^^^^^^^^^^^^^
Return the day of the week as an integer, where Monday is 1 and Sunday is 7. The same as :code:`self.date().isoweekday()`.
attodatetime.isocalendar()
^^^^^^^^^^^^^^^^^^^^^^^^^^
Return a 3-tuple, (ISO year, ISO week number, ISO weekday). The same as :code:`self.date().isocalendar()`.
attodatetime.isoformat([sep])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Return a string representing the date and time in ISO 8601 format, :code:`YYYY-MM-DDTHH:MM:SS.mmmmmm` or, if microsecond is 0, :code:`YYYY-MM-DDTHH:MM:SS`
If :code:`utcoffset()` does not return :code:`None`, a 6-character string is appended, giving the UTC offset in (signed) hours and minutes: :code:`YYYY-MM-DDTHH:MM:SS.mmmmmm+HH:MM` or, if microsecond is 0 :code:`YYYY-MM-DDTHH:MM:SS+HH:MM`
The optional argument :code:`sep` (default 'T') is a separator, placed between the date and time portions of the result.
The decimal second component may be expanded up to 16 place fixed point.
attodatetime.ctime()
^^^^^^^^^^^^^^^^^^^^
Return the result of `datetime.ctime() <https://docs.python.org/2/library/datetime.html#datetime.datetime.ctime>`_ for a native Python :code:`datetime` matching the :code:`attodatetime`. Nanosecond precision is lost.
attodatetime.strftime(format)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Return a string representing the date and time, controlled by an explicit format string. Only the directives explicitly listed in the `strftime() and strptime() Behavior <https://docs.python.org/2/library/datetime.html#strftime-strptime-behavior>`_ section of the Python documentation are supported, as well as the following:
+---------------------------+---------------------------+---------------------------+
| Directive | Meaning | Example |
+===========================+===========================+===========================+
| %o | Picosecond as a decimal | 000000, 000001, …, 999999 |
| | number, zero-padded on | |
| | the left. | |
+---------------------------+---------------------------+---------------------------+
| %q | Attosecond as a decimal | 000000, 000001, …, 999999 |
| | number, zero-padded on | |
| | the left. | |
+---------------------------+---------------------------+---------------------------+
| %v | Yoctosecond as a decimal | 000000, 000001, …, 999999 |
| | number, zero-padded on | |
| | the left. | |
+---------------------------+---------------------------+---------------------------+
attotime
========
:code:`attotime` is an object wrapping a native `time <https://docs.python.org/2/library/datetime.html#time-objects>`_ object along with fractional nanoseconds stored as a :code:`Decimal`.
*class* attotime.attotime([hour[, minute[, second[, microsecond[, nanosecond[, tzinfo]]]]]])
--------------------------------------------------------------------------------------------
All arguments are optional. :code:`tzinfo` may be :code:`None`, or an instance of a `tzinfo subclass <https://docs.python.org/2/library/datetime.html#tzinfo-objects>`_. The nanosecond argument may be float or :code:`Decimal`. The remaining arguments must be ints.
Instance attributes (read-only)
-------------------------------
* :code:`hour` In :code:`range(24)`.
* :code:`minute` In :code:`range(60)`.
* :code:`second` In :code:`range(60)`.
* :code:`microsecond` In :code:`range(1000000)`.
* :code:`nanosecond` In :code:`range(1000)`, as :code:`Decimal`.
* :code:`tzinfo` The object passed as the :code:`tzinfo` argument to the :code:`attotime` constructor, or :code:`None` if none was passed.
Supported operations
--------------------
* :code:`t1 < t2` t1 is considered less than t2 if t1 precedes t2 in time.
* :code:`str(t1)` Equivalent to :code:`t1.isoformat()`.
* :code:`repr(t1)` Returns a string in the form :code:`attotime.objects.attotime(h, m, s, us, ns, [tz])`.
Instance methods
----------------
attotime.replace([hour[, minute[, second[, microsecond[, nanosecond[, tzinfo]]]]]])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Return an :code:`attotime` object with the same attributes, except for those attributes given new values by whichever keyword arguments are specified. Note that :code:`tzinfo=None` can be specified to create a naive :code:`attotime` from an aware :code:`attotime` with no conversion of date and time data.
attotime.isoformat()
^^^^^^^^^^^^^^^^^^^^
Return a string representing the time in ISO 8601 format, :code:`HH:MM:SS.mmmmmm` or, if microsecond is 0, :code:`HH:MM:SS`
If :code:`utcoffset()` does not return :code:`None`, a 6-character string is appended, giving the UTC offset in (signed) hours and minutes: :code:`HH:MM:SS.mmmmmm+HH:MM` or, if microsecond is 0 :code:`HH:MM:SS+HH:MM`
The decimal second component may be expanded up to 16 place fixed point.
attotime.strftime(formatstr)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Raises **NotImplementedError**
attotime.utcoffset()
^^^^^^^^^^^^^^^^^^^^
If :code:`tzinfo` is :code:`None`, returns :code:`None`, else return :code:`self.tzinfo.utcoffset(self)` as an :code:`attotimedelta`.
attotime.dst()
^^^^^^^^^^^^^^
If :code:`tzinfo` is :code:`None`, returns :code:`None`, else return :code:`self.tzinfo.dst(self)` as an :code:`attotimedelta`.
attotime.tzname()
^^^^^^^^^^^^^^^^^^^
If :code:`tzinfo` is :code:`None`, returns :code:`None`, else returns :code:`self.tzinfo.tzname(self)`.
Development
===========
Setup
-----
It is recommended to develop using a `virtualenv <https://virtualenv.pypa.io/en/stable/>`_.
Inside a virtualenv, development dependencies can be installed automatically::
$ pip install -e .[dev]
`pre-commit <https://pre-commit.com/>`_ is used for managing pre-commit hooks::
$ pre-commit install
To run the pre-commit hooks manually::
$ pre-commit run --all-files
Tests
-----
Tests can be run using the `unittest testing framework <https://docs.python.org/3/library/unittest.html>`_::
$ python -m unittest discover attotime
Contributing
============
attotime is an open source project hosted on `Bitbucket <https://bitbucket.org/nielsenb/attotime>`_.
Any and all bugs are welcome on our `issue tracker <https://bitbucket.org/nielsenb/attotime/issues>`_. Of particular interest are places where the attotime implementation incorrectly deviates from native Python behavior. Pull requests containing unit tests or fixed bugs are always welcome!
References
==========
* `PEP 410 which describes the need for high precision time types <https://www.python.org/dev/peps/pep-0410/>`_
* `Bug report with implementation of PEP 410 <https://bugs.python.org/issue13882>`_
* `Bug report discussing loss of precision when parsing ISO8601 timestamps <https://bitbucket.org/nielsenb/aniso8601/issues/10/sub-microsecond-precision-in-durations-is>`_