aniso8601


Nameaniso8601 JSON
Version 9.0.1 PyPI version JSON
download
home_pagehttps://bitbucket.org/nielsenb/aniso8601
SummaryA library for parsing ISO 8601 strings.
upload_time2021-03-02 01:33:22
maintainer
docs_urlNone
authorBrandon Nielsen
requires_python
license
keywords iso8601 parser
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            aniso8601
=========

Another ISO 8601 parser for Python
----------------------------------

Features
========
* Pure Python implementation
* Logical behavior

  - Parse a time, get a `datetime.time <http://docs.python.org/3/library/datetime.html#datetime.time>`_
  - Parse a date, get a `datetime.date <http://docs.python.org/3/library/datetime.html#datetime.date>`_
  - Parse a datetime, get a `datetime.datetime <http://docs.python.org/3/library/datetime.html#datetime.datetime>`_
  - Parse a duration, get a `datetime.timedelta <http://docs.python.org/3/library/datetime.html#datetime.timedelta>`_
  - Parse an interval, get a tuple of dates or datetimes
  - Parse a repeating interval, get a date or datetime `generator <https://wiki.python.org/moin/Generators>`_

* UTC offset represented as fixed-offset tzinfo
* Parser separate from representation, allowing parsing to different datetime representations (see `Builders`_)
* No regular expressions

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

The recommended installation method is to use pip::

  $ pip install aniso8601

Alternatively, you can download the source (git repository hosted at `Bitbucket <https://bitbucket.org/nielsenb/aniso8601>`_) and install directly::

  $ python setup.py install

Use
===

Parsing datetimes
-----------------

*Consider* `datetime.datetime.fromisoformat <https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat>`_ *for basic ISO 8601 datetime parsing*

To parse a typical ISO 8601 datetime string::

  >>> import aniso8601
  >>> aniso8601.parse_datetime('1977-06-10T12:00:00Z')
  datetime.datetime(1977, 6, 10, 12, 0, tzinfo=+0:00:00 UTC)

Alternative delimiters can be specified, for example, a space::

  >>> aniso8601.parse_datetime('1977-06-10 12:00:00Z', delimiter=' ')
  datetime.datetime(1977, 6, 10, 12, 0, tzinfo=+0:00:00 UTC)

UTC offsets are supported::

  >>> aniso8601.parse_datetime('1979-06-05T08:00:00-08:00')
  datetime.datetime(1979, 6, 5, 8, 0, tzinfo=-8:00:00 UTC)

If a UTC offset is not specified, the returned datetime will be naive::

  >>> aniso8601.parse_datetime('1983-01-22T08:00:00')
  datetime.datetime(1983, 1, 22, 8, 0)

