zulu 2.0.1
A drop-in replacement for native datetimes that embraces UTC
License: MIT License
Requires Python: >=3.6
Keywords: zulu date time datetime timedelta utc parser

zulu
****

|version| |build| |coveralls| |license|


A drop-in replacement for native datetimes that embraces UTC


Links
=====

- Project: https://github.com/dgilland/zulu
- Documentation: https://zulu.readthedocs.io
- PyPI: https://pypi.python.org/pypi/zulu/
- Github Actions: https://github.com/dgilland/zulu/actions


Features
========

- All datetime objects converted and stored as UTC.
- Parses ISO8601 formatted strings and POSIX timestamps by default.
- Timezone representation applied only during string output formatting or when casting to native datetime object.
- Drop-in replacement for native datetime objects.
- Python 3.6+


Quickstart
==========

Install using pip:


::

    pip3 install zulu


.. code-block:: python

    import zulu

    zulu.now()
    # <Zulu [2016-07-25T19:33:18.137493+00:00]>

    dt = zulu.parse('2016-07-25T19:33:18.137493+00:00')
    # <Zulu [2016-07-25T19:33:18.137493+00:00]>

    dt = zulu.create(2016, 7, 25, 19, 33, 18, 137493)
    # <Zulu [2016-07-25T19:33:18.137493+00:00]>

    dt.isoformat()
    # '2016-07-25T19:33:18.137493+00:00'

    dt.timestamp()
    # 1469475198.137493

    dt.naive
    # datetime.datetime(2016, 7, 25, 19, 33, 18, 137493)

    dt.datetime
    # datetime.datetime(2016, 7, 25, 19, 33, 18, 137493, tzinfo=<UTC>)

    dt.format('%Y-%m-%d')
    # 2016-07-25

    dt.format('YYYY-MM-dd')
    # 2016-07-25

    dt.format("E, MMM d, ''YY")
    # "Mon, Jul 25, '16"

    dt.format("E, MMM d, ''YY", locale='de')
    # "Mo., Juli 25, '16"

    dt.format("E, MMM d, ''YY", locale='fr')
    # "lun., juil. 25, '16"

    dt.shift(hours=-5, minutes=10)
    # <Zulu [2016-07-25T14:43:18.137493+00:00]>

    dt.replace(hour=14, minute=43)
    # <Zulu [2016-07-25T14:43:18.137493+00:00]>

    dt.start_of('day')
    # <Zulu [2016-07-25T00:00:00+00:00]>

    dt.end_of('day')
    # <Zulu [2016-07-25T23:59:59.999999+00:00]>

    dt.span('hour')
    # (<Zulu [2016-07-25T19:00:00+00:00]>, <Zulu [2016-07-25T19:59:59.999999+00:00]>)

    dt.time_from(dt.end_of('day'))
    # '4 hours ago'

    dt.time_to(dt.end_of('day'))
    # 'in 4 hours'

    list(zulu.range('hour', dt, dt.shift(hours=4)))
    # [Zulu [2016-07-25T19:33:18.137493+00:00]>,
    #  Zulu [2016-07-25T20:33:18.137493+00:00]>,
    #  Zulu [2016-07-25T21:33:18.137493+00:00]>,
    #  Zulu [2016-07-25T22:33:18.137493+00:00]>]

    list(zulu.span_range('minute', dt, dt.shift(minutes=4)))
    # [(Zulu [2016-07-25T19:33:00+00:00]>, Zulu [2016-07-25T19:33:59.999999+00:00]>),
    #  (Zulu [2016-07-25T19:34:00+00:00]>, Zulu [2016-07-25T19:34:59.999999+00:00]>),
    #  (Zulu [2016-07-25T19:35:00+00:00]>, Zulu [2016-07-25T19:35:59.999999+00:00]>),
    #  (Zulu [2016-07-25T19:36:00+00:00]>, Zulu [2016-07-25T19:36:59.999999+00:00]>)]

    zulu.parse_delta('1w 3d 2h 32m')
    # <Delta [10 days, 2:32:00]>

    zulu.parse_delta('2:04:13:02.266')
    # <Delta [2 days, 4:13:02.266000]>

    zulu.parse_delta('2 days, 5 hours, 34 minutes, 56 seconds')
    # <Delta [2 days, 5:34:56]>


Why Zulu?
=========

Why zulu instead of `native datetimes <https://docs.python.org/3.5/library/datetime.html#datetime-objects>`_:

- Zulu has extended datetime features such as ``parse()``, ``format()``, ``shift()``, and `python-dateutil`_ timezone support.
- Parses ISO8601 and timestamps by default without any extra arguments.
- Easier to reason about ``Zulu`` objects since they are only ever UTC datetimes.
- Clear delineation between UTC and other time zones where timezone representation is only applicable for display or conversion to native datetime.
- Supports more string parsing/formatting options using `Unicode date patterns`_ as well as ``strptime/strftime`` directives.


