pyRFC3339-1.0/0000755000175000017500000000000012620015127012647 5ustar kurtkurt00000000000000pyRFC3339-1.0/LICENSE.txt0000644000175000017500000000204012620014757014476 0ustar kurtkurt00000000000000Copyright (c) 2015 Kurt Raschke 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. pyRFC3339-1.0/PKG-INFO0000644000175000017500000000371112620015127013746 0ustar kurtkurt00000000000000Metadata-Version: 1.1 Name: pyRFC3339 Version: 1.0 Summary: Generate and parse RFC 3339 timestamps Home-page: https://github.com/kurtraschke/pyRFC3339 Author: Kurt Raschke Author-email: kurt@kurtraschke.com License: MIT Description: Description =========== .. image:: https://travis-ci.org/kurtraschke/pyRFC3339.svg?branch=master :target: https://travis-ci.org/kurtraschke/pyRFC3339 pyRFC3339 parses and generates :RFC:`3339`-compliant timestamps using `Python `_ `datetime.datetime `_ objects. >>> from pyrfc3339 import generate, parse >>> from datetime import datetime >>> import pytz >>> generate(datetime.utcnow().replace(tzinfo=pytz.utc)) #doctest:+ELLIPSIS '...T...Z' >>> parse('2009-01-01T10:01:02Z') datetime.datetime(2009, 1, 1, 10, 1, 2, tzinfo=) >>> parse('2009-01-01T14:01:02-04:00') datetime.datetime(2009, 1, 1, 14, 1, 2, tzinfo=) Installation ============ To install the latest version from `PyPI `_: ``$ pip install pyRFC3339`` To install the latest development version: ``$ pip install https://github.com/kurtraschke/pyRFC3339/tarball/master#egg=pyRFC3339-dev`` To build the documentation with Sphinx: #. ``$ pip install Sphinx`` #. ``$ python setup.py build_sphinx`` The documentation is also available online at: ``https://pythonhosted.org/pyRFC3339/`` Keywords: rfc 3339 timestamp Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python Classifier: Topic :: Internet pyRFC3339-1.0/pyRFC3339.egg-info/0000755000175000017500000000000012620015127015646 5ustar kurtkurt00000000000000pyRFC3339-1.0/pyRFC3339.egg-info/PKG-INFO0000644000175000017500000000371112620015123016741 0ustar kurtkurt00000000000000Metadata-Version: 1.1 Name: pyRFC3339 Version: 1.0 Summary: Generate and parse RFC 3339 timestamps Home-page: https://github.com/kurtraschke/pyRFC3339 Author: Kurt Raschke Author-email: kurt@kurtraschke.com License: MIT Description: Description =========== .. image:: https://travis-ci.org/kurtraschke/pyRFC3339.svg?branch=master :target: https://travis-ci.org/kurtraschke/pyRFC3339 pyRFC3339 parses and generates :RFC:`3339`-compliant timestamps using `Python `_ `datetime.datetime `_ objects. >>> from pyrfc3339 import generate, parse >>> from datetime import datetime >>> import pytz >>> generate(datetime.utcnow().replace(tzinfo=pytz.utc)) #doctest:+ELLIPSIS '...T...Z' >>> parse('2009-01-01T10:01:02Z') datetime.datetime(2009, 1, 1, 10, 1, 2, tzinfo=) >>> parse('2009-01-01T14:01:02-04:00') datetime.datetime(2009, 1, 1, 14, 1, 2, tzinfo=) Installation ============ To install the latest version from `PyPI `_: ``$ pip install pyRFC3339`` To install the latest development version: ``$ pip install https://github.com/kurtraschke/pyRFC3339/tarball/master#egg=pyRFC3339-dev`` To build the documentation with Sphinx: #. ``$ pip install Sphinx`` #. ``$ python setup.py build_sphinx`` The documentation is also available online at: ``https://pythonhosted.org/pyRFC3339/`` Keywords: rfc 3339 timestamp Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python Classifier: Topic :: Internet pyRFC3339-1.0/pyRFC3339.egg-info/SOURCES.txt0000644000175000017500000000100112620015123017516 0ustar kurtkurt00000000000000LICENSE.txt MANIFEST.in README.rst setup.cfg setup.py docs/source/conf.py docs/source/doc.rst docs/source/generator.rst docs/source/index.rst docs/source/parser.rst docs/source/pyrfc3339.rst docs/source/utils.rst pyRFC3339.egg-info/PKG-INFO pyRFC3339.egg-info/SOURCES.txt pyRFC3339.egg-info/dependency_links.txt pyRFC3339.egg-info/requires.txt pyRFC3339.egg-info/top_level.txt pyrfc3339/__init__.py pyrfc3339/generator.py pyrfc3339/parser.py pyrfc3339/utils.py pyrfc3339/tests/__init__.py pyrfc3339/tests/tests.pypyRFC3339-1.0/pyRFC3339.egg-info/requires.txt0000644000175000017500000000000512620015123020235 0ustar kurtkurt00000000000000pytz pyRFC3339-1.0/pyRFC3339.egg-info/dependency_links.txt0000644000175000017500000000000112620015123021710 0ustar kurtkurt00000000000000 pyRFC3339-1.0/pyRFC3339.egg-info/top_level.txt0000644000175000017500000000001212620015123020365 0ustar kurtkurt00000000000000pyrfc3339 pyRFC3339-1.0/docs/0000755000175000017500000000000012620015127013577 5ustar kurtkurt00000000000000pyRFC3339-1.0/docs/source/0000755000175000017500000000000012620015127015077 5ustar kurtkurt00000000000000pyRFC3339-1.0/docs/source/conf.py0000644000175000017500000001464012620014747016412 0ustar kurtkurt00000000000000# -*- coding: utf-8 -*- # # pyRFC3339 documentation build configuration file, created by # sphinx-quickstart on Mon Jul 27 19:32:42 2009. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.append(os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = u'pyRFC3339' copyright = u'2015, Kurt Raschke' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '1.0' # The full version, including alpha/beta/rc tags. release = '1.0' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = 'nature' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'pyRFC3339doc' # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'pyRFC3339.tex', u'pyRFC3339 Documentation', u'Kurt Raschke', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None, #'http://pytz.sourceforge.net/':None } pyRFC3339-1.0/docs/source/parser.rst0000644000175000017500000000026212617663276017151 0ustar kurtkurt00000000000000:mod:`pyrfc3339.parser` -- Parse :RFC:`3339` timestamps ============================================================= .. automodule:: pyrfc3339.parser :members: pyRFC3339-1.0/docs/source/pyrfc3339.rst0000644000175000017500000000012612617663276017321 0ustar kurtkurt00000000000000:mod:`pyrfc3339` -- Imports ============================= .. automodule:: pyrfc3339 pyRFC3339-1.0/docs/source/index.rst0000644000175000017500000000054412620012374016744 0ustar kurtkurt00000000000000.. pyRFC3339 documentation master file, created by sphinx-quickstart on Mon Jul 27 19:32:42 2009. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to pyRFC3339's documentation! ===================================== Contents: .. toctree:: :maxdepth: 3 intro doc pyRFC3339-1.0/docs/source/utils.rst0000644000175000017500000000027412617663276017020 0ustar kurtkurt00000000000000:mod:`pyrfc3339.utils` -- Utilities for working with timestamps =============================================================== .. automodule:: pyrfc3339.utils :members: pyRFC3339-1.0/docs/source/doc.rst0000644000175000017500000000030212620013366016374 0ustar kurtkurt00000000000000Modules ======= .. toctree:: :maxdepth: 2 :glob: pyrfc3339 generator parser utils Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search`pyRFC3339-1.0/docs/source/generator.rst0000644000175000017500000000027312617663276017645 0ustar kurtkurt00000000000000:mod:`pyrfc3339.generator` -- Generate :RFC:`3339` timestamps ============================================================= .. automodule:: pyrfc3339.generator :members: pyRFC3339-1.0/README.rst0000644000175000017500000000225012620011515014332 0ustar kurtkurt00000000000000Description =========== .. image:: https://travis-ci.org/kurtraschke/pyRFC3339.svg?branch=master :target: https://travis-ci.org/kurtraschke/pyRFC3339 pyRFC3339 parses and generates :RFC:`3339`-compliant timestamps using `Python `_ `datetime.datetime `_ objects. >>> from pyrfc3339 import generate, parse >>> from datetime import datetime >>> import pytz >>> generate(datetime.utcnow().replace(tzinfo=pytz.utc)) #doctest:+ELLIPSIS '...T...Z' >>> parse('2009-01-01T10:01:02Z') datetime.datetime(2009, 1, 1, 10, 1, 2, tzinfo=) >>> parse('2009-01-01T14:01:02-04:00') datetime.datetime(2009, 1, 1, 14, 1, 2, tzinfo=) Installation ============ To install the latest version from `PyPI `_: ``$ pip install pyRFC3339`` To install the latest development version: ``$ pip install https://github.com/kurtraschke/pyRFC3339/tarball/master#egg=pyRFC3339-dev`` To build the documentation with Sphinx: #. ``$ pip install Sphinx`` #. ``$ python setup.py build_sphinx`` The documentation is also available online at: ``https://pythonhosted.org/pyRFC3339/`` pyRFC3339-1.0/setup.cfg0000644000175000017500000000074612620015127014477 0ustar kurtkurt00000000000000[nosetests] verbosity = 2 detailed-errors = 1 with-coverage = 1 cover-package = pyrfc3339 cover-erase = 1 cover-html = 1 cover-html-dir = docs/coverage with-doctest = 1 attr = !slow [build_sphinx] source-dir = docs/source build-dir = docs/build all-files = 1 builder = html [upload_sphinx] upload-dir = docs/build/html [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 [bdist_wheel] universal = 1 [aliases] release = egg_info -RDb '' make_dist = release sdist bdist_wheel pyRFC3339-1.0/pyrfc3339/0000755000175000017500000000000012620015127014314 5ustar kurtkurt00000000000000pyRFC3339-1.0/pyrfc3339/tests/0000755000175000017500000000000012620015127015456 5ustar kurtkurt00000000000000pyRFC3339-1.0/pyrfc3339/tests/tests.py0000644000175000017500000001102212617663276017212 0ustar kurtkurt00000000000000''' Test suite for pyRFC3339. ''' from datetime import datetime from pyrfc3339 import generate, parse from pyrfc3339.utils import timezone import pytz from nose.tools import eq_, raises class TestCore(): ''' This test suite contains tests to address cases not tested in the doctests, as well as additional tests for end-to-end verification. ''' def test_timezone_rounding(self): ''' Test rounding of timezone values to the nearest second. ''' eq_(timezone(5429), '+01:30') eq_(timezone(5431), '+01:31') def test_zero_offset(self): ''' Both +00:00 and -00:00 are equivalent to the offset 'Z' (UTC). ''' timestamp = '2009-01-01T10:02:03+00:00' dt = parse(timestamp) eq_(dt.tzinfo, pytz.utc) timestamp = '2009-01-01T10:02:03-00:00' dt = parse(timestamp) eq_(dt.tzinfo, pytz.utc) def test_parse_microseconds(self): ''' Test parsing timestamps with microseconds. ''' timestamp = '2009-01-01T10:02:03.25Z' dt = parse(timestamp) eq_(dt.microsecond, 250000) def test_generate_microseconds(self): ''' Test generating timestamps with microseconds. ''' dt = datetime(2009, 1, 1, 10, 2, 3, 500000, pytz.utc) timestamp = generate(dt, microseconds=True) eq_(timestamp, '2009-01-01T10:02:03.500000Z') def test_mixed_case(self): ''' Timestamps may use either 'T' or 't' and either 'Z' or 'z' according to :RFC:`3339`. ''' dt1 = parse('2009-01-01t10:01:02z') dt2 = datetime(2009, 1, 1, 10, 1, 2, tzinfo=pytz.utc) eq_(dt1, dt2) def test_parse_naive_utc(self): ''' Test parsing a UTC timestamp to a naive datetime. ''' dt1 = parse('2009-01-01T10:01:02Z', produce_naive=True) eq_(dt1.tzinfo, None) @raises(ValueError) def test_parse_naive_local(self): ''' Test that parsing a local timestamp to a naive datetime fails. ''' parse('2009-01-01T10:01:02-04:00', produce_naive=True) def test_generate_utc_parse_utc(self): ''' Generate a UTC timestamp and parse it into a UTC datetime. ''' dt1 = datetime.utcnow() dt1 = dt1.replace(tzinfo=pytz.utc) dt2 = parse(generate(dt1, microseconds=True)) eq_(dt1, dt2) def test_generate_local_parse_local(self): ''' Generate a local timestamp and parse it into a local datetime. ''' eastern = pytz.timezone('US/Eastern') dt1 = eastern.localize(datetime.utcnow()) dt2 = parse(generate(dt1, utc=False, microseconds=True), utc=False) eq_(dt1, dt2) def test_generate_local_parse_utc(self): ''' Generate a local timestamp and parse it into a UTC datetime. ''' eastern = pytz.timezone('US/Eastern') dt1 = eastern.localize(datetime.utcnow()) dt2 = parse(generate(dt1, utc=False, microseconds=True)) eq_(dt1, dt2) class TestExhaustiveRoundtrip(): ''' This test suite exhaustively tests parsing and generation by generating a local RFC 3339 timestamp for every timezone supported by pytz, and parsing that timestamp into a local datetime and a UTC datetime. ''' slow = True def test_local_roundtrip(self): for tz_name in pytz.all_timezones: yield self.local_roundtrip, tz_name def local_roundtrip(self, tz_name): ''' Generates a local datetime using the given timezone, produces a local timestamp from the datetime, parses the timestamp to a local datetime, and verifies that the two datetimes are equal. ''' tzinfo = pytz.timezone(tz_name) dt1 = tzinfo.localize(datetime.utcnow()) timestamp = generate(dt1, utc=False, microseconds=True) dt2 = parse(timestamp, utc=False) eq_(dt1, dt2) def test_utc_roundtrip(self): for tz_name in pytz.all_timezones: yield self.utc_roundtrip, tz_name def utc_roundtrip(self, tz_name): ''' Generates a local datetime using the given timezone, produces a local timestamp from the datetime, parses the timestamp to a UTC datetime, and verifies that the two datetimes are equal. ''' tzinfo = pytz.timezone(tz_name) dt1 = tzinfo.localize(datetime.utcnow()) timestamp = generate(dt1, utc=False, microseconds=True) dt2 = parse(timestamp) eq_(dt1, dt2) pyRFC3339-1.0/pyrfc3339/tests/__init__.py0000644000175000017500000000000012617663276017601 0ustar kurtkurt00000000000000pyRFC3339-1.0/pyrfc3339/generator.py0000644000175000017500000000417212620013734016662 0ustar kurtkurt00000000000000import pytz from pyrfc3339.utils import timezone, timedelta_seconds def generate(dt, utc=True, accept_naive=False, microseconds=False): ''' Generate an :RFC:`3339`-formatted timestamp from a :class:`datetime.datetime`. >>> from datetime import datetime >>> generate(datetime(2009,1,1,12,59,59,0,pytz.utc)) '2009-01-01T12:59:59Z' The timestamp will use UTC unless `utc=False` is specified, in which case it will use the timezone from the :class:`datetime.datetime`'s :attr:`tzinfo` parameter. >>> eastern = pytz.timezone('US/Eastern') >>> dt = eastern.localize(datetime(2009,1,1,12,59,59)) >>> generate(dt) '2009-01-01T17:59:59Z' >>> generate(dt, utc=False) '2009-01-01T12:59:59-05:00' Unless `accept_naive=True` is specified, the `datetime` must not be naive. >>> generate(datetime(2009,1,1,12,59,59,0)) Traceback (most recent call last): ... ValueError: naive datetime and accept_naive is False >>> generate(datetime(2009,1,1,12,59,59,0), accept_naive=True) '2009-01-01T12:59:59Z' If `accept_naive=True` is specified, the `datetime` is assumed to be UTC. Attempting to generate a local timestamp from a naive datetime will result in an error. >>> generate(datetime(2009,1,1,12,59,59,0), accept_naive=True, utc=False) Traceback (most recent call last): ... ValueError: cannot generate a local timestamp from a naive datetime ''' if dt.tzinfo is None: if accept_naive is True: if utc is True: dt = dt.replace(tzinfo=pytz.utc) else: raise ValueError("cannot generate a local timestamp from " + "a naive datetime") else: raise ValueError("naive datetime and accept_naive is False") if utc is True: dt = dt.astimezone(pytz.utc) timestamp = dt.strftime('%Y-%m-%dT%H:%M:%S') if microseconds is True: timestamp += dt.strftime('.%f') if dt.tzinfo is pytz.utc: timestamp += 'Z' else: timestamp += timezone(timedelta_seconds(dt.tzinfo.utcoffset(dt))) return timestamp pyRFC3339-1.0/pyrfc3339/parser.py0000644000175000017500000000630312620014076016166 0ustar kurtkurt00000000000000import re from datetime import datetime import pytz from pyrfc3339.utils import FixedOffset def parse(timestamp, utc=False, produce_naive=False): ''' Parse an :RFC:`3339`-formatted timestamp and return a `datetime.datetime`. If the timestamp is presented in UTC, then the `tzinfo` parameter of the returned `datetime` will be set to `pytz.utc`. >>> parse('2009-01-01T10:01:02Z') datetime.datetime(2009, 1, 1, 10, 1, 2, tzinfo=) Otherwise, a `tzinfo` instance is created with the appropriate offset, and the `tzinfo` parameter of the returned `datetime` is set to that value. >>> parse('2009-01-01T14:01:02-04:00') datetime.datetime(2009, 1, 1, 14, 1, 2, tzinfo=) However, if `parse()` is called with `utc=True`, then the returned `datetime` will be normalized to UTC (and its tzinfo parameter set to `pytz.utc`), regardless of the input timezone. >>> parse('2009-01-01T06:01:02-04:00', utc=True) datetime.datetime(2009, 1, 1, 10, 1, 2, tzinfo=) The input is strictly required to conform to :RFC:`3339`, and appropriate exceptions are thrown for invalid input. >>> parse('2009-01-01T06:01:02') Traceback (most recent call last): ... ValueError: timestamp does not conform to RFC 3339 >>> parse('2009-01-01T25:01:02Z') Traceback (most recent call last): ... ValueError: hour must be in 0..23 ''' parse_re = re.compile(r'''^(?:(?:(?P[0-9]{4})\-(?P[0-9]{2})\-(?P[0-9]{2}))T(?:(?:(?P[0-9]{2})\:(?P[0-9]{2})\:(?P[0-9]{2})(?P(?:\.[0-9]{1,}))?)(?P(?:Z|(?P(?P(?:\+|\-)[0-9]{2})\:(?P[0-9]{2}))))))$''', re.I | re.X) match = parse_re.match(timestamp) if match is not None: if match.group('time_offset') in ["Z", "z", "+00:00", "-00:00"]: if produce_naive is True: tzinfo = None else: tzinfo = pytz.utc else: if produce_naive is True: raise ValueError("cannot produce a naive datetime from " + "a local timestamp") else: tzinfo = FixedOffset(int(match.group('time_houroffset')), int(match.group('time_minuteoffset'))) secfrac = match.group('time_secfrac') if secfrac is None: microsecond = 0 else: microsecond = int(round(float(secfrac) * 1000000)) dt_out = datetime(year=int(match.group('date_fullyear')), month=int(match.group('date_month')), day=int(match.group('date_mday')), hour=int(match.group('time_hour')), minute=int(match.group('time_minute')), second=int(match.group('time_second')), microsecond=microsecond, tzinfo=tzinfo) if utc: dt_out = dt_out.astimezone(pytz.utc) return dt_out else: raise ValueError("timestamp does not conform to RFC 3339") pyRFC3339-1.0/pyrfc3339/__init__.py0000644000175000017500000000112712620013761016430 0ustar kurtkurt00000000000000""" pyRFC3339 parses and generates :RFC:`3339`-compliant timestamps using Python :class:`datetime.datetime` objects. >>> from pyrfc3339 import generate, parse >>> from datetime import datetime >>> import pytz >>> generate(datetime.utcnow().replace(tzinfo=pytz.utc)) #doctest:+ELLIPSIS '...T...Z' >>> parse('2009-01-01T10:01:02Z') datetime.datetime(2009, 1, 1, 10, 1, 2, tzinfo=) >>> parse('2009-01-01T14:01:02-04:00') datetime.datetime(2009, 1, 1, 14, 1, 2, tzinfo=) """ from pyrfc3339.generator import generate from pyrfc3339.parser import parse __all__ = ['generate', 'parse'] pyRFC3339-1.0/pyrfc3339/utils.py0000644000175000017500000000610212620013647016032 0ustar kurtkurt00000000000000from __future__ import division from datetime import timedelta, tzinfo class FixedOffset(tzinfo): ''' Represent a timezone with a fixed offset from UTC and no adjustment for DST. >>> FixedOffset(4,0) >>> FixedOffset(-4,0) >>> FixedOffset(4,30) >>> tz = FixedOffset(-5,0) >>> tz.dst(None) datetime.timedelta(0) The class tries to do the right thing with the sign of the time zone offset: >>> FixedOffset(-9,30) >>> FixedOffset(-9,-30) Traceback (most recent call last): ... ValueError: minutes must not be negative Offsets must thus be normalized so that the minute value is positive: >>> FixedOffset(-8,30) ''' def __init__(self, hours, minutes): ''' Create a new FixedOffset instance with the given offset. ''' tzinfo.__init__(self) if minutes < 0: raise ValueError("minutes must not be negative") if hours < 0: minutes *= -1 self.__offset = timedelta(hours=hours, minutes=minutes) self.__name = "UTC" + timezone(timedelta_seconds(self.__offset)) def dst(self, dt): ''' Return offset for DST. Always returns timedelta(0). ''' return timedelta(0) def utcoffset(self, dt): ''' Return offset from UTC. ''' return self.__offset def tzname(self, dt): ''' Return name of timezone. ''' return self.__name def __repr__(self): return "<{0}>".format(self.tzname(None)) def timedelta_seconds(td): ''' Return the offset stored by a :class:`datetime.timedelta` object as an integer number of seconds. Microseconds, if present, are rounded to the nearest second. Delegates to :meth:`timedelta.total_seconds() ` if available. >>> timedelta_seconds(timedelta(hours=1)) 3600 >>> timedelta_seconds(timedelta(hours=-1)) -3600 >>> timedelta_seconds(timedelta(hours=1, minutes=30)) 5400 >>> timedelta_seconds(timedelta(hours=1, minutes=30, ... microseconds=300000)) 5400 >>> timedelta_seconds(timedelta(hours=1, minutes=30, ... microseconds=900000)) 5401 ''' try: return int(round(td.total_seconds())) except AttributeError: days = td.days seconds = td.seconds microseconds = td.microseconds return int(round((days * 86400) + seconds + (microseconds / 1000000))) def timezone(utcoffset): ''' Return a string representing the timezone offset. Remaining seconds are rounded to the nearest minute. >>> timezone(3600) '+01:00' >>> timezone(5400) '+01:30' >>> timezone(-28800) '-08:00' ''' hours, seconds = divmod(abs(utcoffset), 3600) minutes = round(float(seconds) / 60) if utcoffset >= 0: sign = '+' else: sign = '-' return '{0}{1:02d}:{2:02d}'.format(sign, int(hours), int(minutes)) pyRFC3339-1.0/setup.py0000644000175000017500000000150212620014714014360 0ustar kurtkurt00000000000000from setuptools import setup with open("README.rst", "r") as readme: long_description = readme.read() setup( name = "pyRFC3339", version = "1.0", author = "Kurt Raschke", author_email = "kurt@kurtraschke.com", url = "https://github.com/kurtraschke/pyRFC3339", description = "Generate and parse RFC 3339 timestamps", long_description = open("README.rst").read(), keywords = "rfc 3339 timestamp", license = "MIT", classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Topic :: Internet" ], packages = ['pyrfc3339'], install_requires = ['pytz'], test_suite = 'nose.collector', tests_require = ['nose'] ) pyRFC3339-1.0/MANIFEST.in0000644000175000017500000000002412620010117014373 0ustar kurtkurt00000000000000include LICENSE.txt