Leap seconds are currently not supported and attempting to parse one raises a :code:`LeapSecondError`::

  >>> aniso8601.parse_datetime('2018-03-06T23:59:60')
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/time.py", line 196, in parse_datetime
      return builder.build_datetime(datepart, timepart)
    File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py", line 237, in build_datetime
      cls._build_object(time))
    File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/__init__.py", line 336, in _build_object
      return cls.build_time(hh=parsetuple.hh, mm=parsetuple.mm,
    File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py", line 191, in build_time
      hh, mm, ss, tz = cls.range_check_time(hh, mm, ss, tz)
    File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/__init__.py", line 266, in range_check_time
      raise LeapSecondError('Leap seconds are not supported.')
  aniso8601.exceptions.LeapSecondError: Leap seconds are not supported.

To get the resolution of an ISO 8601 datetime string::

  >>> aniso8601.get_datetime_resolution('1977-06-10T12:00:00Z') == aniso8601.resolution.TimeResolution.Seconds
  True
  >>> aniso8601.get_datetime_resolution('1977-06-10T12:00') == aniso8601.resolution.TimeResolution.Minutes
  True
  >>> aniso8601.get_datetime_resolution('1977-06-10T12') == aniso8601.resolution.TimeResolution.Hours
  True

Note that datetime resolutions map to :code:`TimeResolution` as a valid datetime must have at least one time member so the resolution mapping is equivalent.

Parsing dates
-------------

*Consider* `datetime.date.fromisoformat <https://docs.python.org/3/library/datetime.html#datetime.date.fromisoformat>`_ *for basic ISO 8601 date parsing*

To parse a date represented in an ISO 8601 string::

  >>> import aniso8601
  >>> aniso8601.parse_date('1984-04-23')
  datetime.date(1984, 4, 23)

Basic format is supported as well::

  >>> aniso8601.parse_date('19840423')
  datetime.date(1984, 4, 23)

To parse a date using the ISO 8601 week date format::

  >>> aniso8601.parse_date('1986-W38-1')
  datetime.date(1986, 9, 15)

To parse an ISO 8601 ordinal date::

  >>> aniso8601.parse_date('1988-132')
  datetime.date(1988, 5, 11)

To get the resolution of an ISO 8601 date string::

  >>> aniso8601.get_date_resolution('1981-04-05') == aniso8601.resolution.DateResolution.Day
  True
  >>> aniso8601.get_date_resolution('1981-04') == aniso8601.resolution.DateResolution.Month
  True
  >>> aniso8601.get_date_resolution('1981') == aniso8601.resolution.DateResolution.Year
  True

Parsing times
-------------

*Consider* `datetime.time.fromisoformat <https://docs.python.org/3/library/datetime.html#datetime.time.fromisoformat>`_ *for basic ISO 8601 time parsing*

To parse a time formatted as an ISO 8601 string::

  >>> import aniso8601
  >>> aniso8601.parse_time('11:31:14')
  datetime.time(11, 31, 14)

As with all of the above, basic format is supported::

  >>> aniso8601.parse_time('113114')
  datetime.time(11, 31, 14)

A UTC offset can be specified for times::

  >>> aniso8601.parse_time('17:18:19-02:30')
  datetime.time(17, 18, 19, tzinfo=-2:30:00 UTC)
  >>> aniso8601.parse_time('171819Z')
  datetime.time(17, 18, 19, tzinfo=+0:00:00 UTC)

Reduced accuracy is supported::

  >>> aniso8601.parse_time('21:42')
  datetime.time(21, 42)
  >>> aniso8601.parse_time('22')
  datetime.time(22, 0)

A decimal fraction is always allowed on the lowest order element of an ISO 8601 formatted time::

  >>> aniso8601.parse_time('22:33.5')
  datetime.time(22, 33, 30)
  >>> aniso8601.parse_time('23.75')
  datetime.time(23, 45)

The decimal fraction can be specified with a comma instead of a full-stop::

  >>> aniso8601.parse_time('22:33,5')
  datetime.time(22, 33, 30)
  >>> aniso8601.parse_time('23,75')
  datetime.time(23, 45)

Leap seconds are currently not supported and attempting to parse one raises a :code:`LeapSecondError`::

  >>> aniso8601.parse_time('23:59:60')
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/time.py", line 174, in parse_time
      return builder.build_time(hh=hourstr, mm=minutestr, ss=secondstr, tz=tz)
    File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py", line 191, in build_time
      hh, mm, ss, tz = cls.range_check_time(hh, mm, ss, tz)
    File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/__init__.py", line 266, in range_check_time
      raise LeapSecondError('Leap seconds are not supported.')
  aniso8601.exceptions.LeapSecondError: Leap seconds are not supported.

To get the resolution of an ISO 8601 time string::

  >>> aniso8601.get_time_resolution('11:31:14') == aniso8601.resolution.TimeResolution.Seconds
  True
  >>> aniso8601.get_time_resolution('11:31') == aniso8601.resolution.TimeResolution.Minutes
  True
  >>> aniso8601.get_time_resolution('11') == aniso8601.resolution.TimeResolution.Hours
  True

Parsing durations
-----------------

To parse a duration formatted as an ISO 8601 string::

  >>> import aniso8601
  >>> aniso8601.parse_duration('P1Y2M3DT4H54M6S')
  datetime.timedelta(428, 17646)

Reduced accuracy is supported::

  >>> aniso8601.parse_duration('P1Y')
  datetime.timedelta(365)

A decimal fraction is allowed on the lowest order element::

  >>> aniso8601.parse_duration('P1YT3.5M')
  datetime.timedelta(365, 210)

The decimal fraction can be specified with a comma instead of a full-stop::

  >>> aniso8601.parse_duration('P1YT3,5M')
  datetime.timedelta(365, 210)

Parsing a duration from a combined date and time is supported as well::

  >>> aniso8601.parse_duration('P0001-01-02T01:30:05')
  datetime.timedelta(397, 5405)

To get the resolution of an ISO 8601 duration string::

  >>> aniso8601.get_duration_resolution('P1Y2M3DT4H54M6S') == aniso8601.resolution.DurationResolution.Seconds
  True
  >>> aniso8601.get_duration_resolution('P1Y2M3DT4H54M') == aniso8601.resolution.DurationResolution.Minutes
  True
  >>> aniso8601.get_duration_resolution('P1Y2M3DT4H') == aniso8601.resolution.DurationResolution.Hours
  True
  >>> aniso8601.get_duration_resolution('P1Y2M3D') == aniso8601.resolution.DurationResolution.Days
  True
  >>> aniso8601.get_duration_resolution('P1Y2M') == aniso8601.resolution.DurationResolution.Months
  True
  >>> aniso8601.get_duration_resolution('P1Y') == aniso8601.resolution.DurationResolution.Years
  True

The default :code:`PythonTimeBuilder` assumes years are 365 days, and months are 30 days. Where calendar level accuracy is required, a `RelativeTimeBuilder <https://bitbucket.org/nielsenb/relativetimebuilder>`_ can be used, see also `Builders`_.

Parsing intervals
-----------------

To parse an interval specified by a start and end::

  >>> import aniso8601
  >>> aniso8601.parse_interval('2007-03-01T13:00:00/2008-05-11T15:30:00')
  (datetime.datetime(2007, 3, 1, 13, 0), datetime.datetime(2008, 5, 11, 15, 30))

Intervals specified by a start time and a duration are supported::

  >>> aniso8601.parse_interval('2007-03-01T13:00:00Z/P1Y2M10DT2H30M')
  (datetime.datetime(2007, 3, 1, 13, 0, tzinfo=+0:00:00 UTC), datetime.datetime(2008, 5, 9, 15, 30, tzinfo=+0:00:00 UTC))

A duration can also be specified by a duration and end time::

  >>> aniso8601.parse_interval('P1M/1981-04-05')
  (datetime.date(1981, 4, 5), datetime.date(1981, 3, 6))

Notice that the result of the above parse is not in order from earliest to latest. If sorted intervals are required, simply use the :code:`sorted` keyword as shown below::

  >>> sorted(aniso8601.parse_interval('P1M/1981-04-05'))
  [datetime.date(1981, 3, 6), datetime.date(1981, 4, 5)]

The end of an interval is returned as a datetime when required to maintain the resolution specified by a duration, even if the duration start is given as a date::

  >>> aniso8601.parse_interval('2014-11-12/PT4H54M6.5S')
  (datetime.date(2014, 11, 12), datetime.datetime(2014, 11, 12, 4, 54, 6, 500000))
  >>> aniso8601.parse_interval('2007-03-01/P1.5D')
  (datetime.date(2007, 3, 1), datetime.datetime(2007, 3, 2, 12, 0))

Concise representations are supported::

  >>> aniso8601.parse_interval('2020-01-01/02')
  (datetime.date(2020, 1, 1), datetime.date(2020, 1, 2))
  >>> aniso8601.parse_interval('2007-12-14T13:30/15:30')
  (datetime.datetime(2007, 12, 14, 13, 30), datetime.datetime(2007, 12, 14, 15, 30))
  >>> aniso8601.parse_interval('2008-02-15/03-14')
  (datetime.date(2008, 2, 15), datetime.date(2008, 3, 14))
  >>> aniso8601.parse_interval('2007-11-13T09:00/15T17:00')
  (datetime.datetime(2007, 11, 13, 9, 0), datetime.datetime(2007, 11, 15, 17, 0))

Repeating intervals are supported as well, and return a `generator <https://wiki.python.org/moin/Generators>`_::

  >>> aniso8601.parse_repeating_interval('R3/1981-04-05/P1D')
  <generator object _date_generator at 0x7fd800d3b320>
  >>> list(aniso8601.parse_repeating_interval('R3/1981-04-05/P1D'))
  [datetime.date(1981, 4, 5), datetime.date(1981, 4, 6), datetime.date(1981, 4, 7)]

Repeating intervals are allowed to go in the reverse direction::

  >>> list(aniso8601.parse_repeating_interval('R2/PT1H2M/1980-03-05T01:01:00'))
  [datetime.datetime(1980, 3, 5, 1, 1), datetime.datetime(1980, 3, 4, 23, 59)]

Unbounded intervals are also allowed (Python 2)::

  >>> result = aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00')
  >>> result.next()
  datetime.datetime(1980, 3, 5, 1, 1)
  >>> result.next()
  datetime.datetime(1980, 3, 4, 23, 59)

or for Python 3::

  >>> result = aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00')
  >>> next(result)
  datetime.datetime(1980, 3, 5, 1, 1)
  >>> next(result)
  datetime.datetime(1980, 3, 4, 23, 59)

Note that you should never try to convert a generator produced by an unbounded interval to a list::

  >>> list(aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00'))
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py", line 560, in _date_generator_unbounded
      currentdate += timedelta
  OverflowError: date value out of range

To get the resolution of an ISO 8601 interval string::

  >>> aniso8601.get_interval_resolution('2007-03-01T13:00:00/2008-05-11T15:30:00') == aniso8601.resolution.IntervalResolution.Seconds
  True
  >>> aniso8601.get_interval_resolution('2007-03-01T13:00/2008-05-11T15:30') == aniso8601.resolution.IntervalResolution.Minutes
  True
  >>> aniso8601.get_interval_resolution('2007-03-01T13/2008-05-11T15') == aniso8601.resolution.IntervalResolution.Hours
  True
  >>> aniso8601.get_interval_resolution('2007-03-01/2008-05-11') == aniso8601.resolution.IntervalResolution.Day
  True
  >>> aniso8601.get_interval_resolution('2007-03/P1Y') == aniso8601.resolution.IntervalResolution.Month
  True
  >>> aniso8601.get_interval_resolution('2007/P1Y') == aniso8601.resolution.IntervalResolution.Year
  True

And for repeating ISO 8601 interval strings::

  >>> aniso8601.get_repeating_interval_resolution('R3/1981-04-05/P1D') == aniso8601.resolution.IntervalResolution.Day
  True
  >>> aniso8601.get_repeating_interval_resolution('R/PT1H2M/1980-03-05T01:01:00') == aniso8601.resolution.IntervalResolution.Seconds
  True

Builders
========

Builders can be used to change the output format of a parse operation. All parse functions have a :code:`builder` keyword argument which accepts a builder class.

Two builders are included. The :code:`PythonTimeBuilder` (the default) in the :code:`aniso8601.builders.python` module, and the :code:`TupleBuilder` which returns the parse result as a corresponding named tuple and is located in the :code:`aniso8601.builders` module.

Information on writing a builder can be found in `BUILDERS </BUILDERS.rst>`_.

The following builders are available as separate projects:

* `RelativeTimeBuilder <https://bitbucket.org/nielsenb/relativetimebuilder>`_ supports parsing to `datetutil relativedelta types <https://dateutil.readthedocs.io/en/stable/relativedelta.html>`_ for calendar level accuracy
* `AttoTimeBuilder <https://bitbucket.org/nielsenb/attotimebuilder>`_ supports parsing directly to `attotime attodatetime and attotimedelta types <https://bitbucket.org/nielsenb/attotime>`_ which support sub-nanosecond precision
* `NumPyTimeBuilder <https://bitbucket.org/nielsenb/numpytimebuilder>`_ supports parsing directly to `NumPy datetime64 and timedelta64 types <https://docs.scipy.org/doc/numpy/reference/arrays.datetime.html>`_

TupleBuilder
------------

The :code:`TupleBuilder` returns parse results as `named tuples <https://docs.python.org/3/library/collections.html#collections.namedtuple>`_. It is located in the :code:`aniso8601.builders` module.

Datetimes
^^^^^^^^^

Parsing a datetime returns a :code:`DatetimeTuple` containing :code:`Date` and :code:`Time` tuples . The date tuple contains the following parse components: :code:`YYYY`, :code:`MM`, :code:`DD`, :code:`Www`, :code:`D`, :code:`DDD`. The time tuple contains the following parse components :code:`hh`, :code:`mm`, :code:`ss`, :code:`tz`, where :code:`tz` itself is a tuple with the following components :code:`negative`, :code:`Z`, :code:`hh`, :code:`mm`, :code:`name` with :code:`negative` and :code:`Z` being booleans::

  >>> import aniso8601
  >>> from aniso8601.builders import TupleBuilder
  >>> aniso8601.parse_datetime('1977-06-10T12:00:00', builder=TupleBuilder)
  Datetime(date=Date(YYYY='1977', MM='06', DD='10', Www=None, D=None, DDD=None), time=Time(hh='12', mm='00', ss='00', tz=None))
  >>> aniso8601.parse_datetime('1979-06-05T08:00:00-08:00', builder=TupleBuilder)
  Datetime(date=Date(YYYY='1979', MM='06', DD='05', Www=None, D=None, DDD=None), time=Time(hh='08', mm='00', ss='00', tz=Timezone(negative=True, Z=None, hh='08', mm='00', name='-08:00')))

Dates
^^^^^

Parsing a date returns a :code:`DateTuple` containing the following parse components: :code:`YYYY`, :code:`MM`, :code:`DD`, :code:`Www`, :code:`D`, :code:`DDD`::

  >>> import aniso8601
  >>> from aniso8601.builders import TupleBuilder
  >>> aniso8601.parse_date('1984-04-23', builder=TupleBuilder)
  Date(YYYY='1984', MM='04', DD='23', Www=None, D=None, DDD=None)
  >>> aniso8601.parse_date('1986-W38-1', builder=TupleBuilder)
  Date(YYYY='1986', MM=None, DD=None, Www='38', D='1', DDD=None)
  >>> aniso8601.parse_date('1988-132', builder=TupleBuilder)
  Date(YYYY='1988', MM=None, DD=None, Www=None, D=None, DDD='132')

Times
^^^^^

Parsing a time returns a :code:`TimeTuple` containing following parse components: :code:`hh`, :code:`mm`, :code:`ss`, :code:`tz`, where :code:`tz` is a :code:`TimezoneTuple` with the following components :code:`negative`, :code:`Z`, :code:`hh`, :code:`mm`, :code:`name`, with :code:`negative` and :code:`Z` being booleans::

  >>> import aniso8601
  >>> from aniso8601.builders import TupleBuilder
  >>> aniso8601.parse_time('11:31:14', builder=TupleBuilder)
  Time(hh='11', mm='31', ss='14', tz=None)
  >>> aniso8601.parse_time('171819Z', builder=TupleBuilder)
  Time(hh='17', mm='18', ss='19', tz=Timezone(negative=False, Z=True, hh=None, mm=None, name='Z'))
  >>> aniso8601.parse_time('17:18:19-02:30', builder=TupleBuilder)
  Time(hh='17', mm='18', ss='19', tz=Timezone(negative=True, Z=None, hh='02', mm='30', name='-02:30'))

Durations
^^^^^^^^^

Parsing a duration returns a :code:`DurationTuple` containing the following parse components: :code:`PnY`, :code:`PnM`, :code:`PnW`, :code:`PnD`, :code:`TnH`, :code:`TnM`, :code:`TnS`::

  >>> import aniso8601
  >>> from aniso8601.builders import TupleBuilder
  >>> aniso8601.parse_duration('P1Y2M3DT4H54M6S', builder=TupleBuilder)
  Duration(PnY='1', PnM='2', PnW=None, PnD='3', TnH='4', TnM='54', TnS='6')
  >>> aniso8601.parse_duration('P7W', builder=TupleBuilder)
  Duration(PnY=None, PnM=None, PnW='7', PnD=None, TnH=None, TnM=None, TnS=None)

Intervals
^^^^^^^^^

Parsing an interval returns an :code:`IntervalTuple` containing the following parse components: :code:`start`, :code:`end`, :code:`duration`, :code:`start` and :code:`end` may both be datetime or date tuples, :code:`duration` is a duration tuple::

  >>> import aniso8601
  >>> from aniso8601.builders import TupleBuilder
  >>> aniso8601.parse_interval('2007-03-01T13:00:00/2008-05-11T15:30:00', builder=TupleBuilder)
  Interval(start=Datetime(date=Date(YYYY='2007', MM='03', DD='01', Www=None, D=None, DDD=None), time=Time(hh='13', mm='00', ss='00', tz=None)), end=Datetime(date=Date(YYYY='2008', MM='05', DD='11', Www=None, D=None, DDD=None), time=Time(hh='15', mm='30', ss='00', tz=None)), duration=None)
  >>> aniso8601.parse_interval('2007-03-01T13:00:00Z/P1Y2M10DT2H30M', builder=TupleBuilder)
  Interval(start=Datetime(date=Date(YYYY='2007', MM='03', DD='01', Www=None, D=None, DDD=None), time=Time(hh='13', mm='00', ss='00', tz=Timezone(negative=False, Z=True, hh=None, mm=None, name='Z'))), end=None, duration=Duration(PnY='1', PnM='2', PnW=None, PnD='10', TnH='2', TnM='30', TnS=None))
  >>> aniso8601.parse_interval('P1M/1981-04-05', builder=TupleBuilder)
  Interval(start=None, end=Date(YYYY='1981', MM='04', DD='05', Www=None, D=None, DDD=None), duration=Duration(PnY=None, PnM='1', PnW=None, PnD=None, TnH=None, TnM=None, TnS=None))

A repeating interval returns a :code:`RepeatingIntervalTuple` containing the following parse components: :code:`R`, :code:`Rnn`, :code:`interval`, where :code:`R` is a boolean, :code:`True` for an unbounded interval, :code:`False` otherwise.::

  >>> aniso8601.parse_repeating_interval('R3/1981-04-05/P1D', builder=TupleBuilder)
  RepeatingInterval(R=False, Rnn='3', interval=Interval(start=Date(YYYY='1981', MM='04', DD='05', Www=None, D=None, DDD=None), end=None, duration=Duration(PnY=None, PnM=None, PnW=None, PnD='1', TnH=None, TnM=None, TnS=None)))
  >>> aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00', builder=TupleBuilder)
  RepeatingInterval(R=True, Rnn=None, interval=Interval(start=None, end=Datetime(date=Date(YYYY='1980', MM='03', DD='05', Www=None, D=None, DDD=None), time=Time(hh='01', mm='01', ss='00', tz=None)), duration=Duration(PnY=None, PnM=None, PnW=None, PnD=None, TnH='1', TnM='2', TnS=None)))

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 aniso8601

Contributing
============

aniso8601 is an open source project hosted on `Bitbucket <https://bitbucket.org/nielsenb/aniso8601>`_.

Any and all bugs are welcome on our `issue tracker <https://bitbucket.org/nielsenb/aniso8601/issues>`_.
Of particular interest are valid ISO 8601 strings that don't parse, or invalid ones that do. At a minimum,
bug reports should include an example of the misbehaving string, as well as the expected result. Of course
patches containing unit tests (or fixed bugs) are welcome!

References
==========

* `ISO 8601:2004(E) <http://dotat.at/tmp/ISO_8601-2004_E.pdf>`_ (Caution, PDF link)
* `Wikipedia article on ISO 8601 <http://en.wikipedia.org/wiki/Iso8601>`_
* `Discussion on alternative ISO 8601 parsers for Python <https://groups.google.com/forum/#!topic/comp.lang.python/Q2w4R89Nq1w>`_



            

Raw data

            {
    "_id": null,
    "home_page": "https://bitbucket.org/nielsenb/aniso8601",
    "name": "aniso8601",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "iso8601 parser",
    "author": "Brandon Nielsen",
    "author_email": "nielsenb@jetfuse.net",
    "download_url": "https://files.pythonhosted.org/packages/cb/72/be3db445b03944bfbb2b02b82d00cb2a2bcf96275c4543f14bf60fa79e12/aniso8601-9.0.1.tar.gz",
    "platform": "",
    "description": "aniso8601\n=========\n\nAnother ISO 8601 parser for Python\n----------------------------------\n\nFeatures\n========\n* Pure Python implementation\n* Logical behavior\n\n  - Parse a time, get a `datetime.time <http://docs.python.org/3/library/datetime.html#datetime.time>`_\n  - Parse a date, get a `datetime.date <http://docs.python.org/3/library/datetime.html#datetime.date>`_\n  - Parse a datetime, get a `datetime.datetime <http://docs.python.org/3/library/datetime.html#datetime.datetime>`_\n  - Parse a duration, get a `datetime.timedelta <http://docs.python.org/3/library/datetime.html#datetime.timedelta>`_\n  - Parse an interval, get a tuple of dates or datetimes\n  - Parse a repeating interval, get a date or datetime `generator <https://wiki.python.org/moin/Generators>`_\n\n* UTC offset represented as fixed-offset tzinfo\n* Parser separate from representation, allowing parsing to different datetime representations (see `Builders`_)\n* No regular expressions\n\nInstallation\n============\n\nThe recommended installation method is to use pip::\n\n  $ pip install aniso8601\n\nAlternatively, you can download the source (git repository hosted at `Bitbucket <https://bitbucket.org/nielsenb/aniso8601>`_) and install directly::\n\n  $ python setup.py install\n\nUse\n===\n\nParsing datetimes\n-----------------\n\n*Consider* `datetime.datetime.fromisoformat <https://docs.python.org/3/library/datetime.html#datetime.datetime.fromisoformat>`_ *for basic ISO 8601 datetime parsing*\n\nTo parse a typical ISO 8601 datetime string::\n\n  >>> import aniso8601\n  >>> aniso8601.parse_datetime('1977-06-10T12:00:00Z')\n  datetime.datetime(1977, 6, 10, 12, 0, tzinfo=+0:00:00 UTC)\n\nAlternative delimiters can be specified, for example, a space::\n\n  >>> aniso8601.parse_datetime('1977-06-10 12:00:00Z', delimiter=' ')\n  datetime.datetime(1977, 6, 10, 12, 0, tzinfo=+0:00:00 UTC)\n\nUTC offsets are supported::\n\n  >>> aniso8601.parse_datetime('1979-06-05T08:00:00-08:00')\n  datetime.datetime(1979, 6, 5, 8, 0, tzinfo=-8:00:00 UTC)\n\nIf a UTC offset is not specified, the returned datetime will be naive::\n\n  >>> aniso8601.parse_datetime('1983-01-22T08:00:00')\n  datetime.datetime(1983, 1, 22, 8, 0)\n\nLeap seconds are currently not supported and attempting to parse one raises a :code:`LeapSecondError`::\n\n  >>> aniso8601.parse_datetime('2018-03-06T23:59:60')\n  Traceback (most recent call last):\n    File \"<stdin>\", line 1, in <module>\n    File \"/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/time.py\", line 196, in parse_datetime\n      return builder.build_datetime(datepart, timepart)\n    File \"/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py\", line 237, in build_datetime\n      cls._build_object(time))\n    File \"/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/__init__.py\", line 336, in _build_object\n      return cls.build_time(hh=parsetuple.hh, mm=parsetuple.mm,\n    File \"/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py\", line 191, in build_time\n      hh, mm, ss, tz = cls.range_check_time(hh, mm, ss, tz)\n    File \"/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/__init__.py\", line 266, in range_check_time\n      raise LeapSecondError('Leap seconds are not supported.')\n  aniso8601.exceptions.LeapSecondError: Leap seconds are not supported.\n\nTo get the resolution of an ISO 8601 datetime string::\n\n  >>> aniso8601.get_datetime_resolution('1977-06-10T12:00:00Z') == aniso8601.resolution.TimeResolution.Seconds\n  True\n  >>> aniso8601.get_datetime_resolution('1977-06-10T12:00') == aniso8601.resolution.TimeResolution.Minutes\n  True\n  >>> aniso8601.get_datetime_resolution('1977-06-10T12') == aniso8601.resolution.TimeResolution.Hours\n  True\n\nNote that datetime resolutions map to :code:`TimeResolution` as a valid datetime must have at least one time member so the resolution mapping is equivalent.\n\nParsing dates\n-------------\n\n*Consider* `datetime.date.fromisoformat <https://docs.python.org/3/library/datetime.html#datetime.date.fromisoformat>`_ *for basic ISO 8601 date parsing*\n\nTo parse a date represented in an ISO 8601 string::\n\n  >>> import aniso8601\n  >>> aniso8601.parse_date('1984-04-23')\n  datetime.date(1984, 4, 23)\n\nBasic format is supported as well::\n\n  >>> aniso8601.parse_date('19840423')\n  datetime.date(1984, 4, 23)\n\nTo parse a date using the ISO 8601 week date format::\n\n  >>> aniso8601.parse_date('1986-W38-1')\n  datetime.date(1986, 9, 15)\n\nTo parse an ISO 8601 ordinal date::\n\n  >>> aniso8601.parse_date('1988-132')\n  datetime.date(1988, 5, 11)\n\nTo get the resolution of an ISO 8601 date string::\n\n  >>> aniso8601.get_date_resolution('1981-04-05') == aniso8601.resolution.DateResolution.Day\n  True\n  >>> aniso8601.get_date_resolution('1981-04') == aniso8601.resolution.DateResolution.Month\n  True\n  >>> aniso8601.get_date_resolution('1981') == aniso8601.resolution.DateResolution.Year\n  True\n\nParsing times\n-------------\n\n*Consider* `datetime.time.fromisoformat <https://docs.python.org/3/library/datetime.html#datetime.time.fromisoformat>`_ *for basic ISO 8601 time parsing*\n\nTo parse a time formatted as an ISO 8601 string::\n\n  >>> import aniso8601\n  >>> aniso8601.parse_time('11:31:14')\n  datetime.time(11, 31, 14)\n\nAs with all of the above, basic format is supported::\n\n  >>> aniso8601.parse_time('113114')\n  datetime.time(11, 31, 14)\n\nA UTC offset can be specified for times::\n\n  >>> aniso8601.parse_time('17:18:19-02:30')\n  datetime.time(17, 18, 19, tzinfo=-2:30:00 UTC)\n  >>> aniso8601.parse_time('171819Z')\n  datetime.time(17, 18, 19, tzinfo=+0:00:00 UTC)\n\nReduced accuracy is supported::\n\n  >>> aniso8601.parse_time('21:42')\n  datetime.time(21, 42)\n  >>> aniso8601.parse_time('22')\n  datetime.time(22, 0)\n\nA decimal fraction is always allowed on the lowest order element of an ISO 8601 formatted time::\n\n  >>> aniso8601.parse_time('22:33.5')\n  datetime.time(22, 33, 30)\n  >>> aniso8601.parse_time('23.75')\n  datetime.time(23, 45)\n\nThe decimal fraction can be specified with a comma instead of a full-stop::\n\n  >>> aniso8601.parse_time('22:33,5')\n  datetime.time(22, 33, 30)\n  >>> aniso8601.parse_time('23,75')\n  datetime.time(23, 45)\n\nLeap seconds are currently not supported and attempting to parse one raises a :code:`LeapSecondError`::\n\n  >>> aniso8601.parse_time('23:59:60')\n  Traceback (most recent call last):\n    File \"<stdin>\", line 1, in <module>\n    File \"/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/time.py\", line 174, in parse_time\n      return builder.build_time(hh=hourstr, mm=minutestr, ss=secondstr, tz=tz)\n    File \"/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py\", line 191, in build_time\n      hh, mm, ss, tz = cls.range_check_time(hh, mm, ss, tz)\n    File \"/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/__init__.py\", line 266, in range_check_time\n      raise LeapSecondError('Leap seconds are not supported.')\n  aniso8601.exceptions.LeapSecondError: Leap seconds are not supported.\n\nTo get the resolution of an ISO 8601 time string::\n\n  >>> aniso8601.get_time_resolution('11:31:14') == aniso8601.resolution.TimeResolution.Seconds\n  True\n  >>> aniso8601.get_time_resolution('11:31') == aniso8601.resolution.TimeResolution.Minutes\n  True\n  >>> aniso8601.get_time_resolution('11') == aniso8601.resolution.TimeResolution.Hours\n  True\n\nParsing durations\n-----------------\n\nTo parse a duration formatted as an ISO 8601 string::\n\n  >>> import aniso8601\n  >>> aniso8601.parse_duration('P1Y2M3DT4H54M6S')\n  datetime.timedelta(428, 17646)\n\nReduced accuracy is supported::\n\n  >>> aniso8601.parse_duration('P1Y')\n  datetime.timedelta(365)\n\nA decimal fraction is allowed on the lowest order element::\n\n  >>> aniso8601.parse_duration('P1YT3.5M')\n  datetime.timedelta(365, 210)\n\nThe decimal fraction can be specified with a comma instead of a full-stop::\n\n  >>> aniso8601.parse_duration('P1YT3,5M')\n  datetime.timedelta(365, 210)\n\nParsing a duration from a combined date and time is supported as well::\n\n  >>> aniso8601.parse_duration('P0001-01-02T01:30:05')\n  datetime.timedelta(397, 5405)\n\nTo get the resolution of an ISO 8601 duration string::\n\n  >>> aniso8601.get_duration_resolution('P1Y2M3DT4H54M6S') == aniso8601.resolution.DurationResolution.Seconds\n  True\n  >>> aniso8601.get_duration_resolution('P1Y2M3DT4H54M') == aniso8601.resolution.DurationResolution.Minutes\n  True\n  >>> aniso8601.get_duration_resolution('P1Y2M3DT4H') == aniso8601.resolution.DurationResolution.Hours\n  True\n  >>> aniso8601.get_duration_resolution('P1Y2M3D') == aniso8601.resolution.DurationResolution.Days\n  True\n  >>> aniso8601.get_duration_resolution('P1Y2M') == aniso8601.resolution.DurationResolution.Months\n  True\n  >>> aniso8601.get_duration_resolution('P1Y') == aniso8601.resolution.DurationResolution.Years\n  True\n\nThe default :code:`PythonTimeBuilder` assumes years are 365 days, and months are 30 days. Where calendar level accuracy is required, a `RelativeTimeBuilder <https://bitbucket.org/nielsenb/relativetimebuilder>`_ can be used, see also `Builders`_.\n\nParsing intervals\n-----------------\n\nTo parse an interval specified by a start and end::\n\n  >>> import aniso8601\n  >>> aniso8601.parse_interval('2007-03-01T13:00:00/2008-05-11T15:30:00')\n  (datetime.datetime(2007, 3, 1, 13, 0), datetime.datetime(2008, 5, 11, 15, 30))\n\nIntervals specified by a start time and a duration are supported::\n\n  >>> aniso8601.parse_interval('2007-03-01T13:00:00Z/P1Y2M10DT2H30M')\n  (datetime.datetime(2007, 3, 1, 13, 0, tzinfo=+0:00:00 UTC), datetime.datetime(2008, 5, 9, 15, 30, tzinfo=+0:00:00 UTC))\n\nA duration can also be specified by a duration and end time::\n\n  >>> aniso8601.parse_interval('P1M/1981-04-05')\n  (datetime.date(1981, 4, 5), datetime.date(1981, 3, 6))\n\nNotice that the result of the above parse is not in order from earliest to latest. If sorted intervals are required, simply use the :code:`sorted` keyword as shown below::\n\n  >>> sorted(aniso8601.parse_interval('P1M/1981-04-05'))\n  [datetime.date(1981, 3, 6), datetime.date(1981, 4, 5)]\n\nThe end of an interval is returned as a datetime when required to maintain the resolution specified by a duration, even if the duration start is given as a date::\n\n  >>> aniso8601.parse_interval('2014-11-12/PT4H54M6.5S')\n  (datetime.date(2014, 11, 12), datetime.datetime(2014, 11, 12, 4, 54, 6, 500000))\n  >>> aniso8601.parse_interval('2007-03-01/P1.5D')\n  (datetime.date(2007, 3, 1), datetime.datetime(2007, 3, 2, 12, 0))\n\nConcise representations are supported::\n\n  >>> aniso8601.parse_interval('2020-01-01/02')\n  (datetime.date(2020, 1, 1), datetime.date(2020, 1, 2))\n  >>> aniso8601.parse_interval('2007-12-14T13:30/15:30')\n  (datetime.datetime(2007, 12, 14, 13, 30), datetime.datetime(2007, 12, 14, 15, 30))\n  >>> aniso8601.parse_interval('2008-02-15/03-14')\n  (datetime.date(2008, 2, 15), datetime.date(2008, 3, 14))\n  >>> aniso8601.parse_interval('2007-11-13T09:00/15T17:00')\n  (datetime.datetime(2007, 11, 13, 9, 0), datetime.datetime(2007, 11, 15, 17, 0))\n\nRepeating intervals are supported as well, and return a `generator <https://wiki.python.org/moin/Generators>`_::\n\n  >>> aniso8601.parse_repeating_interval('R3/1981-04-05/P1D')\n  <generator object _date_generator at 0x7fd800d3b320>\n  >>> list(aniso8601.parse_repeating_interval('R3/1981-04-05/P1D'))\n  [datetime.date(1981, 4, 5), datetime.date(1981, 4, 6), datetime.date(1981, 4, 7)]\n\nRepeating intervals are allowed to go in the reverse direction::\n\n  >>> list(aniso8601.parse_repeating_interval('R2/PT1H2M/1980-03-05T01:01:00'))\n  [datetime.datetime(1980, 3, 5, 1, 1), datetime.datetime(1980, 3, 4, 23, 59)]\n\nUnbounded intervals are also allowed (Python 2)::\n\n  >>> result = aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00')\n  >>> result.next()\n  datetime.datetime(1980, 3, 5, 1, 1)\n  >>> result.next()\n  datetime.datetime(1980, 3, 4, 23, 59)\n\nor for Python 3::\n\n  >>> result = aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00')\n  >>> next(result)\n  datetime.datetime(1980, 3, 5, 1, 1)\n  >>> next(result)\n  datetime.datetime(1980, 3, 4, 23, 59)\n\nNote that you should never try to convert a generator produced by an unbounded interval to a list::\n\n  >>> list(aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00'))\n  Traceback (most recent call last):\n    File \"<stdin>\", line 1, in <module>\n    File \"/home/nielsenb/Jetfuse/aniso8601/aniso8601/aniso8601/builders/python.py\", line 560, in _date_generator_unbounded\n      currentdate += timedelta\n  OverflowError: date value out of range\n\nTo get the resolution of an ISO 8601 interval string::\n\n  >>> aniso8601.get_interval_resolution('2007-03-01T13:00:00/2008-05-11T15:30:00') == aniso8601.resolution.IntervalResolution.Seconds\n  True\n  >>> aniso8601.get_interval_resolution('2007-03-01T13:00/2008-05-11T15:30') == aniso8601.resolution.IntervalResolution.Minutes\n  True\n  >>> aniso8601.get_interval_resolution('2007-03-01T13/2008-05-11T15') == aniso8601.resolution.IntervalResolution.Hours\n  True\n  >>> aniso8601.get_interval_resolution('2007-03-01/2008-05-11') == aniso8601.resolution.IntervalResolution.Day\n  True\n  >>> aniso8601.get_interval_resolution('2007-03/P1Y') == aniso8601.resolution.IntervalResolution.Month\n  True\n  >>> aniso8601.get_interval_resolution('2007/P1Y') == aniso8601.resolution.IntervalResolution.Year\n  True\n\nAnd for repeating ISO 8601 interval strings::\n\n  >>> aniso8601.get_repeating_interval_resolution('R3/1981-04-05/P1D') == aniso8601.resolution.IntervalResolution.Day\n  True\n  >>> aniso8601.get_repeating_interval_resolution('R/PT1H2M/1980-03-05T01:01:00') == aniso8601.resolution.IntervalResolution.Seconds\n  True\n\nBuilders\n========\n\nBuilders can be used to change the output format of a parse operation. All parse functions have a :code:`builder` keyword argument which accepts a builder class.\n\nTwo builders are included. The :code:`PythonTimeBuilder` (the default) in the :code:`aniso8601.builders.python` module, and the :code:`TupleBuilder` which returns the parse result as a corresponding named tuple and is located in the :code:`aniso8601.builders` module.\n\nInformation on writing a builder can be found in `BUILDERS </BUILDERS.rst>`_.\n\nThe following builders are available as separate projects:\n\n* `RelativeTimeBuilder <https://bitbucket.org/nielsenb/relativetimebuilder>`_ supports parsing to `datetutil relativedelta types <https://dateutil.readthedocs.io/en/stable/relativedelta.html>`_ for calendar level accuracy\n* `AttoTimeBuilder <https://bitbucket.org/nielsenb/attotimebuilder>`_ supports parsing directly to `attotime attodatetime and attotimedelta types <https://bitbucket.org/nielsenb/attotime>`_ which support sub-nanosecond precision\n* `NumPyTimeBuilder <https://bitbucket.org/nielsenb/numpytimebuilder>`_ supports parsing directly to `NumPy datetime64 and timedelta64 types <https://docs.scipy.org/doc/numpy/reference/arrays.datetime.html>`_\n\nTupleBuilder\n------------\n\nThe :code:`TupleBuilder` returns parse results as `named tuples <https://docs.python.org/3/library/collections.html#collections.namedtuple>`_. It is located in the :code:`aniso8601.builders` module.\n\nDatetimes\n^^^^^^^^^\n\nParsing a datetime returns a :code:`DatetimeTuple` containing :code:`Date` and :code:`Time` tuples . The date tuple contains the following parse components: :code:`YYYY`, :code:`MM`, :code:`DD`, :code:`Www`, :code:`D`, :code:`DDD`. The time tuple contains the following parse components :code:`hh`, :code:`mm`, :code:`ss`, :code:`tz`, where :code:`tz` itself is a tuple with the following components :code:`negative`, :code:`Z`, :code:`hh`, :code:`mm`, :code:`name` with :code:`negative` and :code:`Z` being booleans::\n\n  >>> import aniso8601\n  >>> from aniso8601.builders import TupleBuilder\n  >>> aniso8601.parse_datetime('1977-06-10T12:00:00', builder=TupleBuilder)\n  Datetime(date=Date(YYYY='1977', MM='06', DD='10', Www=None, D=None, DDD=None), time=Time(hh='12', mm='00', ss='00', tz=None))\n  >>> aniso8601.parse_datetime('1979-06-05T08:00:00-08:00', builder=TupleBuilder)\n  Datetime(date=Date(YYYY='1979', MM='06', DD='05', Www=None, D=None, DDD=None), time=Time(hh='08', mm='00', ss='00', tz=Timezone(negative=True, Z=None, hh='08', mm='00', name='-08:00')))\n\nDates\n^^^^^\n\nParsing a date returns a :code:`DateTuple` containing the following parse components: :code:`YYYY`, :code:`MM`, :code:`DD`, :code:`Www`, :code:`D`, :code:`DDD`::\n\n  >>> import aniso8601\n  >>> from aniso8601.builders import TupleBuilder\n  >>> aniso8601.parse_date('1984-04-23', builder=TupleBuilder)\n  Date(YYYY='1984', MM='04', DD='23', Www=None, D=None, DDD=None)\n  >>> aniso8601.parse_date('1986-W38-1', builder=TupleBuilder)\n  Date(YYYY='1986', MM=None, DD=None, Www='38', D='1', DDD=None)\n  >>> aniso8601.parse_date('1988-132', builder=TupleBuilder)\n  Date(YYYY='1988', MM=None, DD=None, Www=None, D=None, DDD='132')\n\nTimes\n^^^^^\n\nParsing a time returns a :code:`TimeTuple` containing following parse components: :code:`hh`, :code:`mm`, :code:`ss`, :code:`tz`, where :code:`tz` is a :code:`TimezoneTuple` with the following components :code:`negative`, :code:`Z`, :code:`hh`, :code:`mm`, :code:`name`, with :code:`negative` and :code:`Z` being booleans::\n\n  >>> import aniso8601\n  >>> from aniso8601.builders import TupleBuilder\n  >>> aniso8601.parse_time('11:31:14', builder=TupleBuilder)\n  Time(hh='11', mm='31', ss='14', tz=None)\n  >>> aniso8601.parse_time('171819Z', builder=TupleBuilder)\n  Time(hh='17', mm='18', ss='19', tz=Timezone(negative=False, Z=True, hh=None, mm=None, name='Z'))\n  >>> aniso8601.parse_time('17:18:19-02:30', builder=TupleBuilder)\n  Time(hh='17', mm='18', ss='19', tz=Timezone(negative=True, Z=None, hh='02', mm='30', name='-02:30'))\n\nDurations\n^^^^^^^^^\n\nParsing a duration returns a :code:`DurationTuple` containing the following parse components: :code:`PnY`, :code:`PnM`, :code:`PnW`, :code:`PnD`, :code:`TnH`, :code:`TnM`, :code:`TnS`::\n\n  >>> import aniso8601\n  >>> from aniso8601.builders import TupleBuilder\n  >>> aniso8601.parse_duration('P1Y2M3DT4H54M6S', builder=TupleBuilder)\n  Duration(PnY='1', PnM='2', PnW=None, PnD='3', TnH='4', TnM='54', TnS='6')\n  >>> aniso8601.parse_duration('P7W', builder=TupleBuilder)\n  Duration(PnY=None, PnM=None, PnW='7', PnD=None, TnH=None, TnM=None, TnS=None)\n\nIntervals\n^^^^^^^^^\n\nParsing an interval returns an :code:`IntervalTuple` containing the following parse components: :code:`start`, :code:`end`, :code:`duration`, :code:`start` and :code:`end` may both be datetime or date tuples, :code:`duration` is a duration tuple::\n\n  >>> import aniso8601\n  >>> from aniso8601.builders import TupleBuilder\n  >>> aniso8601.parse_interval('2007-03-01T13:00:00/2008-05-11T15:30:00', builder=TupleBuilder)\n  Interval(start=Datetime(date=Date(YYYY='2007', MM='03', DD='01', Www=None, D=None, DDD=None), time=Time(hh='13', mm='00', ss='00', tz=None)), end=Datetime(date=Date(YYYY='2008', MM='05', DD='11', Www=None, D=None, DDD=None), time=Time(hh='15', mm='30', ss='00', tz=None)), duration=None)\n  >>> aniso8601.parse_interval('2007-03-01T13:00:00Z/P1Y2M10DT2H30M', builder=TupleBuilder)\n  Interval(start=Datetime(date=Date(YYYY='2007', MM='03', DD='01', Www=None, D=None, DDD=None), time=Time(hh='13', mm='00', ss='00', tz=Timezone(negative=False, Z=True, hh=None, mm=None, name='Z'))), end=None, duration=Duration(PnY='1', PnM='2', PnW=None, PnD='10', TnH='2', TnM='30', TnS=None))\n  >>> aniso8601.parse_interval('P1M/1981-04-05', builder=TupleBuilder)\n  Interval(start=None, end=Date(YYYY='1981', MM='04', DD='05', Www=None, D=None, DDD=None), duration=Duration(PnY=None, PnM='1', PnW=None, PnD=None, TnH=None, TnM=None, TnS=None))\n\nA repeating interval returns a :code:`RepeatingIntervalTuple` containing the following parse components: :code:`R`, :code:`Rnn`, :code:`interval`, where :code:`R` is a boolean, :code:`True` for an unbounded interval, :code:`False` otherwise.::\n\n  >>> aniso8601.parse_repeating_interval('R3/1981-04-05/P1D', builder=TupleBuilder)\n  RepeatingInterval(R=False, Rnn='3', interval=Interval(start=Date(YYYY='1981', MM='04', DD='05', Www=None, D=None, DDD=None), end=None, duration=Duration(PnY=None, PnM=None, PnW=None, PnD='1', TnH=None, TnM=None, TnS=None)))\n  >>> aniso8601.parse_repeating_interval('R/PT1H2M/1980-03-05T01:01:00', builder=TupleBuilder)\n  RepeatingInterval(R=True, Rnn=None, interval=Interval(start=None, end=Datetime(date=Date(YYYY='1980', MM='03', DD='05', Www=None, D=None, DDD=None), time=Time(hh='01', mm='01', ss='00', tz=None)), duration=Duration(PnY=None, PnM=None, PnW=None, PnD=None, TnH='1', TnM='2', TnS=None)))\n\nDevelopment\n===========\n\nSetup\n-----\n\nIt is recommended to develop using a `virtualenv <https://virtualenv.pypa.io/en/stable/>`_.\n\nInside a virtualenv, development dependencies can be installed automatically::\n\n  $ pip install -e .[dev]\n\n`pre-commit <https://pre-commit.com/>`_ is used for managing pre-commit hooks::\n\n  $ pre-commit install\n\nTo run the pre-commit hooks manually::\n\n  $ pre-commit run --all-files\n\nTests\n-----\n\nTests can be run using the `unittest testing framework <https://docs.python.org/3/library/unittest.html>`_::\n\n  $ python -m unittest discover aniso8601\n\nContributing\n============\n\naniso8601 is an open source project hosted on `Bitbucket <https://bitbucket.org/nielsenb/aniso8601>`_.\n\nAny and all bugs are welcome on our `issue tracker <https://bitbucket.org/nielsenb/aniso8601/issues>`_.\nOf particular interest are valid ISO 8601 strings that don't parse, or invalid ones that do. At a minimum,\nbug reports should include an example of the misbehaving string, as well as the expected result. Of course\npatches containing unit tests (or fixed bugs) are welcome!\n\nReferences\n==========\n\n* `ISO 8601:2004(E) <http://dotat.at/tmp/ISO_8601-2004_E.pdf>`_ (Caution, PDF link)\n* `Wikipedia article on ISO 8601 <http://en.wikipedia.org/wiki/Iso8601>`_\n* `Discussion on alternative ISO 8601 parsers for Python <https://groups.google.com/forum/#!topic/comp.lang.python/Q2w4R89Nq1w>`_\n\n\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "A library for parsing ISO 8601 strings.",
    "version": "9.0.1",
    "split_keywords": [
        "iso8601",
        "parser"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "md5": "eba845463838ae77585aac484f6a563c",
                "sha256": "1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"
            },
            "downloads": -1,
            "filename": "aniso8601-9.0.1-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "eba845463838ae77585aac484f6a563c",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": null,
            "size": 52754,
            "upload_time": "2021-03-02T01:33:20",
            "upload_time_iso_8601": "2021-03-02T01:33:20.669734Z",
            "url": "https://files.pythonhosted.org/packages/e3/04/e97c12dc034791d7b504860acfcdd2963fa21ae61eaca1c9d31245f812c3/aniso8601-9.0.1-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "md5": "89b25ea9fd70b8facaf189aed43afcf8",
                "sha256": "72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"
            },
            "downloads": -1,
            "filename": "aniso8601-9.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "89b25ea9fd70b8facaf189aed43afcf8",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 47345,
            "upload_time": "2021-03-02T01:33:22",
            "upload_time_iso_8601": "2021-03-02T01:33:22.944020Z",
            "url": "https://files.pythonhosted.org/packages/cb/72/be3db445b03944bfbb2b02b82d00cb2a2bcf96275c4543f14bf60fa79e12/aniso8601-9.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2021-03-02 01:33:22",
    "github": false,
    "gitlab": false,
    "bitbucket": true,
    "bitbucket_user": "nielsenb",
    "bitbucket_project": "aniso8601",
    "lcname": "aniso8601"
}
        
Elapsed time: 0.01430s