Why zulu instead of `Arrow`_:

- Zulu is a drop-in replacement for native datetimes (inherits from ``datetime.datetime``). No need to convert using ``arrow.datetime`` when you need a datetime (zulu is always a datetime).
- Stricter parsing to avoid silent errors. For example, one might expect ``arrow.get('02/08/1987', 'MM/DD/YY')`` to fail (input does not match format) but it gladly returns ``<Arrow [2019-02-08T00:00:00+00:00)`` whereas ``zulu.parse('02/08/1987', '%m/%d/%y')`` throws ``zulu.parser.ParseError: Value "02/08/1987" does not match any format in ['%m/%d/%y']``.
- Avoids timezone/DST shifting bugs by only dealing with UTC datetimes when applying timedeltas or performing other calculations.
- Supports ``strptime/strftime`` as well as `Unicode date patterns`_ for string parsing/formatting.


Special Thanks
==============

Special thanks goes out to the authors/contributors of the following libraries that have made it possible for ``zulu`` to exist:

- `Babel`_
- `iso8601`_
- `python-dateutil`_
- `pytimeparse`_
- `pytz`_


For the full documentation, please visit https://zulu.readthedocs.io.



.. _Unicode date patterns: http://www.unicode.org/reports/tr35/tr35-19.html#Date_Field_Symbol_Table
.. _Arrow: https://arrow.readthedocs.io
.. _Babel: https://github.com/python-babel/babel
.. _iso8601: https://bitbucket.org/micktwomey/pyiso8601
.. _python-dateutil: https://github.com/dateutil/dateutil
.. _pytimeparse: https://github.com/wroberts/pytimeparse
.. _pytz: http://pythonhosted.org/pytz

.. |version| image:: https://img.shields.io/pypi/v/zulu.svg?style=flat-square
    :target: https://pypi.python.org/pypi/zulu/

.. |build| image:: https://img.shields.io/github/actions/workflow/status/dgilland/zulu/main.yml?branch=master&style=flat-square
    :target: https://github.com/dgilland/zulu/actions

.. |coveralls| image:: https://img.shields.io/coveralls/dgilland/zulu/master.svg?style=flat-square
    :target: https://coveralls.io/r/dgilland/zulu

.. |license| image:: https://img.shields.io/pypi/l/zulu.svg?style=flat-square
    :target: https://pypi.python.org/pypi/zulu/

Changelog
=========


v2.0.1 (2023-11-20)
-------------------

- Add support for Python 3.12
- Replace usage of deprecated ``datetime.utcnow`` and ``datetime.utcfromtimestamp``.


v2.0.0 (2021-06-26)
-------------------

- Drop support for Python 3.4 and 3.5.


v1.3.1 (2021-06-26)
-------------------

- Fix compatibility issue with Python 3.4 and 3.5 by replacing f-string usage with string-format.


v1.3.0 (2021-01-07)
-------------------

- Add Python 3.9 support.
- Fix bug in ``Zulu.time_from``, ``Zulu.time_to``, ``Zulu.time_from_now``, and ``Zulu.time_to_now`` where keyword arguments weren't passed to underlying ``Delta.format`` call.
- Fix bug in ``Zulu.format`` where "YY" and "YYYY" format patterns would return the year in "Week of Year" based calendars instead of the regular calendar year.


v1.2.0 (2020-01-14)
-------------------

- Add Python 3.8 support.
- Add ``'week'`` option to ``Zulu.start_of``, ``Zulu.end_of``, ``Zulu.span``, and ``Zulu.span_range``. Thanks ThomasChiroux_!
- Fix bug in ``Zulu.astimezone`` in Python 3.8 due to change in return type from ``super().asdatetime``. In Python<=3.7, ``super().asdatetime`` returned as instance of ``datetime``, but in Python 3.8 another instance of ``Zulu`` was returned instead. ``Zulu.astimezone`` will now return a ``datetime`` instance in Python 3.8.


v1.1.1 (2019-08-14)
-------------------

- Remove unused parameter in ``zulu.Timer.__init__()``.


v1.1.0 (2018-11-01)
-------------------

- Add ``fold`` attribute support to ``Zulu``.
- Add ``zulu.to_seconds`` for converting units of time to total number of seconds.
- Add ``zulu.Timer`` class that can be used to track elapsed time (like a stopwatch) or as a countdown timer.


v1.0.0 (2018-08-20)
-------------------

- Drop support for Python 2.7.


v0.12.1 (2018-07-16)
--------------------

- Support Python 3.7.


v0.12.0 (2017-07-11)
--------------------

- Add ``Zulu.datetimetuple()``.
- Add ``Zulu.datetuple()``.
- Remove ``Zulu.__iter__`` method. (**breaking change**)
- Remove ``Delta.__iter__`` method. (**breaking change**)


v0.11.0 (2017-06-28)
--------------------

- Add Python 3.6 support.
- Add ``Delta.__iter__`` method that yields 2-element tuples like ``Zulu.__iter__``. Delta values are normalized into integer values distributed from the higher units to the lower units.
- Add ``Delta.__float__`` and ``Delta.__int__`` methods for converting to seconds.
- Add ``Zulu.__float__`` and ``Zulu.__int__`` methods for converting to epoch seconds.
- Fix issue in Python 3.6 where ``zulu.now()`` returned a naive datetime ``Zulu`` instance.
- Make ``Zulu.__iter__`` yield 2-element tuples containing ``(unit, value)`` like ``(('year', 2000), ('month', 12), ...)`` so it can be converted to a ``dict`` with proper keys easier. (**breaking change**)
- Remove previously deprecated ``zulu.delta()`` function. Use ``zulu.parse_delta()`` instead. (**breaking change**)
- Rename modules: (**breaking change**)

  - ``zulu.datetime -> zulu.zulu``
  - ``zulu.timedelta -> zulu.delta``


v0.10.1 (2017-02-15)
--------------------

- Provide fallback for the default value of ``locale`` in ``Delta.format()`` when a locale is not known via environment variables.


v0.10.0 (2017-02-13)
--------------------

- Add ``zulu.parse_delta`` as alias for ``Delta.parse``.
- Deprecate ``zulu.delta`` in favor of ``zulu.parse_delta``.
- Allow first argument to ``Zulu()``, ``Zulu.parse()``, and ``zulu.parse()`` to be a ``dict`` containing keys corresponding to initialization parameters.
- Fix error message for invalid timezone strings so that the supplied string is shown correctly.


v0.9.0 (2016-11-21)
-------------------

- Require ``python-dateutil>=2.6.0``. (**breaking change**)
- Replace usage of ``pytz`` timezone handling for strings with ``dateutil.tz.gettz``. Continue to support ``pytz`` timezones during ``Zulu()`` object creation. (**breaking change**).
- Replace default UTC timezone with ``dateutil.tz.tzutc()``. Was previously ``pytz.UTC``. (**breaking change**)
- Replace local timezone with ``dateutil.tz.tzlocal()``. Was previously set by the ``tzlocal`` library. (**breaking change**)


v0.8.0 (2016-10-31)
-------------------

- Add comparison methods to ``Zulu``:

  - ``is_before``
  - ``is_on_or_before``
  - ``is_after``
  - ``is_on_or_after``
  - ``is_between``


v0.7.3 (2016-10-24)
-------------------

- Optimize ``Zulu()`` constructor by eliminating multiple unnecessary calls to ``datetime`` constructor.


v0.7.2 (2016-09-06)
-------------------

- Fix ``Zulu`` not being pickle-able.


v0.7.1 (2016-08-22)
-------------------

- Add missing magic method overrides for ``Delta`` for ``+delta``, ``-delta``, and ``abs(delta)`` so that ``Delta`` is returned instead of ``datetime.timedelta``.

  - ``__pos__``
  - ``__neg__``
  - ``__abs__``


v0.7.0 (2016-08-22)
-------------------

- Make ``Zulu.__sub__`` and ``Zulu.subtract`` return a ``Delta`` object instead of ``datetime.timedelta``.
- Make ``zulu.delta`` and ``Zulu.Delta.parse`` accept a number.
- Allow the first argument to ``Zulu.shift`` be a timedelta or relative delta object.
- Ensure that mathematical magic methods for ``Delta`` return ``Delta`` objects and not ``datetime.timedelta``.

  - ``__add__``
  - ``__radd__``
  - ``__sub__``
  - ``__mul__``
  - ``__rmul__``
  - ``__floordiv__``
  - ``__truediv__`` (Python 3 only)
  - ``__div__`` (Python 2 only)
  - ``__mod__`` (Python 3 only)
  - ``__divmod__`` (Python 3 only)


v0.6.0 (2016-08-14)
-------------------

- Replace internal implementation of Unicode date pattern formatting with Babel's ``format_datetime``. **breaking change**
- Remove support for formatting to timestamp using ``X`` and ``XX``. **breaking change**
- Rename parse-from-timestamp token from ``X`` to ``timestamp``. **breaking change**
- Add ``zulu.create`` as factory function to create a ``zulu.Zulu`` instance.
- Add locale support to ``Zulu.format`` when using Unicode date pattern format tokens.
- Restore locale support to ``Delta.format``.


v0.5.0 (2016-08-13)
-------------------

- Remove locale support from ``Delta.format``. Locale is currently not supported in ``Zulu.format`` so decided to disable it in ``Delta.format`` until both can have it. **breaking change**


v0.4.0 (2016-08-13)
-------------------

- Rename ``zulu.DateTime`` to ``zulu.Zulu``. **breaking change**
- Rename ``Zulu.isleap`` to ``Zulu.is_leap_year``. **breaking change**
- Remove ``zulu.format`` alias (function can be accessed at ``zulu.parser.format_datetime``). **breaking change**
- Remove ``Zulu.leapdays``. **breaking change**
- Add ``Zulu.days_in_month``.
- Add ``zulu.Delta`` class that inherits from ``datetime.timedelta``.
- Add ``zulu.delta`` as alias to ``zulu.Delta.parse``.
- Add ``Zulu.time_from``, ``Zulu.time_to``, ``Zulu.time_from_now``, and ``Zulu.time_to_now`` that return "time ago" or "time to" humanized strings.
- Add ``zulu.range`` as alias to ``Zulu.range``.
- Add ``zulu.span_range`` as alias to ``Zulu.span_range``.
- Make time units (years, months, weeks, days, hours, minutes, seconds, microseconds) keyword arguments only for ``Zulu.add/subtract``, but allow positional argument to be an addable/subtractable object (datetime, timedelta, dateutil.relativedelta). **breaking change**


v0.3.0 (2016-08-03)
-------------------

- Rename ``DateTime.sub`` to ``DateTime.subtract``. **breaking change**
- Allow the first argument to ``DateTime.add`` to be a ``datetime.timedelta`` or ``dateutil.relativedelta`` object.
- Allow the first argument to ``DateTime.subtract`` to be a ``DateTime``, ``datetime.datetime``, ``datetime.timedelta``, or ``dateutil.relativedelta`` object.
- Provide ``zulu.ISO8601`` and ``zulu.TIMESTAMP`` as parse/format constants that can be used in ``zulu.parse(string, zulu.ISO8601)`` and ``DateTime.format(zulu.ISO8601)``.
- Remove special parse format string ``'timestamp'`` in favor of using just ``'X'`` as defined in ``zulu.TIMESTAMP``. **breaking change**
- Import ``zulu.parser.format`` to ``zulu.format``.
- Fix bug in ``DateTime`` addition operation that resulted in a native ``datetime`` being returned instead of ``DateTime``.


v0.2.0 (2016-08-02)
-------------------

- Add ``DateTime.datetime`` property that returns a native datetime.
- Add ``DateTime.fromgmtime`` that creates a ``DateTime`` from a UTC based ``time.struct_time``.
- Add ``DateTime.fromlocaltime`` that creates a ``DateTime`` from a local ``time.struct_time``.
- Add ``DateTime.isleap`` method that returns whether its year is a leap year.
- Add ``DateTime.leapdays`` that calculates the number of leap days between its year and another year.
- Add ``DateTime.start_of/end_of`` and other variants that return the start of end of a time frame:

  - ``start/end_of_century``
  - ``start/end_of_decade``
  - ``start/end_of_year``
  - ``start/end_of_month``
  - ``start/end_of_day``
  - ``start/end_of_hour``
  - ``start/end_of_minute``
  - ``start/end_of_second``

- Add ``DateTime.span`` that returns the start and end of a time frame.
- Add ``DateTime.span_range`` that returns a range of spans.
- Add ``DateTime.range`` that returns a range of datetimes.
- Add ``DateTime.add`` and ``DateTime.sub`` methods.
- Add ``years`` and ``months`` arguments to ``DateTime.shift/add/sub``.
- Drop support for milliseconds from ``DateTime.shift/add/sub``. **breaking change**
- Make ``DateTime.parse/format`` understand a subset of `Unicode date patterns <http://www.unicode.org/reports/tr35/tr35-19.html#Date_Field_Symbol_Table>`_.
- Set defaults for year (1970), month (1), and day (1) arguments to new ``DateTime`` objects. Creating a new ``DateTime`` now defaults to the start of the POSIX epoch.


v0.1.2 (2016-07-26)
-------------------

- Don't pin install requirements to a specific version; use ``>=`` instead.


v0.1.1 (2016-07-26)
-------------------

- Fix bug in ``DateTime.naive`` that resulted in a ``DateTime`` object being returned instead of a native ``datetime``.


v0.1.0 (2016-07-26)
-------------------

- First release.


.. _ThomasChiroux: https://github.com/ThomasChiroux

MIT License

Copyright (c) 2020 Derrick Gilland

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.